【C】从常量指针与指针常量谈到C语言的声明解读

请问char * const *(*next)();中的next是个什么东东?

1. 声明与定义

C 语言中的对象必须有且只有一个定义,但它可以有多个 extern 声明。(这里所说的对象跟 C++中的对象并无关系,这里的对象只是跟链接器有关的“东西”,比如函数和变量)

定义是一种特殊的声明,它创建了一个对象;
声明简单地说明了在其他地方创建的对象的名字,它允许你使用这个名字。

术语出现次数作用
定义只能出现在一个地方确定对象的类型并分配内存,用于创建新的对象。例如:int my_array[100];
声明可以多次出现描述对象的类型,用于指代其他地方定义的对象(例如在其他文件里)。例如:extern int my_array[];

声明相当于普通的声明,它所说明的并非自身,而是描述其他地方创建的对象。extern 对象声明告诉编译器对象的类型和名字,对象的内存分配则在别处进行。(由于并未在声明中为数组分配内存,所以并不需要提供关于数组长度的信息。)

定义相当于特殊的声明,它为对象分配内存

2. 指针常量与常量指针

2.1. 关键字const

c语言里面有个关键字const,它是定义只读变量(常变量),您若编写下列函数片段:

const int a = 10;
a = 11;

编译器将提醒你有错误:“表达式必须是可修改的左值(<-更多关于左值)”,这就表示你不能去修改一个本该是存放常量的地址里面的值,也就是其无法放在赋值符号=的左边

2.2. const&指针

上面举例说明了const如何修饰int类型,下面的写法都是一样的

const int a = 10;
int const a = 10;

然而,const与指针一起使用的时候,情况将有所不同,如下代码片段

int z = 10;

int a = 10;
const int * pa = &a;
	
int b = 10;
int const * pb = &b;

int c = 10;
int* const pc = &c;

*pa = 1;//error
*pb = 1;//error
*pc = 1;//right

pa = &z;//right
pb = &z;//right
pc = &z;//error

上面代码片段中列出了三种声明:

  1. const int *p;

  2. int const *p;

  3. int *const p;
    其中前两种,指针p指向的地址里面的值是不能改动的,也就是说const修饰的是*p(p为常量指针),最后一种指针p的值是不能修改的,也就是说const修饰的是指针p(p为指针常量)
    当然你也可以让指针为常量,指针指向的也为常量,也就是常量指针常量:

  4. const int * const p;

  5. int const * const p;

或许我们已经看出const的修饰规则了,那么继续往下阅读

3. 奇怪的C语言的设计哲学

对象的声明形式与它的使用形式尽可能相似
这是C语言从它的前辈那里接棒后,支持二进制外的其他数据类型(例如int)时产生的设计哲学

比如定义一个指向int类型变量a的指针时使用表达式p(int *p = &a),使用的时候也是用p这样的表达来引用或使用指针(*p = 1)指向的int数据。这样使定义(声明)与使用一致,但是会让我们这种从左往右阅读的人类,很难推断诸如int *p[3]到底是一个 int 类型的指针数组,还是一个指向 int数组的指针(其实这是一个int 类型的指针数组)。

“声明的形式和使用的形式相似”即使在当时也不像是一个特别好的主意。把两种截然不同的东西做成同一个样子真的有什么重要意义吗?贝尔实验室的学究们也承认此批评有理,但他们坚决死扛原来的决定,至今依然。一个比较好的声明指针的方法是:
int &p; 它至少能提示 p 是一个整型数的地址。这种语法现已被 C++采纳,用于表示参数的传址调用。

3.1. 声明的优先级规则

理解 C 语言声明的优先级规则

  1. 声明从它的名字开始读取,然后按照优先级顺序依次读取。
  2. 优先级从高到低依次如下。
    2.1. 声明中被括号括起来的那部分。
    2.2. 后缀操作符:括号()表示这是一个函数、方括号[]表示这是一个数组
    2.3. 前缀操作符:星号*表示“指向……的指针”。
  3. 如果 const 和(或)volatile 关键字紧跟类型说明符(如 int、long 等),那么它作用于类型说明符。在其他情况下,const 和(或)volatile 关键字作用于它左边紧邻的指针星号。

比如用上面的规则去解读文章开头的char * const *(*next)();

适用规则解 释
1首先,看变量名 next,并注意到它直接被括号所括住
2.1所以先把括号里的东西作为一个整体,得出“next 是一个指向……的指针”
2然后考虑括号外面的内容,在星号前缀和括号后缀之间作出选择
2.2规则告诉我们优先级较高的是右边的函数括号,所以得出“next 是一个函数指针,指向一个返回……的函数”
2.3再次,处理前缀“*”,得出指针所指的内容
3最后,把 char * const 解释为指向字符的常量指针

所以一通解释完,可以知道这个声明表示“next 是一个指针,它指向一个函数,该函数返回另一个指针,该指针指向一个类型为 char 的常量指针”

3.2. 图表分析声明:

当然这儿也有直观的图表解码声明(解密式编程)
C 语言声明的神奇解码环

一开始,我们从左边开始向右寻找,直到找到第一个标识符。当声明中的某个符号与图中所示匹配时,便把它从声明中处理掉,以后不再考虑。在具体的每一步骤上,我们首先查看右边的符号,然后再看左边。当所有的符号都被处理完毕后,便宣告大功告成

比如还是分析上面的char * const *(*next)();

剩余的声明(从最左边的标识符开始)所采取的下一步骤结 果
char * const *(*next) ( );第 1 步表示“next 是……”
char * const *(* ) ( );第 2、3 步不匹配,转到下一步,表示“next 是……”
char * const *(* ) ( );第 4 步不匹配,转到下一步
char * const *(* ) ( );第 5 步与星号匹配,表示“指向……的指针”,转第 4 步
char * const *( ) ( );第 4 步“(”和“)”匹配,转到第 2 步
char * const * ( );第 2 步不匹配,转到下一步
char * const * ( );第 3 步表示“返回……的函数”
char * const * ;第 4 步不匹配,转到下一步
char * const * ;第 5 步表示“指向……的指针”
char * const ;第 5 步表示“只读的……”
char * ;第 5 步表示“指向……的指针”
char ;第 6 步表示“char”

拼在一起,读作:
“next 是一个指向函数的指针,该函数返回另一个指针,该指针指向一个只读的指向 char的指针”

看懂了的话可以试试char *(* c[10])(int **p);

4. 参考与引用:

  1. 《C专家编程》第三、四章
    书籍pdf可以来我的博客寻找
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值