可以先阅读《巧解函数指针的声明》热热身,理论上,《C专家编程》中的方法可以分析所有C复杂声明,非常科学。
历史原因
- 19世纪60年代晚期,人们在设计C语言这部分内容的时候,“类型模型”这个概念对于当时的编程语言理论而言尚属于陌生。
- BCPL语言(C的祖先)甚至没有类型,只有二进制字作为唯一的数据类型,所以C先天不足
- C语言的设计哲学,要求对象的声明形式和它的使用形式尽可能的相似
- 最大的问题是无法从左到右依次阅读一个声明
char (*j)[20];
j = (char(*)[20]) malloc(20);
char* const*(*next)();
声明器与声明
- 主要涉及三个符号,指针*,数组[],函数(),优先级后两者大于前者
- 函数的返回值不能是一个函数,foo()()非法
- 函数的返回值不能是一个数组,foo()[ ]非法
- 数组里面不能有函数,foo[ ]()非法
- 函数的返回值可以是函数指针,int (*func())();返回值是int(*)()
- 函数的返回值可以是指向数组的指针,int(*foo())[ ];返回值是int(*)[ ]
- 数组里面可以有函数指针,int(*foo[ ])();数组里是int(*)()
- 数组里可以有其他数组,int foo[ ][ ]
- 上面三条总结起来就是[]和()不能同时出现,但是可以和*一起出现
优先级规则
typedef
- typedef为一种类型引入一个新的名字,而不是为变量分配内存空间
- 不要在1个typedef后面放入太多声明,不要把typedef放在声明的中间部分,如下所示
typedef int \*ptr, (*fuc)(), arr[5];
unsigned const long typedef int volatile *kumquat;
- 一般情况下,typedef用于简洁的表示指向其他东西的指针,如下所示
//signal函数两个参数,1个int,1个函数指针void(*)(int)
//void(*)(int)是用户自定义的信号处理函数
//返回值也是1个函数指针void(*)(int)
void (*signal(int,void(*)(int)))(int);
typedef void (*HANDLER) (int);
HANDLER signal(int, HANDLER);
- typedef类似宏文本替换,因为它不引入新类型,但是typedef声明之后不能在修改内部的封装,在几次连续的声明中,typedef可以保证每个都正确,宏不能保证;
#define x int[10]
typedef int x[10];
#define peach int
unsigned peach i;//合法
typedef banana int;
unsigned banana j;//不合法
typedef struct foo{int foo;} foo;的含义
C语言存在很多名字空间:
- 标签名:
- 标签:用于所有的结构,枚举,联合
- 成员名:每个结构或联合都有自身的名字空间
- 其他
在同一个名字空间内部,任何名字都必须具有唯一性,但在不同的空间中,可以存在相同的名字。因为每个结构或联合都有自己的名字空间,它们可以有相同的成员名字。
- 不要为省写struct而用typedef
- 应该在数组,结构,指针以及函数的复合类型中使用
- 使用_tag
Reference
C专家编程