【C语言进阶】指针笔试题详解(真的超详细!)
十、指针笔试题
1 笔试题1
代码示例:
#include<stdio.h>
int main()
{
//笔试题1
int arr[5] = { 1,2,3,4,5 };
int* ptr = (int*)(&arr + 1);
printf("%d, %d\n", *(arr + 1), *(ptr - 1));
//打印结果:2, 5
return 0;
}
解释说明:
1 (int*)(&arr + 1)
&arr
表示整个数组的地址,+1表示向后跳过一个数组类型的地址( int(*)[4]
);
&arr+1
即为数组末尾元素 5 后一个位置的地址。
将该地址强制类型转换为 int*
后赋给指针变量 ptr
2 *(arr + 1)
arr+1
表示 &arr[1]
,*(arr + 1)
表示 arr[1]
。
图示说明:
2 笔试题2
代码示例:
#include<stdio.h>
struct Test
{
int Num;
char* pcName;
short sDate;
char cha[2];
short sBa[4];
}*p = 0x100000; //假设指针变量p的值为0x100000
int main()
{
//笔试题2
//已知结构体类型的变量大小为20个字节(0x14)
printf("%p\n", p + 0x1);
//地址+1,跳过一个结构体类型的地址
//地址计算:地址+1 --> 0x100000 + 0x14 = 0x00100014
printf("%p\n", (unsigned long)p + 0x1);
//值+1,十六进制整数+1
//十六进制整数计算:0x100000 + 0x1 = 0x00100001
printf("%p\n", (unsigned int*)p + 0x1);
//地址+1,跳过一个整型地址
//地址计算:地址+1 --> 0x100000 + 0x4 = 0x00100004
/*
* 打印结果:
* 00100014
* 00100001
* 00100004
*/
return 0;
}
解释说明:
1 p + 0x1
表示结构体指针 p
( struct Test*
类型)的值+1,也就是地址+1,p
类型的大小为20个字节( 整数20的十六进制形式:0x14 )。
地址+1后的值为:p的值(地址) + 0x14
//0x100000 + 0x14 = 0x00100014
2 (unsigned long)p + 0x1
指针变量 p
的值被强转为了一个 unsigned long
类型的值
即: 一个十六进制的整数 + 0x1
//0x100000 + 0x1 = 0x00100001
3 (unsigned int*)p + 0x1)
指针变量 p
的类型被强转为 (unsigned int*)
类型,还是一个地址,地址+1;
新的类型大小为4个字节( 整数4的十六进制形式:0x4 );
地址+1后的值为:p的值(地址)+ 0x4
//0x100000 + 0x4 = 0x00100004
3 笔试题3
代码示例:
#include<stdio.h>
int main()
{
//笔试题3
int arr[4] = { 1,2,3,4 };
int* ptr1 = (int*)(&arr + 1);
int* ptr2 = (int*)((int)arr + 1);
printf("%x, %x\n", ptr1[-1], *ptr2);//4, 2000000
return 0;
}
解释说明:
1 (int*)(&arr + 1)
&arr
表示整个数组的地址,+1表示向后跳过一个数组类型的地址( int(*)[4]
);
&arr+1
即为数组末尾元素 5 后一个位置的地址。
将该地址强制类型转换为 int*
后赋给指针变量 ptr1
2 (int*)((int)arr + 1)
arr -- 数组首元素的地址
(int)arr -- 将地址(int*类型的值)强制类型转换为int型的值
(int)arr+1 -- int型的值 +1 -- 值可以看做是首元素的地址向后移动一个字节的地址
(int*)((int)arr+1) -- 将首元素的地址向后移动1个字节大小空间后的地址 -- 原地址+0x1
3 %x
转换说明
表示以十六进制形式打印值。
图示说明:
1 ptr1
2 ptr2
该机器以小端字节序存储,取得的 int
型整数为:02000000(十六进制)。
4 笔试题4
代码示例:
#include<stdio.h>
int main()
{
//笔试题4
int arr[3][2] = { {0,1},{2,3},{4,5} };
int* p;
p = arr[0];//二维数组的首元素,是一个一维数组的数组名
//p[0] -> *p -> *(p+0) -> *arr[0] -> *(arr[0]+0) -> arr[0][0] -> 第一行第一个元素 -> 0
printf("%d\n", p[0]);//0
printf("%d\n", *arr[0]);
printf("%d\n", arr[0][0]);
printf("%d\n", *p);
printf("%d\n", *(p + 0));
printf("%d\n", *(arr[0] + 0));
return 0;
}
解释说明:
1 arr[0]
表示二维数组的首元素
相当于一个一维数组数组名,一维数组名表示一维数组首元素的地址(&arr[0][0]
)。
2 p[0]
相当于是 *(p+0)
p[0] --> *(p+0) --> *(arr[0]+0) --> arr[0][0] --> 第一行第一个元素 --> 0
5 笔试题5
代码示例:
#include<stdio.h>
int main()
{
//笔试题5
int arr[5][5];//二维数组
int(*p)[4];//数组指针
p = arr;
//arr -> int(*)[5]
//p -> int(*)[4]
printf("%p, %d\n", (&p[4][2] - &arr[4][2]), (&p[4][2] - &arr[4][2]));//FFFFFFFC, -4
//&p[4][2] -> &(*(*(p+4)+2))
return 0;
}
解释说明:
1 arr
是二维数组数组名,表示二维数组首元素的地址,其类型是 int(*)[5]
。
2 &p[4][2] - &arr[4][2]
表示指针-指针,|指针-指针|(绝对值)得到的是指针之间元素的个数。
3 &p[4][2] - &arr[4][2]
计算结果是-4, %p
以十六进制形式打印输出,会将-4的补码当做地址打印。
10000000 00000000 00000000 00000100 - -4的原码
11111111 11111111 11111111 11111011 - -4的反码
11111111 11111111 11111111 11111100 - -4的补码
//将-4的补码当做地址打印(将二进制补码转换成十六进制整数):
1111 1111 1111 1111 1111 1111 1111 1100
F F F F F F F C
FF FF FF FC
图示说明:
6 笔试题6
代码示例:
#include<stdio.h>
int main()
{
//笔试题6
int arr[2][5] = { {1,2,3,4,5},{6,7,8,9,10} };
int* ptr1 = (int*)(&arr + 1);
//&arr+1表示向后跳过一个二维数组类型的地址
int* ptr2 = (int*)(*(arr + 1));
//*(arr+1)表示arr[1],也就是二维数组第二行,表示一个一维数组名,即&arr[1][0]
printf("%d, %d\n", *(ptr1 - 1), *(ptr2 - 1));//10, 5
return 0;
}
解释说明:
1 &arr + 1
&arr
表示取出二维数组的地址,&arr+1
表示跳过一个二维数组类型的地址。
2 *(arr + 1)
arr+1
表示 &arr[1]
,*(arr+1)
表示 arr[1]
,即二维数组首元素,一个一维数组数组名,即 &arr[1][0]
。
*(arr+1) -> arr[1] -> &arr[1][0] -> 第二行第一列元素的地址 -> int*
图示说明:
7 笔试题7
代码示例:
#include<stdio.h>
int main()
{
//笔试题7
char* arr[] = { "work","at","alibaba" };
char** pa = arr;//arr -> 指向字符'w'的字符指针变量的地址
pa++;//p++ -> arr+1
//*pa -> *(arr+1) -> arr[1] -> 字符'a'的地址
printf("%s\n", *pa);//at
return 0;
}
解释说明:
1 arr
是字符指针数组数组名,表示数组首元素的地址,即指向字符 w
的字符指针变量的地址。
2 pa++;
可以理解为 pa = arr+1;
,此时二级指针变量 pa
存放的是字符指针数组第二个元素的地址。
3*pa
*pa -> *(arr+1) -> arr[1] -> 字符'a'的地址
图示说明:
8 笔试题8
代码示例:
#include<stdio.h>
int main()
{
//笔试题8
char* c[] = { "ENTER","NEW","POINT","FIRST" };
char** pc[] = { c + 3,c + 2,c + 1,c };
char*** ppc = pc;
printf("%s\n", **++ppc); //POINT
//++ppc -> pc+1 -> &pc[1]
//*++ppc -> *(pc+1) -> pc[1] -> c+2
//**++ppc -> *(c+2) -> 'P'的地址
//此时ppc == pc+1
//
printf("%s\n", *-- * ++ppc + 3);//ER
//++ppc -> pc+2 -> &pc[1]
//*++ppc -> *(pc+2) -> pc[2] -> c+1
//--*++ppc -> c -> &c[0] -- (此时pc[2]的值由c+1变成了c)
//*--*++ppc -> *c -> c[0] -> 第一个'E'的地址
//*--*++ppc+3 -> c[0]+3 -> 第二个'E'的地址
//此时ppc == pc+2,pc[1] == c
//
printf("%s\n", *ppc[-2] + 3); //ST
//ppc[-2] -> *(ppc-2) -> *(pc+2 -2) -> *(pc) -> *(pc+0) -> pc[0] -> c+3
//*ppc[-2] -> *(c+3) -> c[3] -> 'F'的地址
//*ppc[-2]+3 -> c[3]+3 -> 'S'的地址
//
printf("%s\n", ppc[-1][-1] + 1);//EW
//ppc[-1] -> *(ppc-1) -> *(pc+2 -1) -> *(pc+1) -> pc[1] -> c+2
//ppc[-1][-1] -> *(*(ppc-1)-1) -> *(c+2 -1) -> *(c+1) -> c[1] -> 'N'的地址
//ppc[-1][-1]+1 -> c[1]+1 -> 'E'的地址
//
/*
* POINT
* ER
* ST
* EW
*/
return 0;
}
解释说明:
1 指向关系图示:
2 **++ppc
ppc --> pc+0 --> c+3 --> 'F'的地址(FIRST)
++ppc --> ppc+1 --> pc+1
*(++ppc) --> *(ppc+1) --> *(pc+1) --> c+2
**(++ppc) --> **(ppc+1) --> **(pc+1) --> *(c+2) --> 'P'的地址(POINT)
3 *-- * ++ppc + 3
ppc --> pc+1 --> c+2 --> 'P'的地址(POINT)
++ppc -- > ppc+1 --> (pc+1)+1 --> pc+2
*(++ppc) --> *(pc+2) --> c+1
--(*(++ppc)) --> --(*(pc+2)) --> --(c+1) --> c
*(--(*(++ppc))) --> *(--(*(pc+2))) --> *(--(c+1)) --> *c --> 'E'的地址(ENTER)
*(--(*(++ppc)))+3 --> 'E'的地址+3 --> 'E'的地址(ER)
4 *ppc[-2] + 3
ppc --> pc+2 --> c+1 --> 'N'的地址(NEW)
*ppc[-2]+3 --> *(*(ppc-2)+3)
ppc[-2] --> *(ppc-2) --> *((pc+2)-2) --> *(pc+0) --> c+3
*ppc[-2] --> **pc --> *(c+3) --> 'F'的地址(FIRST)
*ppc[-2]+3 --> *(c+3)+3 --> 'F'的地址+3 --> 'S'的地址(ST)
5 ppc[-1][-1] + 1
ppc --> pc+2 --> c+1 --> 'N'的地址(NEW)
ppc[-1][-1]+1 --> *(*(ppc-1)-1)+1
ppc[-1] --> *(ppc-1) --> *((pc+2)-1) --> *(pc+1) --> c+2
ppc[-1][-1] --> *(*(pc+1)-1) --> *((c+2)-1) --> *(c+1) --> 'N'的地址(NEW)
ppc[-1][-1]+1 --> *(c+1)+1 --> 'N'的地址+1 --> 'E'的地址(EW)
总结:
本节详细介绍了8道指针相关的笔试题,用详细的注释与示例图画对题目代码进行深度剖析。
感谢您的阅读!如有任何错误,欢迎您的批评指正!