C语言之指针笔试题解析(下)

114 阅读6分钟

指针对于不少人来说:是多么一个难的代名词呀!!当然这个里面也包括笔者!所以……笔者对于指针满满的后怕!但是也有着更多的期待!毕竟阴影是用来打破的!后怕打破了,也就是龙腾虎跃的时候了!!下面进入正题:

笔者将用代码+解析的方式来带领大家走进:指针!! 第一题!

#include <stdio.h>
int main()
{
	int arr[3][2] = { (0,1),(2,3),(4,5) };
	int* p;
	p = arr[0];
	printf("%d\n", p[0]);   //1
	return 0;
}

注意,笔者的代码是在vs2022 的x86环境下进行演示的!请注意细节,若是在vs2022的x64环境下,出现不一样或者一样的结果!但结果都是正确的!(笔者亲测,在vs2022中x64的环境下,笔者讲解的部分指针笔试题解析,有着不一样的结果!但都是正确的!!)

c09661e6f8ea4a6d844abffe8a5910c6.png 若是有老铁能直接看懂,并且能够跟答案对上号,那么这篇博客,这位老铁就没有看的必要了,下面内容主要是对上面的代码的讲解!并不做其他的内容!但若是有兴趣,可以进一步欣赏! 下面进入讲解部分:

1.首先对于数组:int arr[3][2] = { (0,1),(2,3),(4,5) }; 笔者在这里面,小小的考验一下读者:对于前面所讲述的逗号表达式,是否已经忘记??在这儿强调一下!!所以上述数组初始化后实际初始化的内容为:1,3,5,因此整个数组初始化后内容为:int arr[3][2] = { 1,3,5,0,0,0}!笔者在刚刚接触到这个数组时候,也是巧妙地被……栽坑里面了!!

2.对于数组: arr[3][2] = { 1,3,5,0,0,0}

a6c1d3b461e145ab9737c62720077986.png int *p :整型指针,arr[0] 为第一行的数组名,arr[0]没有单独放在sizeof内部,也没有进行&arr[0], 所以,数组名表示首元素的地址,即元素1的地址!p=arr[0],指的意思为:p=&arr[0][0],所以指向元素1, 对于: p[0]指: *(p+0)—》*p ;指向第一个元素1,即打印结果为1!!

代码的运行结果为:

1d22848e57544bdab98d11ebcc19a9ae.png 第二题:

#include <stdio.h>
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]);  //FFFFFFFC  -4
	return 0;
}

对于这个题目,感觉很有难度!!若是读者能够自己看懂!已经很算是大佬级别人物!!下面请看笔者解析!

首先,笔者先进行简单提示一下:指针(地址)减去指针(地址)计算的是两个指针之间的元素个数!!

1.对于:int(p)[4] ;p是一个指针,它能指向一个数组,4个元素,每个元素都为int (整型)!p=a;将a赋值给p, a是数组名,表示数组首元素的地址!即为第一行的地址!a的类型为:int ()[5]是一个指针数组,而p 的类型为: int (*)[4]; 与a的类型不一样,会发生警告!!

774eb603337b405191ae6cdcc8deb524.png 2.int (*p) [4] : 指向4个元素,每个元素为int 类型!p+1, p+2, p+3,p+4,在图上分别标注出来:

7d96bd0b4aea4cc1a203b937249e6ba1.png p[4][2]相当于: *(*p+4)+2) ; 整型的数组指针:解引用的时候,有权利访问4个字节!

3.随着数组下标的增长,地址由低到高!

230819bdc11e4dc79d493056b1862245.png 对于:&p[4][2] - &a[4][2] 以%d打印时候:小地址减去大地址!(之间的元素个数为4个)所以结果为-4!!

&p[4][2] - &a[4][2] 以%p打印时候:

-4的原码为:1000000000000000000000000000000100

 反码为:  111111111111111111111111111111111111011

 补码为:  111111111111111111111111111111111111100

但是以%p的形式打印,并不讲究原码,反码,补码的问题!但是由于在内存中存放的是补码!所以将:补码: 111111111111111111111111111111111111100当作了地址来进行打印!

4个二进制位为一个16进制位!而且4个1为一个F,所以上面划算后的结果为:FFFFFFFC!!

代码的运行结果为:

88870292541c49a28be384c31291116f.png

第三题

#include <stdio.h>
int main()
{
	int arr[2][5] = { 1,2,3,4,5,6,7,8,9,10 };
	int* ptr1 = (int*)(&arr + 1);
	int* ptr2 = (int*)(*(arr + 1));
 
	printf("%d %d \n", *(ptr1 - 1), *(ptr2 - 1));  //10  5
	return 0;
}

1.对于数组: int arr[2][5] = { 1,2,3,4,5,6,7,8,9,10 };

54ad734753b24b5da92115f727cd541d.png 我们也可以写为:

d749541b8eeb48e194f8399e5358811a.png 更为方便使用!!

  1. &arr : 取出的是二维数组的地址!&arr+1,跳过整个数组,通过:(int*)(&arr + 1) 强制类型转化为:整型指针!

&arr+1是一个数组指针!

ptr1的类型为:int* ; ptr1-1向前挪动一个整型,指向10的地址!再通过*(ptr-1)解引用操作,得到10;

3.arr是二维数组的数组名,数组名表示二维数组的首元素的地址!二维数组的首元素为第一行,即表示第一行的地址!arr+1 :跳过第一行,对*(arr+1)进行解引用得到第二行!(arr+1) 相当于arr[1],即,第二行的数组名!而arr[1]并没有单独放在sizeof内部,也没有进行&(取地址操作),即指:arr[1][0]的地址!也为元素6的地址!而对于:6的地址,不需要转化为:int * 故 题目中的(int)(*(arr + 1)):int 是迷惑人的!!ptr2拿到6的地址!而对ptr2-1,向前挪动一位,(ptr2-1)解引用,得到元素5!!

20852b081b1a46b69967dbcf538510d0.png

代码的运行结果为:

7e54f60fedc6409385a4d314b4890bc1.png 第二题

#include <stdio.h>
int main()
{
	char* arr[] = { "wolk","at","ailibaba" };
	char** pa = arr;
	pa++;
	printf("%s\n", *pa);  //at
	return 0;
}

对于: char* arr[] = { "wolk","at","ailibaba" }; 数组的每个元素都为: char * ,即,数组arr里面存放的都是char *的指针(地址)!而字符串作表达式的时候,它的值,实际上为首字符的地址!!

5d7bca248d4a407fbc661a5e97f4c2b2.png 而对于:char** pa = arr; pa是一个二级指针,而将arr赋值给pa, a 是数组名,表示首元素的地址!每个元素都是char* ;

但是对于:char **pa,该如何理解?

pa指的是:pa为指针!char 告诉我们,pa指向的对象为:char *类型!

pa++: 跳过一个char* 类型的元素大小,即指向at!!

代码的运行结果为:

3f54dd196afa418d8d5f192f4d472f69.png 对于指针的笔试题,经过4个博客,笔者终于写完了!!在最后,为了考验一下!请自主完成……

#include <stdio.h>
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;
 
}

非常有难度!代码的运行结果为:

a70ae372964144fcbd703addeadaa106.png