前言
本文面向已经掌握 C 语言基础语法、正在准备笔试面试或想彻底搞懂「数组与指针」的同学。如果你曾被sizeof和strlen的输出结果绕晕,或者看到&a + 1、*(*(a + 1) + 1)这类表达式就头皮发麻,那么这篇文章正是为你准备的。
本文覆盖的知识点包括:
sizeof与strlen的本质区别:一个是运算符、一个是库函数,一个算内存占用、一个数\0之前的字符个数;- 数组与指针笔试题:一维数组、字符数组、二维数组中数组名退化的各种情形;
- 指针运算笔试题:指针加减整数、指针减指针、多级指针与字符串指针数组的综合运用。
阅读建议:建议先看第 1 节把sizeof和strlen的差异吃透,再按顺序做第 2、3 节的题目。每道题请先自己写出答案,再对照解析,重点关注「数组名在什么情况下代表整个数组、什么情况下退化为首元素地址」这一条主线。文中题目均以图片形式给出,建议边看边在纸上画内存布局图,效果会好很多。
目录
- 前言
- 1.sizeof和strlen的对比
- 1.sizeof
- 2.strlen
- 3.sizeof和strlen的对比
- 2.数组和指针笔试题解析
- 1.一维数组
- 2.字符数组
- 3.二维数组
- 3.指针运算笔试题解析
- 总结
1.sizeof和strlen的对比
1.sizeof
我们在之前已经学过sizeof了,sizeof是用来计算变量所占内存空间的大小的,单位为是字节,如果操作数是类型的话,计算的是使用类型创建的变量所占内存空间的大小。
sizeof只关注占用内存空间的大小,不在乎内存中存放什么数据。
比如:
2.strlen
strlen是C语言库函数,在使用该函数前,必须要包含对应的头文件:#include <stdio.h>,功能是求字符串长度。函数的原型如下:
size_tstrlen(constchar*str);我们在之前已经模拟实现过这个函数,这里就不做过多的演示了。
strlen函数统计的是从参数str存放的地址开始往后统计\0之前字符的个数,strlen函数直到遇到\0才会停止查找,有时候可能会存在越界查找。
3.sizeof和strlen的对比
2.数组和指针笔试题解析
首先我们先回忆一下数组名的意义:
1.sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
2.&数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
3.除了以上2种情况以外,数组名均表示首元素的地址。
1.一维数组
注:题目%d均换成%zu
题目:
解析:
2.字符数组
代码1:
解析:
代码2:
解析:
代码3:
解析:
代码4:
解析:
代码5:
解析:
代码6:
解析:
3.二维数组
代码1:
解析:
关于sizeof并不会真实地去访问数据的解释:在编译链接阶段,sizeof就将括号里面的表达式转化为了二进制的指令,等到运行代码时,sizeof括号内的表达式早就不存在了。
例如:
运行结果:
我们通过运行结果可以发现s没有发生改变,说明了sizeof括号里的表达式并不会真实的计算
3.指针运算笔试题解析
题目1:
解析:
题目2:
解析:
但是在16进制下,是要打印8个16进制的比特位的,所以结果为:
题目3:
解析:
题目4:
解析:我们先来分析一下
指针-指针的结果是2个指针之间相差的元素个数,而小地址-大地址算出来的是一个负数
&p[4][2]-&a[4][2]==-4,但是由于%d和%p打印的区别:
但是%d打印在屏幕上的是原码,-4就是10进制的,转化为原码还是-4
%p的打印如下图:
运行结果:
题目5:
解析:
题目6:
解析:
题目7:
解析:
在打印之前,变量之间的关系应该是这样的:
按printf函数的打印顺序,分别称第几次打印
第一次打印:
cpp++,再解引用,打印得到POINT,接着因为++改变了原来cpp的指向,所以第二次打印的图是基于第一次打印完成的图继续做的
第二次打印:
++cpp,解引用得到cp[3],cp–,得到c,c存放的是E的地址,加3往后得到的是E,最后打印出来的是ER
第三次打印的图是基于第二次打印完成的图继续做的
第三次打印:
第四次打印的图是基于第二次打印完成的图继续做的
第四次打印:
cpp-1解引用得到是c+2的地址,再此基础上减1,得到的是c[1]里面存放的N的地址,再加1,得到的是E的地址,最后打印得到EW。
运行结果:
总结
- 数组名退化规则:只有在
sizeof(数组名)和&数组名两种场景下,数组名才表示整个数组;其余任何场合(包括作为函数实参、参与算术运算、被解引用)数组名都会退化为首元素地址,二维数组则退化为第一行(一维数组)的地址。 sizeof是运算符而非函数:它在编译期就完成求值,括号内的表达式不会被真实计算,因此sizeof不会产生任何运行时副作用,也不会访问内存。strlen是库函数:它从给定地址开始逐个字符向后统计,直到遇到\0才停止,因此传入的若不是以\0结尾的字符串,就可能发生越界查找,结果不可预期。sizeof与strlen的对比:sizeof计算的是类型或变量占用的字节数(含\0),strlen统计的是\0之前的有效字符个数(不含\0);前者在编译期确定,后者在运行时遍历。- 指针运算的本质:指针加减整数按所指类型的大小步进,指针减指针得到两者之间相差的元素个数(可能为负),而
%d与%p打印同一地址会得到截然不同的结果,做题时务必看清格式符与指针类型。