1、定义申明最重要的区别:定义创建了对象并为这个对象分配了内存,声明没有分配内存。
即一个需要建立存储空间,如 int i; 这是定义;而 extern int i; 是声明,并没有建立存储空间,只是告诉编译器该变量已经在别处定义过了。对于函数的定义和声明也同样如此,这就为什么内联 inline 函数只能在放在函数定义的前面,而不能放在声明之前。
2、在 union 中所有的数据成员共用一个空间,同一时间只能存储其中一个数据成员,所有的数据成员具有相同的起始地址。可以根据这一特性来测试电脑存储模式(大端OR小端),参见代码:
1 | int main() |
术语”大端”和“小端”表示多字节值得哪一端(大端OR小端)存储在该值的起始地址。
3、typedef 的真正意思是给一个已经存在的数据类型(不是变量)取一个别名,而非定义一个新的数据类型。
4、“贪心法”:C语言有这样一个规则:每一个符号应该包含尽可能多的字符。也就是说,编译器将程序分解成符号的方法是,从左到右一个一个字符地读入,如果该字符可能组成一个符号,那么再读入下一个字符,判断已经读入的两个字符组成的字符串是否可能是一个符号的组成部分,如果是,继续读入下一个字符,重复上述判断,直到读入的字符组成的字符串已不再可能组成一个有意义的符号。注意,除了字符串和字符常量,符号的中间不能嵌有空白。
5、关键字 sizeof 求值是在编译的时候,根据类型确定其值。即 sizeof 只能计算在该系统该编译器环境下,数据类型的大小。
6、对指针进行加 1 操作,得到的是下一个元素的地址,而不是原有地址值直接加 1。所以,一个类型为 T 的指针的移动,以 sizeof(T)为移动单位。
1 | int main() |
7、C语言中,当一维数组作为函数参数的时候,编译器总是把它解析成一个指向其首元素首地址的指针,一维数组作为形参传递时是不需要指定大小的,因为函数并不为数组参数分配内存空间,形参只是一个指针,指向其他地方已经分配好的内存空间,所以函数是无法知道数组的长度的,除非显式的把数组长度传递给函数。但是多维数组就需要指定了,因为多维数组的每个元素本身是另外一个数组,编译器需要知道它的维数,以便为函数形参的下标表达式进行求值。
在C语言中,所有非数组形式的数据实参均以传值形式调用,即对实参做一份拷贝并传递给被调用的函数,函数只能修改传递给它的那份拷贝。实际上指针也是传值形式调用,只不过函数内则通过解引用操作实际变量,就习惯把它认作是传址调用传递。编译器总是要为函数的每个参数制作临时副本(位于栈中)。
8、sizeof 与 strlen:sizeof 第5点讲了一部分,sizeof 操作符的结果类型是 size_t ,它在头文件中 typedef 为 unsigned int 类型
1 | int main() |
第一个 for 循环,i 与 sizeof(int) 比较时,先将 i 转换为 unsigned int,那就是一个很大的数(VC 4294967295)。
sizeof 是关键字,是算符, strlen 是函数。sizeof 可以用类型作参数,strlen 只能用 char * 做参数,且必须是以‘\0’结尾的。数组作 sizeof 的参数不退化,传递给strlen 就退化为指针。strlen 是在运行期确定,计算的是实际长度。
当适用于一个结构类型或变量,sizeof 返回实际的大小;当适用一静态的空间数组,sizeof 返回全部数组的尺寸,但是 sizeof 操作符不能返回动态的被分配了的数组或外部的数组的尺寸。
sizeof(express) ,其中的 express 在编译过程中是不会被编译的,而是被替代为类型。
1 | int main() |
对函数使用 sizeof,在编译阶段会被替换为函数的返回值的类型
1 | char fun() |
如果函数为 void fun() {} 那么 sizeof(fun()) 编译过程中会出现错误,因为替换之后的sizeof(void) 编译无法通过。
清楚一点,sizeof 是在编译期计算大小的。
9、声明一个指针并不会自动分配任何内存。在对指针执行间接访问前,指针必须执行初始化,使它指向现有的内存,或者给它分配动态内存。
1 | char c = 'a'; |
10、一个表达式能作为左值,表明该表达式标识了一个特定的内存位置。马克思告诉我们,世界是物质的。计算机科学中,任何数据的存储都需要一个物质性的地址空间。不然数据搁哪?