前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >指针和数组笔试题及解析

指针和数组笔试题及解析

作者头像
AUGENSTERN_
发布2024-04-09 20:32:49
790
发布2024-04-09 20:32:49
举报
文章被收录于专栏:畅游IT畅游IT

在写笔试题之前,我们要先了解两个特殊情况:

代码语言:javascript
复制
int a1 = sizeof(arr);
int* a2 = &arr;

一个是sizeof(数组名),另一个是取地址数组名

sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小

&数组名,这里的数组名表示整个数组,取出的是整个数组的地址

除此之外所有的数组名都表示首元素的地址

还需要注意的是,指针的大小由于系统的地址总线不同而有所不同,当我们使用的是x86环境时,指针的大小为4个字节,若使用的时x64环境,则指针的大小为8;


(下述所有指针面试题都在x64环境下进行)


接下来我们看我们的笔试题:

1. 一维数组笔试题

代码语言:javascript
复制
int a[] = {1,2,3,4};
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(a+0));
printf("%d\n",sizeof(*a));
printf("%d\n",sizeof(a+1));
printf("%d\n",sizeof(a[1]));
printf("%d\n",sizeof(&a));
printf("%d\n",sizeof(*&a));
printf("%d\n",sizeof(&a+1));
printf("%d\n",sizeof(&a[0]));
printf("%d\n",sizeof(&a[0]+1));

解析

1. sizeof(a)中的a指代的是整个数组的大小,而数组中有四个元素,且int的大小为四个字节,故输出答案应该为16;

2. 第二个和第一个又有所不同,看似和sizeof(数组名)很类似,实际上并不符合该条件,因为括号内并不是纯粹的数组名,反而多出一个+0,这也是大多数人会困惑的地方,故a+0指代的是数组首元素地址,所以输出应该为8;

3. *a的意思是a指针所指向的元素,而a代表的是数组首元素地址,故*a指的是a数组的第一个元素,即a[0],故输出答案为4;

4. a指的是数组首元素地址,首元素地址+1为第二个元素的地址,地址的大小为8个字节,故输出结果为8;

5. a[1]指的是第一个元素,数组类型为整形,元素大小为4个字节,故输出结果为4;

6. &a指的是整个数组,但依然是一个地址,故输出结果为8;

7.&a指的是整个数组,是一个指针,指针解引用后就指向了整个数组,整个数组的大小为16;故输出结果为16;

8.&a指向的是整个数组的地址,&a+1则指向的是a数组之后的地址,既然是指针,那么输出的结果就为8;

9.a[0]指的是a数组的第1个元素,而&a[0]则是指向a[0]的指针,指针的大小为8,故输出结果为8;

10.&a[0]+1指的就是a[1]的地址,既然是指针,那么他的大小就为8,故输出结果为8;

运行结果

2. 字符数组笔试题

由于字符数组的题目较多,我们分六个部分讲解:

题组1:

代码语言:javascript
复制
char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(arr+0));
printf("%d\n", sizeof(*arr));
printf("%d\n", sizeof(arr[1]));
printf("%d\n", sizeof(&arr));
printf("%d\n", sizeof(&arr+1));
printf("%d\n", sizeof(&arr[0]+1));
解析:

1. sizeof(arr)指的是整个数组的大小,而char类型的大小为1个字节,arr数组一共有6个元素,故输出结果为6;

2. sizeof(arr)指的是整个数组,但是sizeof(arr+0)指的仅仅只是数组首元素的地址的大小,并不满足sizeof(数组名)的格式,故输出结果为8;

3. arr是首元素的地址,那么*arr就是arr数组的第1个元素的值,故输出结果为1;

4. arr[1]是数组的第二个元素,故输出结果为1;

5. &arr是arr整个数组的地址,是一个指针,故输出结果为8;

6. 7. 同理,都是指针,输出结果都为8,但区别在于6和7指向的空间不同;6指向的是数组后6个字节的内存空间,而7指向的是数组第二个元素的内存空间;

运行结果:

题组2:

代码语言:javascript
复制
char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", strlen(arr));
printf("%d\n", strlen(arr+0));
printf("%d\n", strlen(&arr));
printf("%d\n", strlen(&arr+1));
printf("%d\n", strlen(&arr[0]+1));
//printf("%d\n", strlen(*arr));
//printf("%d\n", strlen(arr[1]));
解析:

根据cplusplus官网上对strlen的解释

可知,strlen函数的参数应该为一个字符指针; 故题中最后两行代码会运行异常!!! 并且,strlen函数是一个求字符串的长度的函数,'\0'是字符串结束的标志,strlen函数只有遇到了'\0'字符才会停止,综合以上,我们继续来完成笔试题;

1. 由于arr数组的元素个数为6,且没有'\0',故strlen函数读取完整个数组之后,会继续向后读取,直到后面某个地址中存放着'\0'为止,故输出的结果是一个随机值,在我的编译器上运行的结果就是42,而为什么会使42呢?我们接下来继续探讨:

图中0x0000001C6BFF914是数组的首元素地址,内存中存放的是十六进制的数据,十六进制的61就是十进制的97,也就是字符'a',由该内存监视图我们不难看出从61往后数(包括61)42个字节,到第43个字节时,内存中存放的值就是00;也就是我们说的'\0',故当strlen函数读取到00时,就停止运行了,所以我们输出的结果就是42;

2. 和第一个没有任何区别,输出的结果同样也是42;

3. &arr指的是整个数组的地址,但是该指针内存放的依旧是数组首元素的地址;故输出结果为42;

4. &arr + 1指向的是arr数组之后的一块内存空间,也就是说该指针跳过了arr数组,指向数组最后一个元素的后一个地址,所以输出结果为42-6 = 36;

5. &arr[0] + 1指向的是arr数组的第二个元素,故输出结果为42 - 1 = 41;

运行结果:

题组3:

代码语言:javascript
复制
char arr[] = "abcdef";
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(arr+0));
printf("%d\n", sizeof(*arr));
printf("%d\n", sizeof(arr[1]));
printf("%d\n", sizeof(&arr));
printf("%d\n", sizeof(&arr+1));
printf("%d\n", sizeof(&arr[0]+1));
解析:

1. arr数组中存放的是字符串,但字符串是以'\0'结尾的,故arr数组中存放的内容为abcdef和\0,一共七个元素,故输出结果为7;

2. sizeof(arr+0)不符合sizeof(数组名)的格式,故arr指的是数组首元素的地址,故输出的结果为8;

3. *arr指的是arr数组的第一个元素,如输出结果为1;

4. arr[1]指的是arr数组的第二个元素,故输出结果为1;

5 6 7. sizeof中的参数都是地址,故输出结果都为8;

运行结果:

题组4:

代码语言:javascript
复制
char arr[] = "abcdef";
printf("%d\n", strlen(arr));
printf("%d\n", strlen(arr+0));
printf("%d\n", strlen(&arr));
printf("%d\n", strlen(&arr+1));
printf("%d\n", strlen(&arr[0]+1));
//printf("%d\n", strlen(*arr));
//printf("%d\n", strlen(arr[1]));
解析:

strlen函数的参数是字符指针,故最后两行会报错;

1. strlen函数遇到'\0'则停止,而字符串以'\0'结尾,故输出的结果为6;

2. 和第一种没有区别;

3. &arr指的是arr整个数组的地址,但其内存放的依然是arr数组的首元素地址,故输出结果为6;

4. &arr+1指的是arr数组最后一个元素之后的一块7个字节大小的内存空间,这块空间内存放的是随机值,故strlen输出的结果也为随机值;

5. &arr[0] + 1指的是arr数组的第二个元素,也就是arr[1];故输出的结果为6;

运行结果:

题组5:

代码语言:javascript
复制
char *p = "abcdef";
printf("%d\n", sizeof(p));
printf("%d\n", sizeof(p+1));
printf("%d\n", sizeof(*p));
printf("%d\n", sizeof(p[0]));
printf("%d\n", sizeof(&p));
printf("%d\n", sizeof(&p+1));
printf("%d\n", sizeof(&p[0]+1));
解析:

p是指针,其类型为char*,存放的是字符串的首字符的地址;

1. 指针的大小为8,故输出的结果为8;

2. p+1同样是一个指针,但指向的是第二个字符的地址,故输出结果为8;

3. *p指的是字符串首字符,故输出结果为1;

4. p[0]同样是指字符串首元素字符,故输出结果也为1;

5. p是一个指针,&p同样也是一个指针,不过是一个二级指针,故输出结果为8;

6. 同5,是一个指针,但指向的地址是p字符串之后的一块内存空间,故输出结果为8;

7. 同5,是一个指针,指向的是字符串第二个字符的地址,故输出结果为8;

运行结果:

题组6:

代码语言:javascript
复制
char* p = "abcdef";
printf("%d\n", strlen(p));
printf("%d\n", strlen(p+1));
printf("%d\n", strlen(&p));
printf("%d\n", strlen(&p+1));
printf("%d\n", strlen(&p[0]+1));
//printf("%d\n", strlen(*p));
//printf("%d\n", strlen(p[0]));
解析:

由于strlen的参数要为字符指针,故最后两行代码会运行异常;

1. p指向的是字符串的首字符,而字符串默认以'\0'结尾,故输出结果为6;

2. p+1指向的是字符串第二个元素,故输出结果为5;

3. &p指向的是整个字符串,但存放的依然是字符串首元素的地址,故输出结果为6;

4. &p+1指向的是字符串之后的一块空间,'\0'的位置是随机的,故输出结果是一个随机值;

5. p[0]是字符串首元素,取地址后+1指向的是字符串的第二个元素,故输出结果为5;

运行结果:

3. 二维数组笔试题

题组1:

代码语言:javascript
复制
int a[3][4] = {0};
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(a[0][0]));
printf("%d\n",sizeof(a[0]));
printf("%d\n",sizeof(a[0]+1));
解析:

在写二维数组笔试题之前,我们需要了解二维数组的本质,实际上二维数组就是一个存放一维数组的数组,像题中的a[3][4]中的[4]实际上指的就是二维数组中存放的一维数组的元素个数,而[3]指的就是二维数组中存放了几个一维数组,所以在二维数组中,a是一个二维数组数组名,同时a[0]也是数组名,不过是一维数组的数组名,有了以上知识,我们继续解题;

1. sizeof (数组名)指的是整个数组的大小,数组一共有12个整形元素,故输出结果为48;

2. a[0][0]指的是二维数组的第一行第一个元素,故输出结果为4;

3. a[0]指的是二维数组的第一行,符合sizeof(数组名)格式,一共四个元素,故大小为16;

4. sizeof(a[0] + 1)不符合sizeof (数组名)格式,故a[0]指的是第一行的首元素地址,+1则是第一行的第二个元素的地址,故输出结果为8;

运行结果:

题组2:

代码语言:javascript
复制
int a[3][4] = {0};
printf("%d\n",sizeof(*(a[0]+1)));
printf("%d\n",sizeof(a+1));
printf("%d\n",sizeof(*(a+1)));
printf("%d\n",sizeof(&a[0]+1));
printf("%d\n",sizeof(*(&a[0]+1)));
printf("%d\n",sizeof(*a));
解析:

在写这个题之前,我们需要像大家介绍一个新的知识点:

我们平常使用数组的时候,最常见的写法就是:

代码语言:javascript
复制
int arr = { 1, 2, 3, 4 };
arr[1] = 3;

但实际上,他还可以换成另外两种写法,和以上写法是相同的效果:

代码语言:javascript
复制
int arr = { 1, 2, 3, 4 };
int* p = arr;
p[1] = 3;
*(p + 1) = 3;

其实,我们平常所写的arr[1] = 1;在运行时都会将其转换成指针的写法,故以上三种写法都是可以运行的;

示例图:

综合以上知识点,我们继续开始答题:

1. a[0]指的是a数组第一行的第一个元素的地址,+1即第二个元素的地址,解引用就是a数组第一行的第二个元素,故输出结果为4;

