“全网最细“C语言指针笔试题详解

先来点指针与数组结合的题

第一题:

int main()
{
	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));
	return 0;
}

printf("%d\n", sizeof(a));这个语句的意思是sizeof中单独放数组名代表整个数组,一个int占4个字节,4个占16个字节
printf("%d\n", sizeof(a + 0));这个语句的意思是数组名没有单独放在sizeof内部则代表数组首元素地址+0后是第一个元素的地址 地址是4/8个字节
printf("%d\n", sizeof(*a));a代表首元素地址,解引用后是首元素1,,1的大小为4个字节
printf("%d\n", sizeof(a + 1));数组名没有单独放在sizeof内部则代表数组首元素地址+1后是第二个元素的地址 地址是4/8个字节
printf("%d\n", sizeof(a[1]));[]的作用与*(a+1)相同,找到元素2,元素2的大小为4个字节
printf("%d\n", sizeof(&a));&符号+数组名代表整个数组的地址,地址是4/8个字节
printf("%d\n", sizeof(*&a));整个数组的地址解引用代表整个数组,整个数组是16个字节
printf("%d\n", sizeof(&a + 1));整个数组的地址+1跳过一个数组,为下一个数组的地址,地址的大小为4/8字节
printf("%d\n", sizeof(&a[0]));a[0]代表第一个元素,加上&符号表示第一个元素的地址,地址为4/8字节
printf("%d\n", sizeof(&a[0] + 1));第一个元素的地址+1表示指向下一个元素的地址,也就是2的地址,大小为4/8

以下为代码运行结果:

d7caf57c87a4488b973b4206aa4221af.png

第二题:

#include <string.h>

int main()
{
	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));
	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));
	printf("%d\n", strlen(&arr+1));
	printf("%d\n", strlen(&arr[0]+1));
	return 0;
}

做这道题前首先要明白char arr[] = { 'a','b','c','d','e','f' };和char arr2[] = "abcdef";的区别,如下图所示:

f7d8e0ec88494996806dedb0acb8ed6b.png 

arr中末尾没有\0,arr2中末尾会有一个\0 ;

printf("%d\n", sizeof(arr));sizeof中单独放数组名代表整个数组,数组中有6个char类型字符,一个占1个字节,一共6字节
printf("%d\n", sizeof(arr+0));sizeof中没有单独放数组名代表首元素地址,+0后为a的地址,地址大小为4/8
printf("%d\n", sizeof(*arr));sizeof中没有单独放数组名代表首元素地址,解引用后代表字符a,大小为1
printf("%d\n", sizeof(arr[1]));arr[1]代表b字符,大小为1;
printf("%d\n", sizeof(&arr));&arr代表整个数组的地址,大小为4/8
printf("%d\n", sizeof(&arr+1));&arr代表整个数组的地址+1后跳过一个数组代表下一个数组的地址,大小为4/8
printf("%d\n", sizeof(&arr[0]+1));arr[0]为第一个元素,加上&符号后代表第一个元素的地址,+1后为第二个元素的地址,大小为4/8
printf("%d\n", strlen(arr));strlen函数是找有无\0,有\0大小就是\0之前的元素个数,没有\0就是随机值
arr代表数组首元素地址,strlen函数从a开始往后寻找\0,发现没有\0后返回随机值
printf("%d\n", strlen(arr+0));arr+0代表第一个元素地址,strlen根据地址找到元素a,往后寻找\0发现没有\0后返回随机值
这里的随机值是指在数组范围内没有找到\0,超出数组规定范围后有可能在任意地方找到\0所以为随机值
printf("%d\n", strlen(*arr));*arr代表第一个元素a,元素a的ascll值为97,返回数字97后编译器报错,因为97的地址不允许被访问
printf("%d\n", strlen(arr[1]));arr[1]代表元素b,元素b的ascll值为98,返回数字98后编译器报错,因为98的地址不允许被访问
printf("%d\n", strlen(&arr));&arr代表整个数组地址,strlen函数从a开始往后寻找\0,发现没有\0后返回随机值
printf("%d\n", strlen(&arr+1));&arr+1代表下一个数组的地址,下一个数组的地址往后什么时候找到\0并不知道,所以返回随机值
printf("%d\n", strlen(&arr[0]+1));&arr[0]+1代表第二个元素的地址,从第二个元素依次向后寻找\0没有\0返回随机值

第三题:

int main()
{
	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));

	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));
	printf("%d\n", strlen(&arr+1));
	printf("%d\n", strlen(&arr[0]+1));
	return 0;
}

