1. “声明的形式和使用的形式相似”这种用法可能是C的独创。至今为止一个比较好的声明指针的方法是: int &p; 它至少提示p 是一个整形数的地址。这种语法现已被C++采纳,用于表示参数的传址调用(引用)。
2. 如果想要把什么东西的类型强制转换为指向数组的指针,就不得不使用下面的语句来表示这个强制类型转换:
char (*j) [20];
j = (char (*) [20]) malloc(20);
如果把想好两边看上去的明显多余的括号拿掉,代码就会变成非法。
3. 变量名的声明应该与类型的声明分开。
4. 参数传递的问题:
(1) 有些C语言书籍声称“在调用函数时,参数按照从右到左的次序压到堆栈中。”这种说法过于简单了。 参数在传递时首先尽可能的放在寄存器中(追求速度)。注意,int型变量i跟只包含这个int型成员的结构变量s在参数传递时的方式可能完全不同。一个int型参数一般会被传递到寄存器中,而结构参数则有可能被传递到堆栈中。第二点要注意的是,结构中放置数组,如:struct s_tag { int a[100] ;};
现在,可以把数组当作第一等级的类型,用赋值语句拷贝整个数组,已传值调用的方式把它传递到函数,或者把它作为函数的返回类型。
(2) 在典型情况下,并不会频繁地对整个数组进行赋值操作。但是如果需要这样做,可以通过把它放入结构中来实现。
5. 联合(union):在内存中的存储,所有的成员都从偏移地址零开始存储。这样,每个成员的位置都重叠在一起:在某个时刻,只有一个成员真正存储于该地址。
6. union一般被用来节省空间的。因为这些数据项是不可能同时出现的,如果同时存储他们,显然颇为浪费。
7. 关于枚举:
枚举具有一个优点:#define 定义的名字一般在编译时被丢弃,而枚举的名字则通常一直在调试器中可见,可以在调试代码时使用它们。
8. 前缀操作符:*表示“指向... 的指针”。 如果const和volatile关键字的后面紧跟类型说明符(如int,long等),那么它作用于类型说明符。在其他情况下,const和volatile关键字作用于它左边紧邻的指针星号。
9. typedef为数据类型创建别名,而不是创建新的数据类型,可以对任何类型进行typedef声明。
(1) typedef和宏文本替换之间存在一个关键性的区别。把typedef堪称是一个彻底的“封装”类型——在声明它之后不能再往里面增加别的东西。
如:typedef int banana;
unsigned banana i ; // 错误,非法。
(2)在连续几个变量的声明中,用typedef定义的类型能够保证声明中所有的变量均为同一种类型,而用#define定义的类型则无法保证。如下:
#define int_ptr int *
int_ptr a , b ;
a 为指针,b为int类型变量。
而typedef则不会这样。
10. 操作typedef第提示:
不要为了方便起见对结构使用typedef。 这样做的唯一的好处是能使你不必书写“struct”关键字,但这个关键字可以向你提示一些信息。
typedef应该用在: (1)数组,结构,指针以及函数的组合类型。
(2)可移植类型。当你需要一种至少20比特的类型时,可以对它进行typedef操作typedef的提示声明。这样,当把代码移植到不同的平台时,要选择正确的类型如:short,int ,long时,只要在typedef中进行修改就可以了。无需每个声明都去加以修改。
11. 错误思想:“数组定义等同于指针的外部声明” 如: 文件一: int a[10]; 文件二:extern int *a;
12. 区分定义和声明:
声明相当于普通的声明:它所说明的并非自身,而是描述其他地方的创建的对象。
定义相当于特殊的声明:它为对象分配内存。
13. 编译器为每个变量分配一个地址(左值)。这个地址在编译时可知。而且该变量在运行时一直保存在这个地址。相反,存储于变量的值(它的右值)只有在运行的时候才可知。如果需要用到变量中存储的值,编译器就发出指令从指定的地址读入变量值并将它存于寄存器中。
14. 定义指针时,编译器并不为指针所指向的对象分配空间,它只分配指针本省的空间,除非在定义时,同时赋给指针一个字符串常量进行初始化。 注意只有对字符串常量才是如此。不能指望为浮点数之类的常量分配空间。
如:float *p = 3.14; //错误。
15. 链接器确认main函数为初始进入点,把符号引用绑定到内存地址,把所有的目标文件集中在一起,再加上库文件,从而产生可执行文件。