指针和数组笔试题及解析

🎉欢迎大家观看AUGENSTERN_dc的文章(o゜▽゜)o☆✨✨

🎉感谢各位读者在百忙之中抽出时间来垂阅我的文章,我会尽我所能向的大家分享我的知识和经验📖

🎉希望我们在一篇篇的文章中能够共同进步!!!

🌈个人主页:AUGENSTERN_dc

🔥个人专栏:C语言 | Java | 数据结构

⭐个人格言:

一重山有一重山的错落,我有我的平仄

一笔锋有一笔锋的着墨,我有我的舍得

目录

1. 一维数组笔试题

解析

运行结果

2. 字符数组笔试题

题组1:

解析:

运行结果: 

题组2:

解析:

运行结果:

​编辑

题组3:

解析:

运行结果:

题组4:

解析:

运行结果:

题组5:

解析:

运行结果:

题组6:

解析:

运行结果:

3. 二维数组笔试题

题组1:

解析:

运行结果:

题组2:

解析:

运行结果:

4.指针笔试题

题目1:

解析:

运行结果:

题目2:

解析:

运行结果:

题目3:

解析:

运行结果:

题目4:

解析:

运行结果:

题目5:

解析:

运行结果:

题目6:

解析:

运行结果:

感谢观看!!!


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

int a1 = sizeof(arr);
int* a2 = &arr;

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

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

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

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

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


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


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

1. 一维数组笔试题

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:

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:

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:

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:

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:

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:

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:

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:

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));

解析:

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

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

int arr = { 1, 2, 3, 4 };
arr[1] = 3;

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

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:

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:

#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:

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:

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:

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:

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

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多字,希望大家能够支持,同时也希望大家能在我的分享中学习到更多的知识,如果我在文章中某些地方表达不清晰或者有所错误,希望大家能够指出来,有所疑惑的地方也欢迎大家在评论区讨论,我会及时回复的,

感谢观看!!!

  • 25
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
本火锅店点餐系统采用Java语言和Vue技术,框架采用SSM,搭配Mysql数据库,运行在Idea里,采用小程序模式。本火锅店点餐系统提供管理员、用户两种角色的服务。总的功能包括菜品的查询、菜品的购买、餐桌预定和订单管理。本系统可以帮助管理员更新菜品信息和管理订单信息,帮助用户实现在线的点餐方式,并可以实现餐桌预定。本系统采用成熟技术开发可以完成点餐管理的相关工作。 本系统的功能围绕用户、管理员两种权限设计。根据不同权限的不同需求设计出更符合用户要求的功能。本系统中管理员主要负责审核管理用户,发布分享新的菜品,审核用户的订餐信息和餐桌预定信息等,用户可以对需要的菜品进行购买、预定餐桌等。用户可以管理个人资料、查询菜品、在线点餐和预定餐桌、管理订单等,用户的个人资料是由管理员添加用户资料时产生,用户的订单内容由用户在购买菜品时产生,用户预定信息由用户在预定餐桌操作时产生。 本系统的功能设计为管理员、用户两部分。管理员为菜品管理、菜品分类管理、用户管理、订单管理等,用户的功能为查询菜品,在线点餐、预定餐桌、管理个人信息等。 管理员负责用户信息的删除和管理,用户的姓名和手机号都可以由管理员在此功能里看到。管理员可以对菜品的信息进行管理、审核。本功能可以实现菜品的定时更新和审核管理。本功能包括查询餐桌,也可以发布新的餐桌信息。管理员可以查询已预定的餐桌,并进行审核。管理员可以管理公告和系统的轮播图,可以安排活动。管理员可以对个人的资料进行修改和管理,管理员还可以在本功能里修改密码。管理员可以查询用户的订单,并完成菜品的安排。 当用户登录进系统后可以修改自己的资料,可以使自己信息的保持正确性。还可以修改密码。用户可以浏览所有的菜品,可以查看详细的菜品内容,也可以进行菜品的点餐。在本功能里用户可以进行点餐。用户可以浏览没有预定出去的餐桌,选择合适的餐桌可以进行预定。用户可以管理购物车里的菜品。用户可以管理自己的订单,在订单管理界面里也可以进行查询操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值