printf("%d\n", sizeof(arr));sizeof中单独放数组名代表整个数组,除abcdef外还有\0占据一个字节所以一共7个字节
printf("%d\n", sizeof(arr + 0));arr+0代表首元素地址,地址大小为4/8
printf("%d\n", sizeof(*arr));*arr代表首元素a,大小为1
printf("%d\n", sizeof(arr[1]));arr[1]代表元素b,大小为1
printf("%d\n", sizeof(&arr));&arr代表整个数组的地址,大小为4/8
printf("%d\n", sizeof(&arr + 1));&arr+1代表下一个数组的地址,大小为4/8
printf("%d\n", sizeof(&arr[0] + 1));&arr[0]+1代表第二个元素b的地址,大小为4/8

printf("%d\n", strlen(arr));除了sizeof(arr)和&arr为整个数组其他数组名都为首元素地址,由a的地址找到a往后找\0,而f后就是\0,所以长度为\0之前的 长度为6
printf("%d\n", strlen(arr + 0));arr+0是首元素的地址长度为6
printf("%d\n", strlen(*arr));*arr为首元素a,返回a的ascll值为地址为97的空间,编译器会报错
printf("%d\n", strlen(arr[1]));arr[1]代表元素b,返回b的ascll值为地址为98的空间,编译器会报错
printf("%d\n", strlen(&arr));整个数组的地址也是从首元素开始,所以从a开始向后寻找\0长度为6
printf("%d\n", strlen(&arr + 1));&arr+1为下一个数组的地址,下一个数组不知道什么时候有\0所以为随机值
printf("%d\n", strlen(&arr[0] + 1));&arr[0]+1为数组b的地址,长度为5

第四题:

int main()
{
	const 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));

	printf("%d\n", strlen(p));
	printf("%d\n", strlen(p + 1));
	printf("%d\n", strlen(*p));
	printf("%d\n", strlen(p[0]));
	printf("%d\n", strlen(&p));
	printf("%d\n", strlen(&p + 1));
	printf("%d\n", strlen(&p[0] + 1));
	return 0;
}

printf("%d\n", sizeof(p));p指针存放字符串首元素a的地址,通过a的地址可找到整个字符串p是指针,指针的大小为4/8
printf("%d\n", sizeof(p + 1));指针+1向后访问下一个同类型元素地址,大小为4/8
printf("%d\n", sizeof(*p));p指针存放字符串首元素a的地址,*p为首元素a,大小为1
printf("%d\n", sizeof(p[0]));p[0]和*(p+0)相等为第一个元素 大小为1
printf("%d\n", sizeof(&p));&p为二级指针,存放的是a的地址,大小为4/8
printf("%d\n", sizeof(&p + 1));&p+1也是二级指针,二级指针在内存中也有自己的空间,p+1跳过一个二级指针大小找到&p的后一块地址这个地址并没有指向哪个位置,只有&p指向的是存放a地址的空间,只要是地址大小都为4/8
printf("%d\n", sizeof(&p[0] + 1));p[0]为字符a,&p[0]为a的地址,+1后跳过一个char类型的地址为b元素的地址,大小为4/8

printf("%d\n", strlen(p));p中存放a的地址strlen找到字符a后向后寻找\0,\0在f后面所以长度为6
printf("%d\n", strlen(p + 1));p+1为b的地址,长度为5
printf("%d\n", strlen(*p));*p为字符a,返回a的ascll值为97,编译器报错
printf("%d\n", strlen(p[0]));p[0]为字符a,返回a的ascll值为97,编译器报错
printf("%d\n", strlen(&p));&p为存放a的地址的二级指针,二级指针在内存有自己的空间,空间里放的是地址如0x12345678,向后寻找\0并不知道\0在哪里所以是随机值
printf("%d\n", strlen(&p + 1));&p为存放a的地址的二级指针,二级指针+1为指向下一个二级指针的地址,向后寻找\0并不知道什么时候有\0所以是随机值
printf("%d\n", strlen(&p[0] + 1));p[0]为字符a,&p[0]为a的地址,+1后为b的地址,长度为5

第五题:

int main()
{
	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));
	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));
	printf("%d\n", sizeof(a[3]));
	return 0;
}