2. a是二维数组首元素,也就是a[0],a[0]指的是第一行首元素的地址,a + 1也就是第二行的地址,故输出结果为8;

3. a + 1是第二行的地址,*(a + 1)其实也就等价于a[1],a[1]又是数组a第一个元素一维数组的数组名,这样就符合sizeof(数组名)的结构,故a[1]指代的就是整个第一行的大小,故输出结果为16;

4. &a[0]是数组a第一行的地址,+1后依然是一个地址,也就是指向第二行的一个指针,故输出结果为8;(实践上&a[0] +1可以写成 &*(a + 0) + 1,也就是a+1,和第二种无区别)

5. &a[0]+1是整个第二行的地址,解引用后代表整个第二行,也就是a[1],故输出结果为16;

6. *a也就是*(a+0),也就是 a[0],故输出结果为16;

运行结果:

4.指针笔试题

题目1:

代码语言:javascript
复制
void main()
{
	int a[5] = { 1, 2, 3, 4, 5 };
	int* ptr = (int*)(&a + 1);
	printf("%d,%d", *(a + 1), *(ptr - 1));
	//程序的结果是什么?
}
解析:

&a指的是整个数组的大小,而&a+1是整个数组之后的大小和数组a一样大的一块内存空间,而&a内存放的是数组首元素的地址,而ptr存放的是数组之后的第一块内存空间的地址,故*(a+1) = a[1],输出结果为2,*(ptr-1) = a[5],输出结果为5;

运行结果:

题目2:

代码语言:javascript
复制
#include <stdio.h>
int main()
{
 int a[3][2] = { (0, 1), (2, 3), (4, 5) };
 int *p;
 p = a[0];
 printf( "%d", p[0]);
 return 0;
}
解析:

在解题之前,需要给大家介绍一个新的表达式:逗号表达式 逗号表达式很少被使用,具体使用方法就是,若一个括号内有多个值被逗号隔开,我们认为最后一个值是我们所使用的值;

我们在初始化二维数组时,若想将每一行的值进行初始化,一般是使用花括号即'{}',而并不是括号,故该二维数组并不是被完全初始化了,而是非完全初始化,初始化的值是1,3,5;故p[0]所代表的值就是a[0][0]的值,故输出结果为1;

运行结果:

题目3:

