文章目录
- ▶️1.sizeof和strlen的对比
- ➡️1.1 sizeof是什么?
- ➡️1.2sizeof用法举例
- ▶️1.3strlen是什么?
- ▶️1.4 strlen函数用法举例:
- ▶️1.5 strlen和sizeof的对比
- ▶️2. 数组和指针笔试题:
- ▶️2.1 整型数组和指针笔试题1:
- ▶️2.1.1 整型数组和指针笔试题解析~
- ▶️ 2.1.1.1 VS运行结果展示:
- ▶️2.2 字符数组和指针笔试题
- ▶️2.2.1 字符数组和指针笔试题1
- ▶️2.2.1 字符数组和指针笔试题解析:
- ▶️2.2.1.1 VS运行结果展示:
- ▶️2.2.2 字符数组和指针笔试题2
- ▶️2.2.2 字符数组和指针笔试题解析:
- ▶️2.2.2.1 VS运行结果展示:
- ▶️2.2.3 字符数组和指针笔试题3
- ▶️2.2.3字符数组和指针笔试题解析:
- ▶️2.2.3.1 VS运行结果展示:
- ▶️2.2.4 字符数组和指针笔试题4
- ▶️2.2.4字符数组和指针笔试题解析:
- ▶️2.2.4.1 VS运行结果展示:
- ▶️2.2.5 字符数组和指针笔试题5
- ▶️2.2.5字符数组和指针笔试题解析:
- ▶️2.2.5.1 VS运行结果展示:
- ▶️2.2.6 字符数组和指针笔试题6
- ▶️2.2.6字符数组和指针笔试题解析:
- ▶️2.2.6.1 VS运行结果展示:
在上一次介绍篇博客中:
C语言-指针讲解(4)
我们给大家主要讲解了指针的进阶用法,让我们来回顾一下讲了什么吧!
- 回调函数是通过函数指针调用的函数,以及回调函数用法举例。
- 细致地讲解qsort函数的参数,以及用qsort函数排序任意类型用法举例,同时qsort函数是运用了快速排序的思想。
- 改造普通的整型冒泡排序算法,让它模拟实现成一个qsort函数,然后用模拟实现的qsort函数对各种数据类型进行排序操作举例。
那么这次博主根据前面5讲的指针介绍,给大家讲解C语言指针相关的笔试题:
这次讲的内容如下:
首先,在讲解C语言指针笔试题前,我们先对sizeof操作符以及strlen库函数进行详细的介绍。
▶️1.sizeof和strlen的对比
➡️1.1 sizeof是什么?
- sizeof顾名思义,它就是一种操作符。
- 并且sizeof操作符是用来计算变量所占内存空间大小的,单位是字节,计算的是适用类型创建的变量所占内存空间的大小。
➡️1.2sizeof用法举例
如下所示:
#inculde <stdio.h>int main(){int a = 10;printf("%d\n", sizeof(a));printf("%d\n", sizeof a);printf("%d\n", sizeof(int));return 0;}
vs编译结果如下:
代码分析:
1.我们通过上图发现用sizeof计算变量的大小或类型的大小,只要它们的类型相同,那么它们的大小也是相等的,而且如果是计算创建变量a的大小,是无需加上括号的,反倒如果是计算int类型的大小,是需要对它加上一个括号的。
总结: sizeof只关注内存存放空间的大小,不在乎内存中存放的数据。
▶️1.3strlen是什么?
strlen顾名思义,就是C语言的库函数,功能是求字符串长度。
它的函数原型如下:
1 size_t strlen (const char * str);
▶️1.4 strlen函数用法举例:
如下代码所示:
#include int main(){char arr1[3] = { 'a', 'b', 'c' };char arr2[] = "abc";printf("%d\n", strlen(arr1));printf("%d\n", strlen(arr2));return 0;}
vs运行结果如下:
有同学可能会有疑问了,为什么数组arr1和数组arr2存的内容都一样,但为什么用strlen函数计算它们的长度却不相同呢” />代码分析:
- 这是因为strlen函数本质上是统计参数str中这个地址开始往后,\0之前的字符。strlen函数会一直向后找\0字符,直到找到为止。所以:这样有可能会导致数组越界访问。
如下所示:
- 从上图,我们发现arr1数组是一个一个字符一个字符地存放的,如果我们不把\0存进数组中,strlen函数会一直往后找\0。直到找到为止。
- 反倒是strlen计算arr2数组就不一样了,因为arr2数组是以字符串的形式存放在数组中,而\0是字符串的结束标志,因此,如果我们以字符串的形式存放在数组,strlen函数会把arr2数组中‘\0’之前的字符个数都加起来。
▶️1.5 strlen和sizeof的对比
我们把strlen函数和sizeof操作符区别做了张图总结
如下图所示:
▶️2. 数组和指针笔试题:
再讲解数组和指针笔试题之前,我们再来复习一下数组名的概念。
我们之前在这篇博客:C语言-指针讲解(2)讲过:通常情况下,数组名是数组首元素的地址。 但是有两个例外:
1.sizeof(数组名),sizeof里面单独放数组名,这里的数组名代表的是整个数组,计算的是整个数组的大小。
2.&arr表示整个数组,&是指取出整个数组的地址,(整个数组的地址和数组首元素的地址还是有区别的。)
▶️2.1 整型数组和指针笔试题1:
题目如下:
int main(){int a[] = {1,2,3,4};printf("%zd\n",sizeof(a));//1.输出结果是什么呢?printf("%zd\n",sizeof(a+0));//2.输出结果是什么呢?printf("%zd\n",sizeof(*a));//3.输出结果是什么呢?printf("%zd\n",sizeof(a+1));//4.输出结果是什么呢?printf("%zd\n",sizeof(a[1]));//5.输出结果是什么呢?printf("%zd\n",sizeof(&a));//6.输出结果是什么呢?printf("%zd\n",sizeof(*&a));//7.输出结果是什么呢?printf("%d\n",sizeof(&a+1));//8.输出结果是什么呢?printf("%d\n",sizeof(&a[0]));//9.输出结果是什么呢?printf("%d\n",sizeof(&a[0]+1));//10.输出结果是什么呢?return 0;}
大家不妨先思考一下这些题,看看它们的输出结果是什么?
▶️2.1.1 整型数组和指针笔试题解析~
1.由于是sizeof(数组名),计算的是整个数组的大小,单位是字节。这里的整型数组有四个元素,每个元素占4个字节,那么也就是总共占16个字节。
2.这里的数组名并没有单独放在sizeof内部,也没有&,所以a就是数组首元素的地址,是地址就是4/8个字节。需要注意的是,这里的(a+0) == &a[0]。
3.a就是数组首元素的地址,a==&a[0],然后*a其实就是第一个元素,也就是a[0],大小就是4个字节。
4.a就是数组首元素的地址(&a[0]–int ),a+1—>&a[1],a+1就是第二个元素的地址。它的大小也就是4/8字节。
5.计算第2个元素的大小,单位是字节 - 4
6.&a虽然取出的是数组的地址,但是数组的地址也是地址,是地址大小就是4/8个字节。
7.16
8.&a+1是跳过整个数组后的地址,是地址大小就是4/8个字节。
9.首元素的地址,4/8
10.因为&a[0] - int,那么+1就是到第二个元素的地址,也就是&a[1]的地址,大小是4/8个字节。
这里我们重点讲一下第七道和第八道题目:
7.这里我们有两种方法来分析这道题:
1.这里的&a --> int(*p)[4]=&a,也就是说这里的p指向的是一个大小为4的整型数组,这里的 * p是指访问一个数组的大小,然后p+1是跳过一个数组的大小。
&a取出的是数组的地址,数组的地址应该放到数组指针里面去,然后它的数组指针类型是int( * )[4]。然后这里 * p是访问整个数组的大小,也就是说这个数组占了16个字节。
2.这里的&a就是取出整个数组的地址,它的类型是个数组指针,我们对数组指针进行解引用操作,访问的不就是整个数组吗?所以sizeof整个数组不就是16吗?
8.如下图所示:
虽然我们通过&a+1是个地址,是地址就是4/8个字节。但是我们通过图中更能直观地发现&a+1指向的是第四个元素后面的地址。
▶️ 2.1.1.1 VS运行结果展示:
那我们分析的是否正确呢,接下来我们分别用vs的x64环境和x86环境来测试一下运行结果~
x86环境:
x64环境
▶️2.2 字符数组和指针笔试题
▶️2.2.1 字符数组和指针笔试题1
题目如下:
//字符数组1.1#include int main(){ char arr[] = { 'a','b','c','d','e','f' }; printf("%zd\n", sizeof(arr));//1.输出结果是什么? printf("%zd\n", sizeof(arr + 0));//2.输出结果是什么? printf("%zd\n", sizeof(*arr));//3.输出结果是什么? printf("%zd\n", sizeof(arr[1]));//4.输出结果是什么? printf("%zd\n", sizeof(&arr));//5.输出结果是什么? printf("%zd\n", sizeof(&arr + 1));//6.输出结果是什么? printf("%zd\n", sizeof(&arr[0] + 1));//7.输出结果是什么? return 0;}
大家不妨先思考这几道题,待会我们会统一讲解一下。
▶️2.2.1 字符数组和指针笔试题解析:
1.由于是sizeof(数组名),计算的是整个数组的大小,单位是字节。这里的整型数组有六个元素,每个元素占1个字节,那么也就是总共占6个字节。
2.arr是数组首元素的地址,arr+0 还是首元素的地址 是地址大小就是4/8个字节。
3.arr是数组首元素的地址,*arr就是首元素,就占一个字符大小就是1个字节。
4.arr[1]就是数组的第二个元素,大小是1个字节。
5.&arr 是数组的地址,数组的地址也是地址,大小就是4/8。
6.&arr+1 是跳过整个数组,指向f的后面 4/8。
7.&arr[0]是首元素的地址,&arr[0]+1就是第二个元素的地址 4/8。
▶️2.2.1.1 VS运行结果展示:
我们不妨用vs来运行一下此代码,看看我们分析得是否正确~
x64环境:
x86环境:
▶️2.2.2 字符数组和指针笔试题2
题目如下:
#include #include int main(){//字符数组1.2char arr[] = { 'a','b','c','d','e','f' };printf("%zd\n", strlen(arr));//1.输出结果是什么呢?printf("%zd\n", strlen(arr + 0));//2.输出结果是什么呢?printf("%zd\n", strlen(*arr));//3.输出结果是什么呢?printf("%zd\n", strlen(arr[1]));//4.输出结果是什么呢?printf("%zd\n", strlen(&arr));//5.输出结果是什么呢?printf("%zd\n", strlen(&arr + 1));//6.输出结果是什么呢?printf("%zd\n", strlen(&arr[0] + 1));//7.输出结果是什么呢?return 0;}
大家不妨先思考这几道题,待会我们会统一讲解一下。
▶️2.2.2 字符数组和指针笔试题解析:
1.如下图所示:
我们发现,这个arr数组它是没有\0的,也就是说当这个strlen函数拿到首元素字符a的地址,会一直往后找\0。
它有可能会在内存中的某个位置找到\0,然后统计\0之前的字符个数,因此我们是不确定它的值是多少,因此它是个随机值。
2.同样地,这个输出结果也是随机值,因为arr+0,意思就是首元素地址跳过0个元素,本质上还是首元素的地址。
所以strlen函数拿到的也是首元素a的地址,然后向后面找\0,但是arr数组里面没有\0,因此它本质上还是个随机值。
如下图所示:
3.我们从上面两张图中可以发现,* arr就是把它首元素的地址进行解引用,得到的是它的元素a,a的ASCLL码值是97。我们知道strlen函数参数的是一个指针类型的,我们把97它传过去的话,它就把它当成一个地址。
97这个地址你说想访问就访问吗?哪个地址你能说想访问就访问呢?我们通过vs调试也发现0x00000061是访问异常,也就是我们常说的非法访问。那么这个输出结果就会报错。
4.同样地,我们发现arr[1]就是数组的第二个元素。也就是传过去的是字符b,字符b的ASCLL码值是98,也就是传过去的地址是98。也会属于非法访问,所以它的输出结果同样也会出现报错。
总结:通过上面分析,我们知道只能传给strlen地址。而不能胡传,如果胡传,strlen函数会误以为地址,最终就会出现报错。
5.我们发现&arr的类型是char (*p)[6],然后如果把它传到strlen函数里面的话,它的参数类型就强制转换为char *。
站在strlen函数的角度,它依然拿到的是arr起始位置的地址,也就是从字符a向后数的,数到的结果还是随机值。所以它的结果跟第一道题和第二道题输出结果是一样的,都是随机值。
如下图所示:
6.从上图,我们发现,&arr+1指向的是字符f后面的地址,也就是从f后面的地址往后去找\0,
所以从这里往后数,那个内存放什么,我们就更不知道了,但是这个地方得到的随机值跟前面那些随机值是不一样的,因为它们中间相差了几个字符。
如下图所示:
7.从上图我们发现&arr[0]+1得到的是字符b的地址,然后把b的地址传给strlen函数,从b的位置一直往后数找\0,所以它这里的输出结果依然是随机值。
只不过它的随机值跟第一题和第二题输出结果的随机值 少了个1。
综上所述: 对于指针来说 我们要搞清楚它指向哪里,才能知道它指向的内容是什么。
▶️2.2.2.1 VS运行结果展示:
我们不妨用vs来运行一下此代码,看看我们分析得是否正确~
这里需要注意的是: 如果我们直接运行的话,第三题和第四题输出结果是会报错的。那它会影响后面第五-第七题的输出结果,因此我们要先将它的代码先注释掉再运行。
x64环境:
x86环境:
我们从x64环境和x86环境中也知道,除了第三和第四题,其他题的输出结果均为随机值。 说明我们的分析是正确的。
▶️2.2.3 字符数组和指针笔试题3
题目如下:
#include int main() { //字符数组2.1 char arr[] = "abcdef"; printf("%zd\n", sizeof(arr));//1.输出结果是什么? printf("%zd\n", sizeof(arr + 0));//2.输出结果是什么? printf("%zd\n", sizeof(*arr));//3.输出结果是什么? printf("%zd\n", sizeof(arr[1]));//4.输出结果是什么? printf("%zd\n", sizeof(&arr));//5.输出结果是什么? printf("%zd\n", sizeof(&arr + 1));//6.输出结果是什么? printf("%zd\n", sizeof(&arr[0] + 1));//7.输出结果是什么? return 0;}
这里需要注意的是: 由于arr里面存放的是字符串,我们知道\0是字符串的结束标志,所以里面默认存放的是有\0的。
如下所示:
大家不妨先思考这几道题,待会我们会统一讲解一下。
▶️2.2.3字符数组和指针笔试题解析:
1.我们知道sizeof(数组名)是计算整个数组的大小。
那这里数组里面有7个元素,而且该数组为char类型,所以每个元素是占一个字节,那这里就总共占了7个字节。
2.这里的arr+0 表示arr跳过0个元素,本质上还是首元素的地址,所以大小就是4/8个字节。
3.arr表示数组首元素的地址,*arr就是首元素,大小就是1字节
4.arr[1]是第二个元素,大小也是1字节。
5.&arr是数组的地址,但是也是地址,是地址大小就是4/8个字节。
这里我们重点详解一下第六题和第七题
如下图所示:
6.从上图,我们发现&arr+1指向的是\0后面的地址,因此我们可以推导出&arr+1就是跳过整个数组的那个地址。大小为4/8个字节。
如下图所示:
7.同样地,我们从上图发现&arr[0]+1指向的是数组中第二个元素的地址,所以它的大小为4/8个字节。
▶️2.2.3.1 VS运行结果展示:
我们不妨用vs测试一下结果,看看分析的是否正确~
x86环境运行结果:
x64环境运行结果:
▶️2.2.4 字符数组和指针笔试题4
题目如下:
int main() {//字符数组2.2char arr[] = "abcdef";printf("%d\n", strlen(arr));//1.输出结果是什么呢?printf("%d\n", strlen(arr + 0));//2.输出结果是什么呢?printf("%d\n", strlen(*arr));//3.输出结果是什么呢?printf("%d\n", strlen(arr[1]));//4.输出结果是什么呢?printf("%d\n", strlen(&arr));//5.输出结果是什么呢?printf("%d\n", strlen(&arr + 1));//6.输出结果是什么呢?printf("%d\n", strlen(&arr[0] + 1));//7.输出结果是什么呢?return 0;}
大家不妨先思考这几道题,待会我们会统一讲解一下。
▶️2.2.4字符数组和指针笔试题解析:
如下图所示:
1.我们发现strlen(arr) 本质上就是把首元素的地址a传给strlen函数,然后strlen就往后找\0,统计\0之前的个数,从上图,我们能直观地分析到\0之前有6个元素个数,因此它的输出结果是6。
2.同样地,我们发现arr+0 还是首元素跳过0个元素,本质上还是把首元素的地址传给strlen函数,统计\0之前的字符个数,所以它的输出结果依然是6。
3.这里我们前面已经介绍过,* arr就是数组首元素a,它的Ascll码值为97当做地址传给strlen函数,但是这个地址属于是非法访问,因此会出现报错情况。
4.同样地,arr[1]就是数组首元素a,它的Ascll码值为98当做地址传给strlen函数,但是这个地址属于是非法访问,因此会出现报错情况。
5.我们前面已经介绍过这里的&arr本质上它的类型就是char (* p)[7],当我们把这个数组地址传入strlen函数内部,会把它强转为char*类型。
站在strlen函数的角度,它依然拿到的是arr起始位置的地址,也就是从字符a的地址向后数的,直到遇到\0为止,所以它的输出结果也是6。
如下图所示:
6.我们从上图发现&arr+1是指向\0的后面,因此它后面内存放的是什么,以及后面是否有\0,我们是不知道的。
因此它的输出是个随机值。
如下图所示:
7.从上图,我们更能直观地发现,&arr[0]+1指向的是字符b的地址,当我们把字符b的地址传入strlen函数,
会统计字符b的地址到\0之间的字符个数。 所以它的输出结果就是5。
▶️2.2.4.1 VS运行结果展示:
由于我们发现第三题和第四题的结果输出是报错的,我们先用注释把它屏蔽掉,再拿vs来运行此代码。
这里博主用x64环境下运行此代码:
从上图,我们发现第六题输出结果是26,是个随机值。
另外,虽然VS编译器中运行没有报错,但是我们仔细查看的话,会发现&arr是一个数组指针类型,它跟strlen函数参数const char * 指针类型不太一样,所以这里有可能会出现被警告情况。
▶️2.2.5 字符数组和指针笔试题5
题目如下:
#include int main() {//字符数组3.1char* p = "abcdef";printf("%zd\n", sizeof(p));//1.输出结果是什么printf("%zd\n", sizeof(p + 1));//2.输出结果是什么printf("%zd\n", sizeof(*p));//3.输出结果是什么printf("%zd\n", sizeof(p[0]));//4.输出结果是什么printf("%zd\n", sizeof(&p));//5.输出结果是什么printf("%zd\n", sizeof(&p + 1));//6.输出结果是什么printf("%zd\n", sizeof(&p[0] + 1));//7.输出结果是什么return 0;}
如下图所示:
这里大家需要注意的是:我们根据上图可以知道,这里的”abcdef”本质上是一个常量字符串,这里的常量字符串的首字符的地址是放到指针变量p里面去。假设a的地址为0x0012ff40,那么p的地址放的也是0x0012ff40,正因为有这个地址才能找到它。
大家不妨先思考这几道题,待会我们会统一讲解一下。
▶️2.2.5字符数组和指针笔试题解析:
1.我们发现:p是一个指针变量,大小是4/8字节。
2.由于我们知道p指向的是a的地址,那么p+1指向的是a的地址,假设p的地址是0xff12ff40,那么p+1指向的产生b的地址,也就是0xff12ff41–>‘b’,所以p+1是字符b的地址,大小同样是4/8个字节。
3.我们知道p指向的是a的地址,那* p指向的是字符a,类型是char *, 所以 * p是首字符,大小为1个字节。
如下图所示:
4.这道题我们用两种方式来解读:
一.我们从上图发现:我们发现,这个字符串画出来也像数组一样,连续放到内存空间里面,假设我们把这个常量字符串想象成一个数组,
那它有下标,a的下标为0,b的下标为1,c的下标为2,以此类推,可以得知f的下标为5。
数组名也是数组首元素的地址,那么p可以理解为后面这个数组名,那p[0]访问数组下标为0的元素。也就是字符a,同样也是占了一个字节。
二.p[0]从计算的角度被转换为*(p+0)是这个a的地址,那(p+0)也是这个位置的地址。
那解引用不就是对这个a访问吗?也就是占了一个字节。
如下图所示:
5.从上图,我们知道&p相当于是个二级指针,是p的地址,既然是地址,那地址大小就是4/8个字节。
如下图所示:
6.这道题我们先举个例子,比如从上图,我们得知* p是访问一个整型的大小,p+1是跳过一个整型。 接着看下图
然后,我们知道,p的类型其实是char *,那如果我们&p我就会把这个地址放到 ** pp,那pp的类型就是char ** 类型的。
char *p;//1.char ** pp=&p;//2.
从上面第二行代码我们知道:pp是一个char ** 类型的,第二行代码中pp左边的那个* 代表pp是一个指针,而前面的char *表示pp指向的对象是p,它的类型为char *,而第一行的char *和第二行的 char *是一致的。
这足矣说明第二行的代码char * 表示p所指向的对象就是char * 。 那我们是不是能得出这个结论:
&p+1=pp+1?因为我们把&p交给pp了,所以&p+1=pp+1,那pp+1要跳过几个字节,是不是跳过一个char * 对象的大小,那大家想象一下。
如上图红框所示:如果说pp指向p的话,p的地址是0x0012ff40。 那pp的地址也是0x0012ff40,那pp要跳过一个char * 对象的话,那此时p不就是char *类型的对象吗?
那把char *对象跳过的话,不是指向0地址处后面的地址吗?实际上,它就是跳过一个指针变量的大小,从p所在空间的内部跳过去的,因为这块空间用的是自己的地址,那这+1就整块空间到了p后面的地址。
总结:因为&p+1也是地址,&p+1是跳过p变量后的地址。那大小就是4/8个字节。
7.因为我们前面已经介绍过p[0]是首字符a,那我们把它的地址取出来,就是a的地址,+1,那也就是b的地址对不对。
同样地,它的大小是占4/8个字节。
▶️2.2.5.1 VS运行结果展示:
我们用vs来测试一下,看看我们分析的结果是否正确?
我们分别以x64和x86环境来演示一下:
x64环境:
x86环境:
▶️2.2.6 字符数组和指针笔试题6
题目如下:
#include #include int main() {//字符数组3.2char* p = "abcdef";printf("%zd\n", strlen(p));//1.输出结果是什么printf("%zd\n", strlen(p + 1));//2.输出结果是什么printf("%zd\n", strlen(*p));//3.输出结果是什么printf("%zd\n", strlen(p[0]));//4.输出结果是什么printf("%zd\n", strlen(&p));//5.输出结果是什么printf("%zd\n", strlen(&p + 1));//6.输出结果是什么printf("%zd\n", strlen(&p[0] + 1));//7.输出结果是什么return 0;}
如下图所示:
需要注意的是:这里的内存布局跟上个题目是一样的,同样指针变量p拿到的是字符串中首字符a的地址。
好了,大家先思考这七道题的输出结果是什么,等下我们会进行细致的讲解~
▶️2.2.6字符数组和指针笔试题解析:
如下图所示:
1.由于我们知道abcdef是个常量字符串,字符串中是有\0。
通过上图,p中存放的a的地址,然后从a的地址开始往后访问。所以它的字符串长度为6。
2.同样的道理,因为我们已经知道p拿到的首元素a的地址,那么p+1相当于拿到的是字符b的地址,然后往后找\0。
3.因为p是拿到字符a的地址,那么 *p拿到的是a的字符。
但是我们前面已经讲过,它会把字符a的Ascll码值作为地址传给strlen。这就属于是非法访问了,因此会报错。
4.同样地,我们之前讲过,因为p[0]== * (p+0)== * p,因为p拿到首字符a的地址,那(p+0)跳过0个元素同样也是拿到a的地址。
对其进行解引用也是拿到字符a,如果把a的ascll码值传入strlen函数同样也会报错。
5.这道题估计很多同学会误以为它的字符串长度是6。
他们可能以为是从的首元素a的地址往后数。但实际上并不是。接下来我给大家解释一下:
如下图所示:
它实际上是从p的内存空间往后数的,那p这个内存空间放什么我们知道吗?我们刚刚举的0x0012ff40这个地址我们都是假设的。
我们只是假设它放了这个地址,但是具体编译器里面分配了什么地址,我们是不知道的。当然,这里的地址也不是随机的,编译器一定会指派一个有效的地址,但是它地址的值在内存存的是什么,每个字节放的是什么,我们是不知道的,所以我们这里是不能确定答案的。
这里什么时候遇到\0,我们也是不可知的。
因此我们可以得出以下结论:&p是p的地址,它是从p的所占空间起始位置开始查找的,因此它是个随机值。
当然,有同学可能会有这个疑问,要是p的内存中有00就不是随机值了?
这个想法是错的,我们来给大家解释一下:
我们在VS的x64环境通过调试,然后观察&p内存,因为x64环境下,指针变量是占8个字节的,因此我们就调成8列,每列分别是一个字节,然后我们发现00是在第七个字节的位置才遇到的,但是也有可能在前面第二个字节,第三个字节遇到00,是不是这个道理呀,所以不一定是第七个。 所以这个随机值我们是无法预测的。
如下图所示:
6.从图中,我们可以看出&p指向的是p的内存空间起始位置,然后&p+1就相当于跳过一个p内存空间的地址,它就指向下一个内存空间的起始位置。
那从这个位置往后,它也是个内存空间啊,那它内存是什么?什么时候遇到\0,我们同样也是不知道的,那从这个位置往后数,这也是不可控的。因此它也是个随机值。
需要注意的是:
p所占的空间和字符串所占的空间根本就不是一回事,因为p有p的空间,字符串有字符串的空间,只不过我把字符串的首字符地址放在p里面去,所以这两块内存不一定是连续的,也可能是无关的,因此这是两个独立的空间。
7.p[0]是第一个元素,那&p[0]就是把它的地址取出来,就是a的地址,+1,就是从b的地址一直往后数,一直数到\0,所以它的长度是5。
▶️2.2.6.1 VS运行结果展示:
我们用vs来测试一下,看看我们分析的结果是否正确?
首先,先把分析为报错结果的第三题和第四题的代码先注释掉。
然后分别以x64和x86环境来运行一下此代码:
x64环境:
如上图,我们发现第五题和第六题的运行结果分别为6和30。
这里可能有同学对于第五题的运行结果有所疑问,为什么在x64环境下
printf("%zd\n", strlen(&p);
这行代码的运行结果是6,但其实它这个是随机值。为什么呢?
原因如下:
如上图所示:我们通过调试发现恰好那个\0刚好在内存空间p的第七个字节的位置,所以它的字符串长度恰好为。
但是它跟字符串长度是没有任何关系的。
比如说,我们将它那个常量字符串改为10,它运行起来一样是为6的。
如下:
x86环境:
同样地,我们拿x86环境下运行,第五题和第六题运行结果同样是一个随机值。
总结: 这里我们三种不同类型的字符数组都进行了讲解,分别是:
1.拿多个字符来初始化一个字符数组的。这里面是没有\0。
2.拿一个字符串来初始一个数组的情况,这里面的字符串是包括了\0。
3.拿一个字符指针来指向一个常量字符串的情况,它里面虽然是有\0,但是p不是一个数组空间,它是一个变量空间,这个变量存放着p的地址。
希望博主讲的这三种类型的字符数组,大家能理解透彻!!
另外,博主这里只是讲一部分的数组和指针的笔试题,还有一部分的笔试题留到下次的博客再讲解哦~
** 如果觉得博主写的不错的话,**
欢迎大家一键三连支持一下博主,谢谢大家!!!