printf("%d\n", sizeof(a));a单独放在sizeof中代表整个数组,整个数组大小为3*4*4==48
printf("%d\n", sizeof(a[0][0]));a[0][0]代表数组第一个元素 大小为4
printf("%d\n", sizeof(a[0]));a[0]是第一行,第一行是一个一维数组,a[0]是数组名代表整个数组大小为4*4==16
printf("%d\n", sizeof(a[0] + 1));a[0]并没有单独放在sizeof内部所以a[0]为第一行首元素地址,+1后代表第一行第二个元素地址大小4/8
printf("%d\n", sizeof(*(a[0] + 1)));a[0]并没有单独放在sizeof内部所以a[0]为第一行首元素地址,+1后代表第一行第二个元素地址,解引用后为第一行第二个元素,大小为4
printf("%d\n", sizeof(a + 1));a没有单独放在sizeof内部,a代表数组首元素地址,数组首元素为第一行,第一行的地址+1跳过一行指向第二行的地址,地址大小为4/8
printf("%d\n", sizeof(*(a + 1)));第二行解引用后为第二行所有元素,大小为16
printf("%d\n", sizeof(&a[0] + 1));&a[0]代表第一行整个数组的地址,+1后跳到下一个数组也就是第二行数组的地址,地址的大小为4/8
printf("%d\n", sizeof* ((&a[0] + 1)));对第二行整个数组解引用后大小为16
printf("%d\n", sizeof(*a));a代表第一行的地址,解引用后为第一行所有元素,大小为16
printf("%d\n", sizeof(a[3]));a[3]为第四行所有元素,大小为16,即使数组只有三行,但sizeof关键字只是预判括号内的大小并不会进行实际运算

第六题:

int main()
{
	int a[5] = { 1,2,3,4,5 };
	int* ptr = (int*)(&a + 1);
	printf("%d,%d", *(a + 1), *(ptr - 1));
	return 0;
}

int* ptr = (int*)(&a + 1); &a代表整个数组+1后指向下一个数组,将其强制转化为int*类型则每次跳过一个(int*)大小的地址

*(a + 1):a为首元素地址也就是1的地址+1后为2的地址,解引用后就是2

*(ptr - 1)):ptr指向下一个数组起始位置,-1后指向a数组的元素5的地址,解引用后为元素5,以下为示意图:

22469d89ecab4b279b1311e2e4f4230b.png

第七题:

struct Test
{
	int num;
	char* pcname;
	short sdate;
	char a[2];
	short sa[4];
}*p;

int main()
{
	printf("%p\n", p + 0x1);
	printf("%p\n", (unsigned long)p+0x1);
	printf("%p\n", (unsigned int*)p + 0x1);
	return 0;
}

 做这题前我们先计算结构体大小:

a5df057048ff44a5bcc5fcb3f8ef66d0.png

图中由于位置不够18下面少画了一个空间格子,图中已标出最后浪费2个空间凑够20个空间为最大对齐数4的倍数,所以结构体大小为20. 

printf("%p\n", p + 0x1);p是一个结构体指针,0x1就是1,指针+1就是跳过同指针类型的大小,指针类型为结构体大小为20,,地址变为0x100014

printf("%p\n", (unsigned long)p+0x1);将p强制转化为无符号整形,这时候p+1就是跳过一个整形1,地址变为0x100001

printf("%p\n", (unsigned int*)p + 0x1);将p强制转化为int*指针类型,p+1跳过一个指针类型也就是地址加4或8,所以地址为0x100004

第八题:

int main()
{
	int a[4] = { 1,2,3,4 };
	int* ptr1 = (int*)(&a + 1);
	int* ptr2 = (int*)((int)a + 1);
	printf("%x,%x", ptr1[-1], *ptr2);
	return 0;
}

int* ptr1 = (int*)(&a + 1);&a+1指针指向下一个数组起始位置,将其强制转化为int*后相当于每次+1-1跳过一个int*类型大小的空间

int* ptr2 = (int*)((int)a + 1);a为1的地址将其强制转化为int类型后+1只跳过1个字节,然后将其强制转化为(int*)表示大小取int*类型的大小

如图所示:

03e31301579d42e3b0b4de8188789b64.png

ptr1[-1]为*(ptr1-1) 也就是指向4的地址解引用为元素4

*ptr2指向00字节位置,此位置也有可能为02这要看编译器是大端存放还是小端存放但都不影响结果,往后拿int*类型大小的空间就是02000000,所以地址为2000000

第九题:

int main()
{
	int a[3][2] = { (0,1),(2,3),(4,5) };
	int* p;
	p = a[0];
	printf("%d", p[0]);
	return 0;
}