代码语言:javascript
复制
int main()
{
 int a[5][5];
 int(*p)[4];
 p = a;
 printf( "%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
 return 0;
}
解析:

我们知道p[4][2]可以写成*(*(p + 4) + 2),那么&p[4][2]就可以写成*(p + 4) + 2,同理&a[4][2]也可以写成*(a + 4) + 2,而p是一个数组指针,指向的数组有四个元素,而p却指向了a的首元素地址,故p只能保存a数组每行的四个元素,而数组a是二维数组,一共五行五列,故p[4][2]之前实际上只有18个元素,而a[4][2]之前则有22个元素(实际上a[4][2] = p[5][2]),又因为指针 - 指针的结果是两个地址之间的元素个数,正负由地址的大小决定,故&p[4][2] - &a[4][2]的结果为-4,而-4转换成十六进制就是FFFFFFFFFFFFFFFC,故其输出结果为如下:

运行结果:

题目4:

代码语言:javascript
复制
int main()
{
 int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
 int *ptr1 = (int *)(&aa + 1);
 int *ptr2 = (int *)(*(aa + 1));
 printf( "%d,%d", *(ptr1 - 1), *(ptr2 - 1));
 return 0;
}
解析:

&aa + 1指的是aa数组后和aa大小相同的一块内存空间,故*(ptr1 - 1) = aa[1][4],结果为10;

*(aa+1) = aa[1],指向第二行的首元素,故*(ptr2 - 1) = aa[0][4],结果为5;

运行结果:

题目5:

代码语言:javascript
复制
int main()
{
 char *a[] = {"work","at","alibaba"};
 char**pa = a;
 pa++;
 printf("%s\n", *pa);
 return 0;
}
解析:

数组a是一个字符指针数组,存放的是每个字符串首元素的地址,故a数组第一个元素存放的是w的地址,第二个元素存放的是a的地址,pa是数组名,存放的是首元素的地址,故pa++为第二个元素的地址,故输出结果为at;

运行结果:

题目6:

(这个题目大概是整篇文章中最难的了,有讲解的不到位的地方希望大家多多包涵,也欢迎大家在评论区中和我讨论)

代码语言:javascript
复制
int main()
{
 char *c[] = {"ENTER","NEW","POINT","FIRST"};
 char**cp[] = {c+3,c+2,c+1,c};
 char***cpp = cp;
 printf("%s\n", **++cpp);
 printf("%s\n", *--*++cpp+3);
 printf("%s\n", *cpp[-2]+3);
 printf("%s\n", cpp[-1][-1]+1);
 return 0;
}
解析:

数组c的类型为char*(一级指针),存放的是字符串的首元素地址,cp数组存放的是数组c中元素的地址,类型为char**(二级指针),而cpp存放的是cp首元素的地址,类型是char***(三级指针),做题之前我们还要分析操作符的优先级和结合性,[]的优先级最大,*和前置++-- 的优先级一样,结合性是从左到右,优先级最小的是+-双目操作符;

1. 现在来分析**++cpp,cpp先++,指向由cp的第一个元素变成了第二个元素,也就是c+2,第一次解引用后指向了c数组的第三个元素,再次解引用后指向了POINT字符串的首元素,故第一个打印输出结果为POINT;

2. 由于第一个打印中,cpp指向了cp数组的第二个元素,故++cpp指向了cp数组的第三个元素,解引用后指向了c数组的第二个元素,再--则指向了c数组的第一个元素,再次解引用后指向了ENTER的首元素的地址,首元素地址+3,则指向了ENTER字符串的第四个元素,也就是E,故打印输出结果为ER;

3. 由于第二次打印中,cpp指向了cp数组的第三个元素,故cpp[-2]即*(cpp - 2)指向了cp数组的第四个元素,再解引用,则指向了FIRST字符串的首元素,再+3则指向了字符串的第四个元素,即S,故输出结果为ST;

4. 此时cpp指向的还是cp数组的第三个元素,cpp[-1][-1]即*(*(cpp - 1) - 1),cpp - 1则cpp指向了cp数组的第二个元素,解引用后指向了c数组的第三个元素,再-1后指向了c数组的第二个元素,再解引用则指向了字符串NEW的首元素地址,再次+1,则指向了字符串的第二个元素,即E,故打印结果为EW;

运行结果:

以上就是本期全部内容,爆肝8000多字,希望大家能够支持,同时也希望大家能在我的分享中学习到更多的知识,如果我在文章中某些地方表达不清晰或者有所错误,希望大家能够指出来,有所疑惑的地方也欢迎大家在评论区讨论,我会及时回复的,

感谢观看!!!

本文参与?腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2024-03-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客?前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与?腾讯云自媒体分享计划? ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 一维数组笔试题
    • 解析
      • 运行结果
      • 2. 字符数组笔试题
        • 题组1:
          • 解析:
          • 运行结果:
        • 题组2:
          • 解析:
          • 运行结果:
        • 题组3:
          • 解析:
          • 运行结果:
        • 题组4:
          • 解析:
          • 运行结果:
        • 题组5:
          • 解析:
          • 运行结果:
        • 题组6:
          • 解析:
          • 运行结果:
      • 3. 二维数组笔试题
        • 题组1:
          • 解析:
          • 运行结果:
        • 题组2:
          • 解析:
          • 运行结果:
      • 4.指针笔试题
        • 题目1:
          • 解析:
          • 运行结果:
        • 题目2:
          • 解析:
          • 运行结果:
        • 题目3:
          • 解析:
          • 运行结果:
        • 题目4:
          • 解析:
          • 运行结果:
        • 题目5:
          • 解析:
          • 运行结果:
        • 题目6:
          • 解析:
          • 运行结果:
      • 感谢观看!!!
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
      http://www.vxiaotou.com