1、函数返回值不能是一个函数,所以箱foo()()这样是非法的。
2、函数的返回值不能是一个数组,所以想foo()[]这样是非法的。
3、数组里面不能有函数,所以像foo()[]这样是非法的。
但下面这样则是合法的:
1、函数的返回值允许是一个函数指针,如:int(*fun())();
2、函数的返回值允许是一个指向数组的指针,如:int(*foo())[];
3、数组里面允许有函数指针,如int (*foo[])();
4、数组里面允许有其他数组,所以你经常能看到 intfoo[][];
~结构体中也可以存在位段、无名字段以及字对齐所需的填充字段。这些都是通过在字段的声明后面加一个冒号以及一个表示字段位长的整数来实现的。如:
/* 处理ID信息 */
struct pid_tag{
unsigned int inactive : 1;
unsigned int : 1; /* 1个位的填充 */
unsigned int rescount : 6;
unsigned int : 0; /* 填充到下一个字边界 */
short pid_id;
struct pid_tag *link;
}
这种用法通常被称作"深入逻辑元件的编程",你可以在系统编程中看到它们。它也能用于把一个bool标志以位而不是字符来表示。位段的类型必须是int\unsigned int或signed int(或加上限定符)。至于int位段的值可不可以取负值则取决于编译器。
~我们应该更关心代码是否容易阅读,而不是是否容易书写。我们只编一次代码,但在以后的程序维护过程中将多次阅读这些代码。如果一行代码只做一件事,看上去会更简单一些。基于这个理由,变量的声明应该与类型的声明分开。
<联合>:
和结构体外表相似,关键性的区别在于,所有的成员都从偏移地址零开始存储。这样每个成员的位置都重叠在一起:某一时刻,只有一个成员真正存储于该地址。
联合一般一般是作为大型结构的一部分存在的。
联合一般被用来节省空间,因为有些数据项是不可能同时出现的,如果同时存储它们显然颇为浪费。
联合也可以把同一个数据解释成两种不同的东西,而不是把两个不同的数据解释为同一种东西。
<枚举enum>:
枚举具有一个优点:#define定义的名字一般在编译时被丢弃,而枚举名字则通常一直在调试器中可见,可以在调试代码时使用它们。
《优先级规则》:
A 声明从它的名字开始读取,然后按照优先级顺序依次读取。
B 优先级高到底依次是:
1、声明中被括号括起来的那部分
2、后缀 ()表示是一个函数,而方括号[]表示这是一个数组。
3、前缀操作符:星号*表示"指向...的指针"。
C 如果const和(或)volatile关键字的后面紧跟类型说明符(如 int,long等),那么它作用于类型说明符,在其他情况下,const和(或)volatile关键字作用于它左边紧邻的指针星号。
《如》:char * const *(*next)();
A 首先,看变量名"next",并注意到它直接被括号括住,
B.1 所以先把括号里的东西 作为一个整体,得出"next是一个指向...的指针";
B 然后考虑括号外面的东西,在星号前缀和括号后缀之间作出选择;
B.2 B.2规则告诉我们优先级较高的是右边的函数括号,所以得出"next是一个函数指针,指向一 个返回...的函数";
B.3 然后,处理前缀"*",得出指针所指的内容
C 最后,把"char * const"解释为指向字符常量指针。
分析结果:"next是一个指向函数的指针,该函数返回另一个指针,该指针指向一个类型char的常量指针"。
《typedef》:
typedef是一种有趣的声明形式:它为一种类型引入新的名字,而不是为变量分配空间。
typedef和普通的变量声明完全一样(看上去),它们读起来也是一样的。普通的声明表示"这个名字是一个指定类型的变量",而typedef关键字并不创建一个变量,而是宣称"这个名字是指定类型的同义词"。
缺点:它同样具有与其他声明一样的混乱语法,同样可以把几个声明器塞到一个声明中去。对于结构,除了可以在书写时节省struct关键字之外,typedef并不能提供显著的好处,而少写一个struct其实并没有多大帮助。在任何typedef声明中,甚至不必把typedef放在声明的开始位置。
小启发: 操作声明器的一些提示 不要在一个 typedef 中放入几个声明器,如下所示;
typedef int *ptr, (*fun)(), arr[5];
/* ptr是“是一个指向int 的指针” 类型,
* fun是"指向返回值为int的函数指针"类型,
* arr是"长度为5的int型数组"类型。
*/
千万不要把typedef嵌到声明的中间部分,如下所示:
unsigned const long typedef int volatile *kumquat;
<typedef int x[10]和#define x int[10]的区别>:
typedef是一种彻底的"封装"类型----在声明它之后不能再往里面增加别的东西。
它和macro的区别体现在两个方面:
首先,可以用其他类型说明符对宏类型名进行扩展,但对typedef所定义的类型名却不能这样做。
如:#define peach int
unsigned peach i; /* valid */
typedef int banana;
unsigned banana i; /* error! invalid */
其次,在连续的几个变量的声明中,用typedef定义的类型能够保重声明中的所有的变量均为同一类 型,而用#define定义的类型则无法保证。
如:
#define int_ptr int *
int_ptr chalk,cheese;
经过宏扩展,第二行变为:
int *chalk,cheese; /* 两变量类型不同 */
相反,下面的代码中:
typedef char * char_ptr;
char_ptr Bentley,Rolls_Royce; /* 同为指针型变量 */
< typedef struct foo{...foo;}的含义 >:
C语言存在多种名字空间:
1、标签名(label name).
2、标签(tag):这个名字空间用于所有的结构、枚举和联合。
3、成员名:每个结构或联合都有自身的名字空间。
4、其他。
在同一个名字空间里,任何名字必须具有唯一性,但在不同的名字空间里可以存在相同的名字。由于每个结构或联合具有自己的名字空间,所以同一个名字可以出现在许多不同的结构内。
typedef应该用在:
1、数组、结构、指针以及函数的组合类型。
2、可移植类型。比如当你需要一种至少20bit的类型时,可以对它进行typedef操作声明。这样, 当代码移植到不同的平台时,要选择正确的类型,只要在typedef中进行修改就可以了。
3、typedef也可以为后面的强制类型转换提供一个简单的名字。
###当你有两个不同的东西时,一个比较好的原则就是用不同的名字来称呼它们。这样做减少了混淆的危险(这始终是软件的一个重要准则),如果有可能搞不清哪个名字是结构标签,就为它取一个以"_tag"结尾的名字。这使得辨认一个特定的名字变得简单。###

评论