《C专家编程》读书笔记之第8~11章

八、为什么程序员无法分清万圣节和圣诞节

1. 整形提升是指char,short int和位段类型(无论signed或unsigned)以及枚举类型将被提升为int或unsigned int(如果int能够完整容纳原先的数据,则提升为int,否则为unsigned int).如果编译器能够保证运算结果一致,也可以省略类型提升。

2. C语言中参数传递也可能发生隐式类型转换。如果使用了适当的函数原型,类型提升便不会发生,否则也会发生。在被调用函数的内部,提升后的参数被裁减为原先声明的大小。

3. C语言中的隐式类型转换起源于简化最初的编译器的想法。把所有的操作数转换为同一的长度极大地简化了代码的生成。这样,压到堆栈中的参数都是同一长度的,所以运行时系统只需要知道参数的数目,不需要知道它们的长度。

九、再论数组

1. 数组和指针等同的情况:

(1) 表达式中的数组名被编译器当作一个指向该数组第一个元素的指针。

     类似加法,取下标操作符的操作数是可以交换的,所以在一个a[10]的声明中使用a[6]和6[a]都是正确的,因为编译器都将其解释为(a+6)。

(2) 数组下标总是与指针的偏移量相同。

(3) 在函数参数的声明中,数组名被编译器当作指向该数组第一个元素的指针。

     把作为参数的数组和指针等同起来是出于效率的考虑,毕竟传址调用比拷贝整个数组效率更高。注意此时在函数内部不能通过sizeof来获取数组长度,因为数组已转换为指针。

2. 数组名是不可修改的左值,而指针的值可以修改。

 void func(int arr1[])
 {
     int arr2[] = {0,1,2};
     int arr3[] = {0,1,2};
     arr1 = arr2;  // ok, arr1 has been converted to a pointer.
     arr3 = arr2;  // error, array's name is a left-value which can't be changed.
}

3. C语言中的数组就是一维数组,C语言的“多维数组”实际上是数组的数组。

4. 指针所指向的数组的维数不同,其自增操作的结果也不同。

 int parrot[2][3][5];
 int (*r)[5] = parrot[0];  // ++r将偏移4*5=20个字节
 int *t = parrot[0][0];  // ++t将偏移4个字节

5. 对数组进行初始化时,如果数组的长度比所提供的初始化的个数要多,剩余的几个元素会被自动设置为0(若元素为int型)、空格(若元素为char型)、0.0(若元素为double型)、NULL(若元素为指针型).若声明数组时不提供任何初始值,则数组中的元素的值是未确定的。(在vc6.0环境下未初始化的int或指针取值均为0xcccccccc)。注意未初始化的指针的值并非NULL!

十、再论指针

1. “数组名被改写为一个指针参数”的规则并不是递归定义的,数组的数组会被编译器改写为“数组的指针”,而不是“指针的指针”。

     实参                                      所匹配的形参
 char c[8][10]; /*数组的数组*/            char (*c)[10]; /*数组指针*/ 
 char *c[15];   /*指针数组*/              char **c;      /*指针的指针*/
 char (*c)[64]; /*数组指针*/              char (*c)[64]; /*不改变*/
 char **c;      /*指针的指针*/            char **c;      /*不改变*/

 2. 使用指针向函数传递一个多维数组。比如:把array[x][y]改写为一个一维数组array[x+1],其元素类型是指向array[y]的指针,并在数组最后的那个元素array[x]里存储一个NULL指针,以提示数组的结束。

3. C语言中不能用const int常量来定义数组的长度,C++则可以。

 const int SIZE = 100;
 char plum[SIZE];  // error in C, ok in C++

4. 库函数realloc()能够对一个现在的内存块大小进行重新分配,同时不会丢失原先内存块的内容。在实践中,不要把realloc()函数的返回值直接赋给字符指针,如果realloc()函数失败,它会使该指针的值变为NULL,导致无法对现有的表进行访问。

十一、你懂得C,所以C++不在话下

1. C语言允许用户定义新的类型(struct, enum)来支持抽象,但C语言不允许在用户定义类型中重新定义*,<<,[],+等预定义操作符,而C++则允许。

2. C++同时提供自动和受控制的初始化、数据在生命期结束后自动消除以及隐式类型转换。这些特性有些是C语言不支持的,有些在C语言里不是很方便。(?)

3. C语言没有完备的机制来实现封装。

    封装即把数据和相关的操作捆绑在一起,具体说是把用户定义的数据结构和用户定义的能够在这些数据结构上进行操作的函数捆绑在一起实现了数据的完整性,别的函数无法访问用户定义类型的内部数据或操作。C语言允许程序员把各种数据类型组合在一起形成自定义的记录(结构体),但无法对函数进行限制。如果一个结构是完全可见的,其任何部分都可能以任何方式被修改。人们无法把函数固定在数据类型上,使它们清晰地融为一体。

4. 类型转换

int i = 3;
float x = (float)i;  // C style
float y = float(i);  // C++ style

5. 在C语言中,一个语句块中所有的声明都必须放在所有语句的前面,在C++中声明可以出现在语句可以出现的任何地方。

6. 在C++中,一个内层作用域的结构名将会隐藏外层空间中相同的对象名,在C语言中则并非如此。

7. 在C++中字符常量的类型是char,在C语言中字符常量的类型是int。即在C++中,sizeof('a') = 1;在C语言中,sizeof('a') = 4.(假设sizeof(int) = 4)。

8. 由于C++中增加了新的注释符//,有时会在两种语言中产生微妙而怪异的差别。

9. 在C++中存在,但在C语言中却不存在的限制有:

    (1) 在C++中用户代码不能调用main()函数,在C语言中这是运行的(尽管这种情况很罕见)。

    (2) 完整的函数原型声明在C++中是必须的,在C语言中则没这么严格。

    (3) 在C++中由typedef定义的名字不能与已有的结构标签冲突,在C语言中这是允许的(它们分属不同的名字空间)。

    (4) 当void*指针赋值给另一个类型的指针时,C++规定必须进行强制类型转换,在C语言中却无必要。

原文地址:https://www.cnblogs.com/wuhualong/p/ReadingNote_Expert_C_Programming_Chap8-11.html