C语言-数组指针笔试题讲解(1)-干货满满!!!

文章目录

  • ▶️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 <stdio.h>
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 <stdio.h>
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 <stdio.h>
#include <string.h>

int main()
{
	//字符数组1.2
	char 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 <stdio.h>
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.2
	char 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 <stdio.h>
int main() {
	
	//字符数组3.1
	char* 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 <stdio.h>
#include <string.h>

int main() {
	
	//字符数组3.2
	char* 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的地址。

希望博主讲的这三种类型的字符数组,大家能理解透彻!!
在这里插入图片描述另外,博主这里只是讲一部分的数组和指针的笔试题,还有一部分的笔试题留到下次的博客再讲解哦~
** 如果觉得博主写的不错的话,**
在这里插入图片描述
欢迎大家一键三连支持一下博主,谢谢大家!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/257642.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

优化问题笔记(2)

目录 3. 约束优化问题的全局解3.1 凸优化问题3.2 二次优化问题3.3 无约束二次优化问题3.4 一个典型的二次等式约束二次优化问题 Reference 3. 约束优化问题的全局解 3.1 凸优化问题 局部解成为全局解的一类重要的优化问题是所谓凸优化问题. 我们称优化问题 ( f , D ) (f,\ma…

性能测试之全链路压测流量模型你了解多少

前言 现在全链路越来越火&#xff0c;各大厂商也纷纷推出了自己的全链路压测测试方案。特别是针对全链路压测流量模型&#xff0c;各家方案都有所不同。最近我看了一些这方面的资料&#xff0c;有一些感悟。分享给大家。 全链路压测流量模型的梳理呢&#xff0c;这里就先不讲…

Gemini Pro API 详细申请步骤

Gemini Pro API 详细申请步骤 什么是 Gemini ? 上周&#xff0c;谷歌发布了 Gemini&#xff08;双子座&#xff09;&#xff0c;它是谷歌最新、最强大的人工智能模型&#xff0c;旨在迎头痛击 OpenAI 的 GPT。Gemini 在构建时考虑到了多模态性&#xff0c;这意味着它能够理解…

【日常总结】连接Mysql,打开数据表非常慢

问题 Navicat 连接mysql时&#xff0c;第二次打开非常慢 原因 Mysql服务器端会定时清理长时间不活跃空闲的数据库连接&#xff0c;以此优化数据库的性能。 解决方案 数据库右键---编辑连接--高级---保持连接间隔30秒 带来的问题 每次打开Navicat时&#xff0c;设置设置自动…

2023-南京荣耀Honor最新探访-OPEN DAY,实况记录!

前言 金九银十的秋招季缓缓落幕&#xff01; 接完offer&#xff0c;博主也回归啦&#xff01;有幸带着公开公平的心态&#xff0c;给大家分享一波南京荣耀总部的实况解密&#xff01; 最基础的环境状况&#xff0c;有图有真相&#xff01;~上图~ 民以食为天-南京荣耀食堂&…

issue阶段的选择电路的实现

1-of-M的仲裁电路 为什么要实现oldest-first 功能的仲裁呢&#xff1f; 这是考虑到越是旧的指令&#xff0c;和它存在相关性的指令也就越多&#xff0c;因此优先执行最旧的指令&#xff0c;则可以唤醒更多的指令&#xff0c;能够有效地提高处理器执行指令的并行度,而且最旧的指…

MapReduce和Yarn部署+入门

看的黑马视频记的笔记 目录 1.入门知识点 分布式计算&#xff1a; 概念&#xff1a; 两种模式&#xff1a; MapReduce&#xff08;分布式计算&#xff0c;分散汇总模式&#xff09; 概念 执行原理 注&#xff1a; Yarn&#xff08;分布式资源调度&#xff09; 概述 Y…

Linux Mint 21.3 代号为“Virginia”开启下载

Linux Mint 团队今天放出了 Linux Mint 21.3 Beta ISO 镜像&#xff0c;正式版计划在今年圣诞节发布。 支持 在实验性支持 Wayland 之外&#xff0c;Cinnamon 6.0 版 Linux Mint 21.3 Beta 镜像还带来了其它改进&#xff0c;Nemo 文件夹管理器右键菜单支持下载相关操作。 Cin…

SpringBoot之分层解耦以及 IOCDI的详细解析

### 3.2 分层解耦 刚才我们学习过程序分层思想了&#xff0c;接下来呢&#xff0c;我们来学习下程序的解耦思想。 解耦&#xff1a;解除耦合。 #### 3.2.1 耦合问题 首先需要了解软件开发涉及到的两个概念&#xff1a;内聚和耦合。 - 内聚&#xff1a;软件中各个功能模块内…

机器学习---聚类(原型聚类、密度聚类、层次聚类)

1. 原型聚类 原型聚类也称为“基于原型的聚类” (prototype-based clustering)&#xff0c;此类算法假设聚类结构能通过一 组原型刻画。算法过程&#xff1a;通常情况下&#xff0c;算法先对原型进行初始化&#xff0c;再对原型进行迭代更新求解。著 名的原型聚类算法&#…

OneBlog Shiro 反序列化漏洞复现

0x01 产品简介 OneBlog 是一个简洁美观、功能强大并且自适应的Java博客。使用springboot开发,前端使用Bootstrap。支持移动端自适应,配有完备的前台和后台管理功能。 0x02 漏洞概述 OneBlog v2.2.2 及之前的版本存在 shiro 反序列化漏洞,该漏洞源于软件存在硬编码的 shir…

【数据结构和算法】盛最多水的容器

其他系列文章导航 Java基础合集数据结构与算法合集 设计模式合集 多线程合集 分布式合集 ES合集 文章目录 其他系列文章导航 文章目录 前言 一、题目描述 二、题解 2.1 方法一&#xff1a;暴力枚举 2.2 方法二&#xff1a;双指针 三、代码 3.1 方法一&#xff1a;暴力…

递归实现归并排序与测试各类排序的性能

目录 基本思想 代码实现 时空复杂度 测试各排序性能 基本思想 将待排序的数组不断二分&#xff0c;直到每个子数组只包含一个元素或为空。然后通过合并操作将这些子数组逐步合并成较大的有序数组&#xff0c;最终得到完全有序的结果&#xff1a; 下面是递归版本的归并排序…

Linux(4)-LAMP

L-LinuxA-apache/nginxM-mysqlp-php 搭建LAMP以及使用discuz搭建论坛网站 安装apache yum install httpd -y // 安装service httpd start // 启动Apache通过netstat -tunlp查看apache运行的端口&#xff0c;然后打开虚拟机ip 80端口能看到以下页面 或者 安装Mysql centOS6…

Python Pandas 重复值处理(第12讲)

Python Pandas 重复值处理(第12讲)         🍹博主 侯小啾 感谢您的支持与信赖。☀️ 🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔…

基于JAVA的农村物流配送系统 开源项目

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统登录、注册界面2.2 系统功能2.2.1 快递信息管理&#xff1a;2.2.2 位置信息管理&#xff1a;2.2.3 配送人员分配&#xff1a;2.2.4 路线规划&#xff1a;2.2.5 个人中心&#xff1a;2.2.6 退换快递处理&#xff1a;…

yolov5障碍物识别-雪糕筒识别(代码+教程)

简介 这是一个检测交通锥并识别颜色的项目。我使用 yolov5 来训练和检测视锥细胞。此外&#xff0c;我使用 k 均值来确定主色&#xff0c;以对锥体颜色进行分类。目前&#xff0c;支持的颜色为红色、黄色、绿色和蓝色。其他颜色被归类为未知。 数据集和注释 我使用了一个自收…

SpringCloudAliBaba篇之Seata:分布式事务组件理论与实践

1、事务简介 事务(Transaction)是访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。在关系数据库中&#xff0c;一个事务由一组SQL语句组成&#xff0c;事务具有4个属性&#xff1a;原子性、一致性、隔离性、持久性。这四个属性通常称为ACID原则。 原子性(atomici…

开源学习项目推荐

文章目录 koodo-reader凤凰架构学习项目NPS 内网穿透客户端 koodo-reader 项目地址&#xff1a;https://github.com/koodo-reader/koodo-reader 介绍&#xff1a;一个开源的阅读器&#xff0c;阅读pdf也有目录&#xff0c;作为epub阅读器和pdf阅读器看资料挺好 凤凰架构 项…

前端视角看 Docker :在国内的基础配置教程(含国内镜像源)

引言 在国内使用Docker时&#xff0c;直接从Docker Hub拉取镜像可能会遇到网络速度慢的问题。配置国内的镜像加速器可以显著提升拉取速度。本教程将指导您完成安装Docker后的基础配置&#xff0c;特别是设置国内镜像加速器。 1. 安装Docker 确保您已在系统上安装Docker。根…
最新文章