《你必须知道的495个C语言问题》读书笔记之第4-7章:指针

1. Q:为什么我不能对void *指针进行算术运算?

    A:因为编译器不知道所值对象的大小,而指针的算法运算总是基于所指对象的大小的。

 

2. Q:C语言可以“按引用传参”吗?

    A:不可以。严格来说,C语言总是按值传参,你可以模拟按引用传参,定义接受指针的函数,然后在调用时使用&操作符。但C没有任何真正等同于按引用传参的东西。

 

3. Q:怎样在整型和指针之间进行转换?能否暂时把整数放入指针变量,或者相反?

    A:C标准中规定整数与指针之间的相互转换是实现定义的,因此不保证指针和整数之间无需修改就能相互转换。事实上强制进行整数和指针的相互转换从来就不是什么好的实践。当需要同时保存两种类型数据的存储结构时,使用联合是更好的办法。

 

4. Q:空指针到底是什么?

    A:根据C语言定义,每一种指针类型都有一个特殊值——“空指针”,它与同类型的其他所有指针值都不相同,它“保证与任何对象或函数的指针值都不相等”。也就是说,空指针不会指向任何地方,它不是任何对象或函数的地址。取地址操作符&永远也不会返回空指针,对malloc的成功调用也不会返回空指针。注意,空指针在概念上不同于未初始化的指针,空指针可以确保不指向任何对象或函数,而未初始化的指针则可能指向任何地方。

 

5. Q:怎样在程序里获得一个空指针?

    A:使用空指针常量。空指针常量可以是0或NULL(在stdio.h文件中)。根据C语言定义,在指针上下文中的“值为0的整型常量表达式”会在编译时转换为空指针。然而,传入函数的参数不一定被当作指针上下文,此时生成空指针需要显式的类型转换。下表总结了何时可以直接使用空指针常量,何时需要进行显式类型转换:

 

6. Q:如果NULL的值改变了,比如在使用非零内部空指针的机器上,用NULL(而不是0)不是更好吗?

    A:不,尽管符号常量经常代替数字使用以备数字的改变,但这不是用NULL代替0的原因。C语言本身确保了源码中用于指针上下文的0会生成空指针。

 

7. Q:有没有什么简单的方法理解所有这些与空指针有关的东西?

    A:遵循两条简单规则:(a) 当源码中需要空指针常量时,使用"0"或"NULL";(b) 如果在函数调用中"0"或"NULL"用作参数,把它转换成被调函数需要的指针类型。

 

8. Q:空指针是指向地址0的指针吗?

    A:不能将空指针看作指向地址0的指针,空指针不指向任何地方。如果需要访问地址0,应该阅读厂商文档,使用机器提供的技巧来访问。

 

9. Q:在C语言中“指针和数组等价”到底是什么意思?

    A:意思是数组和指针的算法定义使得可以用指针方便地访问数组或者模拟数组,换言之,在C语言中只是指针算术和数组下标运算等价,指针和数组是不同的。特别地,等价的基础来自这个关键定义:一个T数组类型的对象如果出现在表达式中会退化为一个指向数组第一个元素的指针,指针的类型是指向T的指针。

 

10. Q:如果你不能给它赋值,那么数组如何能成为左值呢?

     A:术语“左值”并不完全表示“能赋值的东西”,更好的定义应该是“在内存中有特定位置的东西”。

 

11. Q:既然数组引用会退化为指针,如果array为数组,那么array和&array又有什么区别?

     A:区别在于类型。在标准C中,&array生成一个“T型数组”的指针,指向整个数组;而array生成一个T型的指针,指向数组的首元素。

int a[10];
int *pInt = a; // a的类型是"int型的指针"
int (*pArr)[10] = &a; // &a的类型是"10个int的数组的指针"
int array[NROWS][NCOLUMNS];
int (*pArr2)[NCOLUMNS] = array; // array的类型是"NCOLUMNS个int的数组的指针"
int (*pArr3)[NROWS][NCOLUMNS] = &array; // array的类型是"NROWS个NCOLUMNS个int的数组的数组的指针"

 

12. Q:malloc(0)返回一个空指针还是指向0字节的指针?

     A:ANSI/ISO标准声称它可能返回任意一种,其行为由实现定义。可移植的代码要么别调用malloc(0),要么做好它可能返回空指针的准备。

 

13. Q:当我调用malloc()为一个函数的局部指针分配内存时,我还需要用free()显式地释放吗?

     A:是的。函数返回时,局部指针会被释放,而不是它所指的对象。用malloc()分配的内存在你显式释放它之前都会保留在那里。一般地,每一个malloc()都必须有个对应的free()调用。

 

14. Q:我有个程序分配了大量的内存,然后又释放了,但我发现操作系统的内存占用率并没有降低?

     A:多数malloc/free的实现并不把释放的内存返回操作系统,而是留着供同一程序的后续malloc()使用。

 

15. Q:我的程序总是崩溃,显然发生在malloc内部的某个地方,但是我看不出哪里有问题,是malloc有bug吗?

     A:很不幸,malloc的内部数据结构很容易被破坏,而由此引起的问题会十分棘手。最常见的问题来源是向malloc分配的区域写入比所分配的还多的数据,如malloc(strlen(s))而不是strlen(s)+1。其他问题包括指向已经释放了的内存的指针,分配大小为0 的对象,重分配空指针,释放未从malloc获得的指针、空指针或者已经释放的指针。这些错误的后果可能会在真正出错很久以后才显现出来或在不相关的代码段出现,从而导致排错十分困难。多数malloc的实现在这些问题面前显得十分脆弱,因为它们直接在它们返回的内存旁边存储至关重要的内部信息片段,这些信息很容易被用户指针破坏。

 

16. Q:free()怎么知道有多少字节需要释放?

     A:malloc/free的实现会在分配的时候记下每一块的大小,所以在释放时就不必再考虑它的大小了。(通常,这个大小就记录在分配的内存块旁边,因此对超出分配内存块边界的内存哪怕是轻微的改写,也会导致严重的后果)。

 

17. Q:为什么sizeof不能告诉我它所指的内存块的大小?

     A:sizeof操作符并不知道你使用了malloc为指针分配内存,sizeof只能得到指针本身的大小。

 

18. Q:动态分配数组之后,还能改变它的大小吗?

     A:能,使用realloc。可以使用下边的代码。

dynarray = (int *)realloc((void *)dynarray, 20 * sizeof(int));

     如果realloc能在原地扩大内存区域,它就返回传入的指针;如果它必须到内存中的其他地方去寻找足够大的连续空间,则它会返回一个不同的指针,而原来的指针值变得不可用;如果它根本找不到足够的空间,则它会返回空指针,而原来分配的内存会保留。因此,通常不应立即将新指针赋给旧指针,最好使用一个临时指针。

int *newarray = (int *)realloc((void *)dynarray, 20 * sizeof(int));
if (newarray != NULL) {
    dynarray = newarray;
}
else {
    fprintf(stderr, "Can't reallocate memory.\n");
    /* dynarray remains allocated. */
}

 

posted on 2016-03-12 12:02  whl1729  阅读(280)  评论(0编辑  收藏  举报