《你必须知道的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[];
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章:指针的更多相关文章
- 你必须知道的495个c语言问题(笔记)
1.1我该如何决定使用哪种整数类型? 用到较大的数用long:空间很重要(例如有很大的数组或很多的结构)用short:此外用int. win32: int 32bit 4byte char 8b ...
- 你必须知道的495个C语言问题,学习体会一
C语言作为一门古老的语言,其灵活性和容易出错都让人 又爱又恨,书籍<你必须知道的495个C语言问题>,使用问答的形式,告诉读者 C语言使用的各个方面的知识,包括一些冷知识等.以下,我要摘录 ...
- C语言学习书籍推荐《你必须知道的495个C语言问题》
萨米特 (Steve summit) (作者), 孙云 (译者), 朱群英 (译者) 下载地址:点我 <你必须知道的495个C语言问题>以问答的形式组织内容,讨论了学习或使用C语言的过程中 ...
- 《你必须知道的495个C语言问题》知识笔记及补充
1. extern在函数声明中是什么意思? 它能够用作一种格式上的提示表明函数的定义可能在还有一个源文件里.但在 extern int f(); 和 int f(); 之间并没有实质的差别. 补充:e ...
- 你必须知道的495个C语言问题,学习体会四
本文,我们来学习下指针,这是个梦魇啊.无数次折磨着C语言学习者,无数次的内存泄露,无数次的访问失败,无数次的越界溢出, 这些错误造就的仅仅是一个 跟随者,真正的优秀者必须要正视语言的局限,同时在最大限 ...
- 你必须知道的495个C语言问题,学习体会三
本文是 本系列的第三篇,本文主要对C语言的表达式做个小结 先从两个坑爹的表达式说起:i++ 与++i 上大学的时候,学长告诉我,这两个表达式,意义是一样的,后来老师纠正说,还是有区别的,于是让我们记住 ...
- 你必须知道的495个C语言问题,学习体会二
这是本主题的第二篇文章,主要就结构体,枚举.联合体做一些解释 1.结构体 现代C语言编程 结构化的基石,diy时代的最好代言人,是面向对象编程中类的老祖宗. 我们很容易定义一个结构体,比如学生: st ...
- 《你必须知道的495个C语言问题》读书笔记之第11-14章:ANSI C标准、库函数、浮点数
一.ANSI C标准 1. ANSI向C语言预处理器引入了几项新的功能,包括“字符串化”操作符(#).“符号粘贴”操作符(##).#pragma指令. 2. Q:char a[3] = "a ...
- 《你必须知道的495个C语言问题》读书笔记之第8-10章:字符串、布尔类型和预处理器
一.字符和字符串 1. Q:为什么strcat(string, '!')不行? A:strcat()用于拼接字符串,所以应该写成strcat(string, "!")." ...
随机推荐
- [JSOI2018]潜入行动 (树形背包)
题目链接 题意: 外星人的母舰可以看成是一棵 n 个节点. n−1 条边的无向树,树上的节点用 1,2,⋯,n 编号.JYY 的特工已经装备了隐形模块,可以在外星人母舰中不受限制地活动,可以神不知鬼不 ...
- BZOJ 2169 连边 DP
思路:DP 提交:\(1\)次(课上刚讲过) 题解: 如果不管重边的话,我们设\(f[i][j]\)表示连了\(i\)条边,\(j\)个点的度数是奇数的方案数,那么显然我们可以分三种状态转移: \(f ...
- 003_linuxC++之_namespace使用
(一)引入namespace原因: 假如有很多跟人共同完成一项工程,工程中难免会有函数定义一样的名称,不可能一个一个的询问这个函数 你定义过了没有,所以引入namespace #include < ...
- Oracle 11.2 静默安装脚本
Oracle 11.2 静默安装脚本 cat db_init.sh.20190401 #!/bin/bash####安装Oracle所需依赖包function install_yum(){ yum ...
- learning express step(二)
install express-generator C:\Users\admin\WebstormProjects\learning-express-step2>npm install expr ...
- UDP c/s 模型
server.c /* udp server.c */ #include <string.h> #include <netinet/in.h> #include <std ...
- Java集合总结(三):堆与优先级队列
堆 满二叉树:满二叉树是指,除了最后一层外,每个节点都有两个孩子,而最后一层都是叶子节点,都没有孩子. 完全二叉树:完全二叉树不要求最后一层是满的,但如果不满,则要求所有节点必须集中在最左边,从左到右 ...
- DRF-解析器组件源码解析
解析器组件源码解析 解析器组件源码解析 1 执行request.data 开始找重装的request中的data方法 2 在dispatch找到重装的request def dispatch(self ...
- 转义字符\'和\"的使用示例
/* 转义字符\'和\"的使用示例 */ #include <stdio.h> int main(void) { printf("关于字符串常量和字符常量.\n&quo ...
- 2019腾讯前端技术大会资源TWeb
扫码关注公众号 回复“TWeb”即可获取“2019腾讯前端技术大会”的PPT