1.sizeof和strlen的区别
1.1sizeof
在学习操作符的时候我们接触到了sizeof这样一个库函数,它的作用主要是计算一个变量所占内存的大小,单位是字节,如果操作数是类型的话,计算的是使⽤类型创建的变量所占内存空间的⼤⼩。
sizeof 只关注占⽤内存空间的⼤⼩,不在乎内存中存放什么数据。
例如
1.2strlen
strlen是一个库函数,主要作用是用来求字符串长度,直到遇见\0。函数原型如下
这个库函数会从给的参数str的地址开始一直向后读取,统计\0之前所有字符个数。因为它遇到\0才会停止,所以使用它的时候可能存在越界访问的问题,造成一定的安全问题。如
显然我们字符只有3个,但是统计个数却是35。
1.3strlen和sizeof的对比
1. sizeof是操作符
2. sizeof计算操作数所占内存的⼤⼩, 单位是字节
3. 不关注内存中存放什么数据
1. strlen是库函数,使⽤需要包含头⽂件 string.h
2. srtlen是求字符串⻓度的,统计的是 \0 之前字符的隔个数
3. 关注内存中是否有 \0 ,如果没有 \0 ,就会持续往后找,可能会越界
接下来让我们一起看看一些笔试真题趴
2. 数组和指针笔试题解析
2.1一维数组
大家不妨先停下来思考思考。
在详细讲解这些代码之前,我们先理解一个数组的数组名代表了什么。一般来说,数组名代表数组首元素的地址。但是有两个例外:
1.是sizeof(数组名),数组名代表整个数组,计算整个数组的大小
2.是&数组名,数组名代表整个数组,取出整个数组的地址。
除此以外,数组名都代表的是数组首元素的地址
首先第一个是数组名,计算整个数组的大小,为4*4=16;
第二个,因为数组名没有单独放在sizeof里面所以a是数组首元素的地址,a+0还是首元素地址,而我们的地址又可以理解为指针,指针大小是4/8个字节,在64位环境下,指针大小都是八个字节;
第三个解引用a的地址,a又代表的是数组首元素,所以是4,以下出现的数字单位都是字节,我就不再过多赘述了;
第四个,可以类比第二个,a+1则是数组第二个元素的地址,同理也是8。大家也可以看作为整型指针+1;
第五个,显然a[1]就是数组第二个元素1,大小为4/8;
第六个,取地址a再加1,取出来的是数组的地址,地址的大小就应该是4/8 ;
第七个,先取地址a再解引用,就是先取出整个数组的地址再解引用,即计算整个数组大小,是我们的例外中的第二个,大小为16;
第八个,取地址数组名+1,实际上是取出了整个数组的地址再+1,但是它还是一个地址;
第九个,取出首元素的地址;
第十个,取出首元素的地址再+1。
2.2 字符数组
字符数组和一维数组一样的,同样可以按照一维数组那样理解。
第一个计算的是整个数组的大小,第二个计算的数组首元素地址.......以此类推。
接下来让我们来看看strlen的情形。
注意这里我们数组的元素是一个一个的,不像字符串一样自带\0。
第一个strlen从a开始计算,但是数组中不存在\0所以这里strlen计算的值应该是随机值,形成了越界访问,谁也不知道它会一直读取到哪里,我们只知道他到\0前一个字符才会停止。
第二个与第一个没区别。第三个解引用arr就是‘a’,我们的strlen需要的地址,但是传了一个字符a,a的ascll码值是97,而97代表的地址strlen没有资格访问,所以运行到这里我们的程序就已经崩了。
第四个同理,不过传的是‘b’。第五个取地址+数组名,也是从数组首元素开始计算,得到的也是随机值,第六个是跳过整个数组地址从数组后的第一个地址开始统计,也是随机值。第七个传的就是第二个元素的地址,也为随机值。
接下来我们继续来看一组代码
sizeof单独放了数组名,计算的是整个数组大小,有些同学会斩钉截铁的说是6,但是需要注意的是我们这里的数组是一个字符串,而字符串是自带\0的,只不过隐藏掉了,你不能说它不存在,它存在了就一定会占用内存,所以我们这里应该是6+1=7。第二个代表首元素的地址,为4/8。第三个解引用数组名得到的是首元素a,大小为1。剩下的可以参考上面的代码自行计算。我就不详细讲述了。
第一个显然是6,第二个从首元素开始统计,也为6,第三个解引用数组名得到首元素a,程序崩溃。第四个同理,第五个取出整个数组的地址进行统计,实际上还是从首元素开始统计,为6.第六个跳过了整个数组从数组地址后的第一个地址开始统计,得到的值应该是随机值,第七个从数组的第二个元素开始统计,就是5。
ok啊继续下一组代码。
首先这个字符串的起始地址放在了p里面,所以p代表地址,4/8。p+1则指向第二个字符,同样是地址,4/8。解引用p得到首字符a,char类型,大小为1。那么p[0]是什么呢?实际上就是*(p+0)。取地址p,就是指针p的地址,也是地址,大小也是4/8,可以看成二级指针。那第六个怎么理解呢?
char* *q=&p,而&p+1就是q+1,跳过一个char*的变量,也就是p指针变量后面的一个地址。这里我们可以把字符串理解为一个数组,p[0]就是首元素a,取地址再加1,就是b的地址。
第一个传入指针,也就是数组地址,从a开始统计,为6。p+1就是b的地址,解引用p得到a,程序崩溃。其他可参考上面讲诉的内容独立思考。
2.3 ⼆维数组
二维数组无非是以一维数组为元素的,可以类比一维数组。
3. 指针运算笔试题解析
3.1 题⽬1:问程序运行结果是
首先看&a+1,就是&a是取出整个数组地址,属于我们的两种例外之一,+1表示跳过了整个数组,到了5地址后的第一个地址,再强制类型转换成int*。*(a+1),解引用第二个元素,得到2。*(ptr-1),从ptr指向的地址向前移动一个整型字节大小的地址,也就是指向了5所在的地址,那么我们的结果应该是2,5。
、
3.2 题⽬2
这实际上就是指针+整型的问题,0x1就是十六进制的1,这个时候我们的小凡同学说那不就是0x100020吗,但是注意这是十六进制,所以应该是0x100014。第二个强制类型转换成unsigned long也就是整型类型,整型+1就是+1,结果应该就是0x100001。第三个强制类型转换成iunsigned int*,+1则跳过一个整型大小,结果是0x100004。这里的%p表示将指针地址以十六进制形式输出。
3.3 题⽬3
这里我们很容易会想当然0,1,2,3,4,5,都是数组中的元素,但是需要注意的是这里是括号表达式,每个括号之间都有逗号,所以每个括号表达式是一个元素,而括号表达式的结果又是后一个数字,实际上整个数组的元素是1,3,5,0,0,0。他们应该是这样排列的
a[0]就是数组首元素,那么p就指向1的位置。
3.4 题⽬4
这里我们依旧假设是x86环境,这里的p就是数组指针,指向四个inr类型元素,我们来画图理解一下。
红色的就是&p[4][2],绿色的是a[4][2],低地址-高地址,就是-4,那么将-4转换为十六进制,
可参考此图。
这里的p一次能跳过四个字节,这是需要注意的。
3.5 题⽬5
第一个不难理解,我们来看看第二个。aa是首元素地址,+1就得到下一行的地址,然后prt2往前移动一个整型类型的大小字节,就到了5所在的地址,结果就是5。
3.6 题⽬6
三个char指针分别指向work at alibaba,二级指针pa指向a的首元素地址就是指向work的指针地址。pa++,就指向了指向at指针的地址,解引用就得到l呵at的地址,所以就打印at。
3.7 题⽬7
、
我们先画出示意图。
第一个,cpp先++就是指向了char** (c+2),一次解引用得到第三个char*,二次解引用得到POINT。
第二个,先++,解引用,再--,最后解引用,这是运算顺序。结果就是ER。
第三个,cpp[-2]就是*(cpp-2),指向char**第一个,再解引用得到char*也就是first所在的地址,+3得到ST。
第四个,我们一个一个来算,先算cpp[-1]就是*(cpp-1),就是c+2再-1即*(c+2-1)得到*(c+1),拿到指向NEW的char*,+1,从E开始。