C语言核中核——指针(5)

本文对比了C语言中的sizeof和strlen函数,前者用于计算内存占用大小,后者统计字符串长度,同时讨论了数组和指针的相关概念,以及在面试题中的应用场景,涉及数组名的特殊含义和越界访问问题。
摘要由CSDN通过智能技术生成

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开始。

  • 21
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值