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[];
int *pInt = a; // a的类型是"int型的指针"
int (*pArr)[] = &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,  * sizeof(int));

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

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

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

  1. 你必须知道的495个c语言问题(笔记)

    1.1我该如何决定使用哪种整数类型? 用到较大的数用long:空间很重要(例如有很大的数组或很多的结构)用short:此外用int. win32: int 32bit    4byte char 8b ...

  2. 你必须知道的495个C语言问题,学习体会一

    C语言作为一门古老的语言,其灵活性和容易出错都让人 又爱又恨,书籍<你必须知道的495个C语言问题>,使用问答的形式,告诉读者 C语言使用的各个方面的知识,包括一些冷知识等.以下,我要摘录 ...

  3. C语言学习书籍推荐《你必须知道的495个C语言问题》

    萨米特 (Steve summit) (作者), 孙云 (译者), 朱群英 (译者) 下载地址:点我 <你必须知道的495个C语言问题>以问答的形式组织内容,讨论了学习或使用C语言的过程中 ...

  4. 《你必须知道的495个C语言问题》知识笔记及补充

    1. extern在函数声明中是什么意思? 它能够用作一种格式上的提示表明函数的定义可能在还有一个源文件里.但在 extern int f(); 和 int f(); 之间并没有实质的差别. 补充:e ...

  5. 你必须知道的495个C语言问题,学习体会四

    本文,我们来学习下指针,这是个梦魇啊.无数次折磨着C语言学习者,无数次的内存泄露,无数次的访问失败,无数次的越界溢出, 这些错误造就的仅仅是一个 跟随者,真正的优秀者必须要正视语言的局限,同时在最大限 ...

  6. 你必须知道的495个C语言问题,学习体会三

    本文是 本系列的第三篇,本文主要对C语言的表达式做个小结 先从两个坑爹的表达式说起:i++ 与++i 上大学的时候,学长告诉我,这两个表达式,意义是一样的,后来老师纠正说,还是有区别的,于是让我们记住 ...

  7. 你必须知道的495个C语言问题,学习体会二

    这是本主题的第二篇文章,主要就结构体,枚举.联合体做一些解释 1.结构体 现代C语言编程 结构化的基石,diy时代的最好代言人,是面向对象编程中类的老祖宗. 我们很容易定义一个结构体,比如学生: st ...

  8. 《你必须知道的495个C语言问题》读书笔记之第11-14章:ANSI C标准、库函数、浮点数

    一.ANSI C标准 1. ANSI向C语言预处理器引入了几项新的功能,包括“字符串化”操作符(#).“符号粘贴”操作符(##).#pragma指令. 2. Q:char a[3] = "a ...

  9. 《你必须知道的495个C语言问题》读书笔记之第8-10章:字符串、布尔类型和预处理器

    一.字符和字符串 1. Q:为什么strcat(string, '!')不行? A:strcat()用于拼接字符串,所以应该写成strcat(string, "!")." ...

随机推荐

  1. 解决InputStream中数据读取不完整问题

    转载:https://blog.csdn.net/lilidejing/article/details/37913627 当需要用到InputStream获取数据时,这时就需要读取InputStrea ...

  2. leetcode解题报告(12):Maximum Subarray

    描述 Find the contiguous subarray within an array (containing at least one number) which has the large ...

  3. postgresql 一些操作

    postgresql 对sql语句敏感的. 所以尽量标准化输入 #############查看版本信息 ############ 1.查看客户端版本 psql --version 1 2.查看服务器端 ...

  4. masm for windows2015 下载安装

    下载地址: https://sm.myapp.com/original/Office/wasm2015.rar

  5. 在AspNetCore3.0中使用Autofac

    1. 引入Nuget包 Autofac Autofac.Extensions.DependencyInjection 2. 修改Program.cs 将默认ServiceProviderFactory ...

  6. fatal: unable to access 'https://github.com/Homebrew/brew/'

    最近安装 Homebrew 遇到的坑,总结一下. 我的 Mac 版本是 10.13.6. 首先安装 Homebrew /usr/bin/ruby -e "$(curl -fsSL https ...

  7. Flutter子组件调用父组件方法修改父组件参数

    子组件调用父级组件方法的主要实现是父组件给子组件传入一个方法,然后在子组件中调用父级方法来修改父级的参数.看一下效果图 父级组件实现 在父级组件中写一个_editParentText的方法来修改组件中 ...

  8. redus - 队列

    redus 写如队列 <?php $redis = new \Redis(); $redis->connect('127.0.0.1',6379); $password = '123456 ...

  9. javascript-类型、值和变量

    基本类型和引用类型 MDN-JavaScript 数据类型和数据结构 ECMAScript 变量可能包含两种不同数据类型的值:基本类型值和引用类型值.基本类型值指的是 简单的数据段,而引用类型值指那些 ...

  10. SQL-W3School-高级:SQL CREATE INDEX 语句

    ylbtech-SQL-W3School-高级:SQL CREATE INDEX 语句 1.返回顶部 1. CREATE INDEX 语句用于在表中创建索引. 在不读取整个表的情况下,索引使数据库应用 ...