第十三章:高级指针话题
GitHub
链接:ch13. 高级指针话题
指针是 C
语言的最精华所在了,指针与内存连接,使用得当堪比汇编!!!
本章内容值得反复研读,查阅资料对比阅读!
本章总结及注意点
部分课后习题解答
13.9 问题
-
很强的问题。当时做的时候错了
d,e,g,i,j,l
脑子怕是不太好使… -
与所有指针算法一样,值 1 被缩放到指针所指向的对象的大小,在本例中是指向字符的指针。结果是
ptr
指向数组的下一个元素,即ptr+1=&array[1]
。其实现在ptr
和数组名array
是等价的。就类比一维数组,ptr+1
就是array[1]
。见demo01.c
。 -
三级指针而已。见参考答案:
-
声明个局部变量存
trans->product
可以想到,但是寄存器这也…见参考答案: -
一开始没理解题意。原来是让判断合法性。其中
e、f、h~k
均是非法的。首先明白一点,p
是一个结构体,a
是一个指向一个结构体的指针,重点在于b
,它并不是一个指向结构体的指针,它是一个指向指向结构体的指针,其内部存的是a
的地址而已。它不能直接拿箭头来访问结构指针成员。因为b
本身不指向结构体,而*b
存的是指向结构体的,但同时也要注意->
比*
的优先级要高!基于此,上述这几个错误选项就很清楚了,样例代码可见dem02.c
。见参考答案: -
很明显的错误。其中
b
类型不匹配,a
是指针而y
不是。d
也是类型不匹配,理由同上。 -
优点题目都给说了…帮助处理命令行参数。但该函数没在标准中就意味着其可移植性差!
-
很明显的一个问题,字符串常量的值怎么能修改呢?妥妥
SE
啊。必须得拿字符数组来做。 -
数组刚好能装下初始值,再往后添加任何内容均造成内存溢出,覆盖其它变量,造成
UB
行为。只能扩大数组规模避免该情况发生,或者提前进行判断。答案上还提及了一个潜在的问题是strcat
的使用如果这个路径名前缀应该是与几个不同的文件名一起使用时,用strcat
追加下一个文件名不会产生想要的结果。即它直接修改了前缀没发共用了… -
本题更新(2021年6月23日,在本博文的评论区进行了讨论)。本题的问题有点多,细节也很多。首先是个指针数组,其次写法有问题,在者就是
strcat
函数的第一个参数传错…最后还有溢出风险。当然,从前面就错了,要是对的话可能会溢出,和上题一样。原答案: 依旧还是溢出问题。当
filename
中的字符串长度超过 9 个字符,将溢出数组。要么申请大一点,要么动态申请内存。
13.10 编程练习
-
很眼熟的一道题,是
P188 ch09 9.14
编程第一题,当时拿字符分类函数配合if-else
做的。现在这个阶段就是拿转移表做就行了。是个很不错的练手题,这个问题还能拓展很多很多啊,转移表的方式真的是相当的优雅和简洁。初始化一个函数指针数组做转移表,宏定义求其大小,不可打印字符拿可打印字符取反来进行判断,很不错的一道题目。注意printf()
函数中输出%
是%%
,需要转义一下。见demo03.c
。 -
其实也没必要非纠结回调函数,直接调用不行吗?回调函数就是传一个函数指针进去就行了,写明相关类型即可。参考答案:这个函数可能不是特别有用,因为自己遍历列表几乎和调用一个通用函数一样容易。另一方面,这个范例有足够的价值来证明这个练习。注意,函数仍然需要知道链接在节点结构中的什么位置,所以它毕竟不是那么通用。见
demo04.c
。 -
这字体看着是真难受。直接见参考答案吧:有几个案例直接起作用;这段代码必须移到函数中。这里的主要困难在于想出一个能够满足所有必要功能需求的通用原型;它必须包含任何函数使用的每个参数。新函数必须使用公共原型,现有函数必须修改以匹配它。大多数函数最后都会至少有一个它们不需要的参数。指针必须传递给
current
,这样相应的函数才能修改i
。注意,这个解决方案比原始代码要长得多。这主要是由于为执行一行代码的任务而创建函数的开销。但是,随着事务数量的增长,与单独的事务函数相结合的跳转表将成为比大型switch
语句更易于管理。见demo05.c
。 -
sort()
排序,题目要求可以针对任意类型进行排序,很秀啊。在P344
中有qsort()
的函数原型,基本上和这要求的一致。参考答案:这个函数仿效ANSI C
库中提供的qsort
函数。唯一的两个技巧是定位每个数组元素的开始位置和交换两个元素。元素长度用于这两个任务。不过他这第三个参数:每个数组元素的长度是啥意思啊…看了
qsort()
原型及使用原来就是sizeof(类型)
这也能算作不同类型数据吗?建议跳过这个模拟题。见demo06.c
。 -
不想写了,这说了些啥啊。见参考答案:一个常见的错误是修改
argv
指针列表或参数本身。这个解决方案使用寄存器声明来提高效率。argtype
函数是从一个地方调用的,为了清晰起见,它被写成一个单独的函数。见demo07.c
。
随笔
至此,C
和指针就告一大半了,但是显然这本书讲的还是浅显的入门级别,例子不够多,实例少,代码量少,停留在概念中以及少量的 demo
。不过确实剩下的就是自己的事情了,多敲多练,多阅读阅读开源项目,观察下他们的代码风格以及程序设计。包括常用的多级指针、回调函数等等。说实话,我很少使用二级指针、回调函数,一般的代码量都比较少,大多数都在刷算法题。但这写都是工程实践上的重中之重。
针对本章过一段时间会复看,并做详细总结。本章中函数指针、指针函数、数组指针、指针数组等概念没有对比,印象不够深刻!
疑问
-
P258-P260
的高级声明部分,尤其是间接访问操作符和函数调用操作符和下标符号揉到一起。需要仔细分别,下标优先级高于间接访问,函数调用高于间接访问。仔细分别下几个例子和解释吧! -
函数指针。函数名被使用时总是由编译器把它转换为函数指针。函数指针面试的时候被考过!!!注意回调函数和转移表,回调函数用得少疑问多,至于转移表比较好理解,就是个数组,里面存的全是函数指针,即为函数指针数组。
-
命令行参数与
main()
函数参数问题。其实标准中main()
函数都是有参数的,一般我们不会传参,但也同样在前章节中有提到过,以前某版本的C
语言,声明函数原型是不需要写形参的,这就造成了歧义,所以一般来讲我们形参可以传void
。 -
字符串常量位于表达式中其值是个指针常量,很秀的一个知识点。知乎上以前看到的一个帖子:如何智能地在每个数字中间加一个「,」? 其中高赞答案,简直优雅且天秀!!!
printf(",%d" + !i, a[i]);
i
从 0 开始遍历整个数组,利用printf()
的格式化常量字符串+指针偏移,无比优雅的完成了这项任务!!!完美。