首先我们可以看到数组初始化的时候用到了逗号表达式,只有逗号右边才是最后结果,所以初始化结果应该为 int a[3][2] = {1,3,5};

2beef54483ee4b62aee56234897ee0ea.png

a[0]代表数组第一行第一个元素的地址,将地址给p后p指向1的地址,所以p[0]为1

第十题:

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

int(*p)[4]; p是一个数组指针,数组指针里每个指针只能放4个元素

ec6732d48df94e47a923b1a2694da60c.png

两个指针相减为指针之间的元素个数,由图可知两个指针之间有4个元素,而用地址小的指针减去地址大的指针为负数所以是-4,打印地址的时候要将-4的补码进行打印,-4的补码为11111111111111111111111111111100转化为16进制为FF FF FF FC 

第十一题:

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

int* ptr1 = (int*)(&aa + 1);&aa为整个数组+1后指向下一个数组起始位置

int* ptr2 = (int*)(*(aa + 1));aa为第一行的地址,aa+1为第二行的地址,指针指向第二行起始位置

41f6305567614c9ab047c4cb7d9e1a3a.png

如图所示ptr1-1为10的地址解引用后为10,ptr2-1为第一行最后一个元素5的地址,解引用后为元素5 所以答案为10 5。

第十二题:

int main()
{
	const char* a[] = { "work","at","alibaba" };
	const char** pa = a;
	pa++;
	printf("%s\n", *pa);
	return 0;
}

const char* a[] = { "work","at","alibaba" }; a数组为char*类型,存放三个字符串。

const char** pa = a;将a的地址给二级指针pa存储

650bbb4b16734fb592f9973b39d87d6e.png

如图所示pa与a一样都指向第一个字符串work中w的地址, a中每个字符串都是char*类型,需要用二级指针接收,pa++后指针指向at中首元素a的地址然后解引用%s形式打印出at

第十三题:

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

char* c[] = { "ENTER","NEW","POINT","FIRST" };c数组有4个char*类型字符串

char** cp[] = { c + 3,c + 2,c + 1,c };二级指针数组cp中c+3存放FIRST的地址,c+2中存放POINT的地址,c+1中存放NEW的地址,c中存放ENTER的地址。

char*** cpp = cp;三级指针cpp存放二级指针数组中首元素地址也就是指向c+。

 printf("%s\n", **++cpp);cpp指针先自加,自加后指针指向c+2,然后解引用拿到C+2,而c+2中存放的C[2]的地址,c[2]的地址解引用拿到了字符串POINT中p的地址,由P的地址就可以打印整个字符串POINT   如图所示:(图中由cpp指向c+3的线应该删去,原因是cpp的指向发生改变)

95144c324b9c46bcb77f76c5ba007dda.png

printf("%s\n", *--*++cpp+3);cpp指针先自加指向 c+1,然后解引用拿到c+1的内容,而c+1中存放的是c[1]的地址,然后指针自减指向c[0],c[0]解引用后拿到ENTER中E的地址,然后+3指向ER中E的地址,由E的地址打印出ER  如图所示:

75753409b9684925b04a3fc3860707ea.png

printf("%s\n", *cpp[-2] + 3);cpp[-2]与*(cpp-2 )相同,cpp减2指向c+3然后解引用拿到c+3的内容,而c+3的内容是c[3]的地址,然后解引用拿到了c[3]中存放的FIRST字符串中首元素F的地址,+3后为ST中S的地址,通过S的地址打印出ST。(注意这里的cpp指针并没有自加自减或者赋值所以cpp原来的指向不改变还是指向c+1) 如图所示:

677bbb3d41e74cbb851e4de61aca7e89.png

printf("%s\n", cpp[-1][-1] + 1);cpp-1指向c+2,然后解引用找到其内容,c+2的内容是c[2]的地址,然后-1指向c[1]的地址然后解引用找到c[1]的内容也就是字符串NEW中N的地址,+1后指向EW中E的地址,通过E的地址打印出EW。如图所示:

6b6939d81b2542b1a2b54b3a83004724.png 

 同样,cpp指针并没有自加自减或者赋值所以cpp原来的指向不改变还是指向c+1。以上就是C语言指针的笔试题讲解,在这里总结一下:

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

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

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

4.p[i]和*(p+i)一样。  p[i][j]和*(*(p+i)+j)一样

5.指针+1-1跳过的是一个指针类型,例 int*p  p+1后指针往后跳过一个整形指向下一个整形

 

 

 

  • 15
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一朵猫猫菇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值