首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【C语言】指针笔试题2

【C语言】指针笔试题2

作者头像
苏兮
发布2026-01-13 17:13:16
发布2026-01-13 17:13:16
1040
举报

前言:

继上文【C语言】指针笔试题1,此篇文章是指针学习的最终章,笔试题详解。建议对指针有了深入理解再来食用。

指针笔试题

笔试题1

代码:

代码语言:javascript
复制
int main()
{
	int a[5] = { 1, 2, 3, 4, 5 };
	int* ptr = (int*)(&a + 1);
	printf("%d,%d", *(a + 1), *(ptr - 1));
	return 0;
}

输出

在这里插入图片描述
在这里插入图片描述

解析:

代码语言:javascript
复制
int main()
{
	int a[5] = { 1, 2, 3, 4, 5 };
	int* ptr = (int*)(&a + 1);   
	//&a为数组指针,&a + 1则跳过整个数组,
	//即此时指向数组最后一个元素(5)的后面
	//(int*)(&a + 1)又将它强制类型转化为int*的指针
	printf("%d,%d", *(a + 1), *(ptr - 1));
	//a数组名首元素地址,a + 1则为的第二个元素地址,
	// *(a + 1)解引用则为2;
	//ptr为int*的指针,指向数组最后一个元素(5)的后面,
	//ptr - 1则指针向后退4个字节(因为指针为int*),指向数组最后一个元素,
	//*解引用则为5;
	return 0;
}

笔试题2

代码:

代码语言:javascript
复制
//这里直接告诉大家结构体Test类型的变量大小是20个字节
struct Test
{
	int Num;
	char* pcName;
	short sDate;
	char cha[2];
	short sBa[4];
}*p;
//假设p 的值为0x0。 如下表表达式的值分别为多少?
int main()
{
	printf("%p\n", p + 0x1);
	printf("%p\n", (unsigned long)p + 0x1);
	printf("%p\n", (unsigned int*)p + 0x1);
	return 0;
}

输出(都为16进制):

在这里插入图片描述
在这里插入图片描述

解释:

代码语言:javascript
复制
int main()
{
	printf("%p\n", p + 0x1);
	//p + 0x1,跳过一个结构体大小,故为0x14(16进制);
	printf("%p\n", (unsigned long)p + 0x1);
	//(unsigned long)p,将p强制类型转化为无符号长整形,
	//(unsigned long)p + 0x1则为0x1;
	printf("%p\n", (unsigned int*)p + 0x1);
	//(unsigned int*)p将结构体指针强制类型转换为无符号整型指针,
	//且unsigned int为4个字节,整数运算:
	//所以(unsigned int*)p + 0x1则跳过四个字节,故为0x4
	return 0;
}

笔试题3

看代码:

代码语言:javascript
复制
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;
}

输出:

在这里插入图片描述
在这里插入图片描述

解释:

代码语言:javascript
复制
int main()
{
	int a[4] = { 1, 2, 3, 4 };
	int* ptr1 = (int*)(&a + 1);    
	//&a数组地址,
	//&a + 1数组指针跳过整个数组,即指向数组最后一个元素的后面
	//(int*)(&a + 1)强制类型转换,将数组指针转化为(int*)类型的指针;
	//即ptr1为指向数组最后一个元素后面int*指针
	int* ptr2 = (int*)((int)a + 1);//下面画图解释
	printf("%x\n", ptr1[-1]);
	printf("%x\n", *ptr2);  //下面画图解释
	//ptr1[-1]可以转换为*(ptr-1),
	//ptr - 1,ptr为int*指针,-1则为向后退4个字节,指向数组最后一个元素;
	//故输出为4;
	return 0;
}

咱们主要来分析一下这两行代码(干货哦!!!):

代码语言:javascript
复制
	int* ptr2 = (int*)((int)a + 1);
	printf("%x\n", *ptr2); 

在此之前,咱们要对大小端字节序了解,可以看我关于整形在内存中的存储的介绍。 假设咱们是小端字节序,则数组中元素存储是这样的:

在这里插入图片描述
在这里插入图片描述

了解了这个,来分析一下代码: (int)a:a是数组首元素地址,(int)a是将a强制类型转化为int型的数据; ((int)a + 1):假设a是0x1000,(int)a + 1此时为整数运算则为0x1001, (int*)((int)a + 1):又将(int)a + 1强制类型转化为int型的指针,那它此时指向哪里呢,看图 a指针指向示意图:

在这里插入图片描述
在这里插入图片描述

(int)((int)a + 1)指针指向示意图:

在这里插入图片描述
在这里插入图片描述

那对于*ptr2怎么理解呢?

因为ptr2是int型的指针,所以解引用访问四个字节,有因为咱们是小端字节序,所以它是从从ptr2指针指向的位置起(0x1001)向后访问4个字节(int)内容,即为*ptr2

图示:

在这里插入图片描述
在这里插入图片描述

16进制则为0x02000000; 当然,咱们也可以试试大端字节序的情况下是什么样的。

笔试题4

看代码:

代码语言: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;
}

输出:

在这里插入图片描述
在这里插入图片描述

解释:

代码语言:javascript
复制
#include <stdio.h>
int main()
{
	int a[3][2] = { (0, 1), (2, 3), (4, 5) };
	//重点在这:
	//(0, 1)逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。
	//即数组可写为int a[3][2] = { 1, 3, 5 };后面默认为0;
	int* p;
	p = a[0];
	//a[0]为第一行的数组名
	//数组名为首元素地址
	//即p=&a[0][0]
	printf("%d", p[0]);
	//p[0]可以这样看*(p+0);
	//故为1;
	return 0;
}

笔试题5

看代码:

代码语言: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;
}

输出:

在这里插入图片描述
在这里插入图片描述

解释:

代码语言:javascript
复制
int main()
{
	int a[5][5];
	int(*p)[4];
	//一维数组指针
	p = a;
	//首先,咱们先对p与a的类型进行分析:
	// p是int(*)[4];a是int(*p)[5];这个是要明白的,然后分析代码;
	// 对于a与p他俩的区别是什么呢:
	//a + 1 会跳过 5 * sizeof(int) 字节(通常 20 字节)
	//p + 1 会跳过 4 * sizeof(int) 字节(通常 16 字节)
	printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
	return 0;
}

首先,先来看数组a的示意图:

在这里插入图片描述
在这里插入图片描述

咱们先来看a[4][2]所在的位置,很简单:

在这里插入图片描述
在这里插入图片描述

那来看看p[4][2]

在这里插入图片描述
在这里插入图片描述

将两者放一起:

在这里插入图片描述
在这里插入图片描述

那么对于&p[4][2] - &a[4][2],即指针相减,得到的是两个指针之间的元素,为-4; 对于%d形式的输出,就为-4; 但对于%p形式的输出呢? 解释:

  • %p 用于打印指针(内存地址),而内存地址本质上是无符号整数。
  • -4 的二进制补码:0xFFFFFFFC(即 11111111 11111111 11111111 11111100)。
  • 当 -4 被当作指针(无符号数)时,它会被解释为 0xFFFFFFFC。也可以说,%p不管内存中存储的是什么,它只会原样输出存储的二进制(因为无符号数“补码”的概念)。

笔试题6

代码:

代码语言: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;
}

输出:

在这里插入图片描述
在这里插入图片描述

解释:

代码语言:javascript
复制
int main()
{
	int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int* ptr1 = (int*)(&aa + 1);
	//&aa取出整个数组的地址
	//&aa+1跳过整个数组,指向数组最后一个元素的后面
	//(int*)(&aa + 1)将数组指针强制类型转化为int*类型的
	//即ptr1是一个int*的指针,且指向数组最后一个元素的后面
	int* ptr2 = (int*)(*(aa + 1));
	//aa二维数组的数组名是第一行的地址,类型为int(*)[5]
	//aa + 1第二行地址,类型为int(*)[5]
	//*(aa + 1),可以这样写aa[1]
	//即数组第二行元素,为一维数组
	//一维数组数组名是首元素地址
	//首元素地址类型为int*
	//(int*)(),多余
	//即ptr2指向数组第二行第一个元素
	printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));
	//*(ptr1 - 1)数组最后一个元素10;
	//*(ptr2 - 1)数组第一行最后一个元素5
	return 0;
}

笔试题7

看代码:

代码语言:javascript
复制
#include <stdio.h>
int main()
{
	char* a[] = { "work","at","alibaba" };
	char** pa = a;
	pa++;
	printf("%s\n", *pa);
	return 0;
}

输出:

在这里插入图片描述
在这里插入图片描述

解释:

代码语言:javascript
复制
#include <stdio.h>
int main()
{
	char* a[] = { "work","at","alibaba" };
	//对字符指针数组进行解释:
	//它是将三个字符串的首元素地址存储在数组中
	char** pa = a;
	//a 是数组名,在大多数表达式中会退化为指向首元素的指针,即 pa 为 &a[0](类型是 char**)。
	//pa 存储的是 a[0] 的地址(即 &a[0])
	pa++;
	//pa 原本指向 a[0],pa++ 后指向 a[1](即 &a[1])
	printf("%s\n", *pa);
	//*pa 得到 a[1],即 'at' 的首地址,printf 打印这个字符串";
	return 0;
}

笔试题8

这道题需要考虑的点较多,仔细点哦!!! 代码:

代码语言: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;
}

输出:

在这里插入图片描述
在这里插入图片描述

解释:

代码语言:javascript
复制
int main()
{
	char* c[] = { "ENTER","NEW","POINT","FIRST" };
	//数组 c 的每个元素(c[0]、c[1] 等)存储的是指向对应字符串常量的指针:
	//c[0] → 指向 "ENTER" 的首地址(即字符 'E' 的地址)。
	//c[1] → 指向 "NEW" 的首地址。
	//c[2] → 指向 "POINT" 的首地址。
	//c[3] → 指向 "FIRST" 的首地址。
	char** cp[] = { c + 3,c + 2,c + 1,c };
	//c数组名为首元素地址,即&c[0];
	//c + 3 → 指向c[3]     cp[0]
	//c + 2 → 指向c[2]     cp[1]
	//c + 1 → 指向c[1]     cp[2]
	//c  → 指向c[0]        cp[3]
	char*** cpp = cp;
	//cp数组名为首元素地址,即&cp[0];
	//cpp → 指向cp[0]

	//搞清楚上面这部分,就很简单了
	//注意:++cpp会产生副作用,会使得cpp改变

	printf("%s\n", **++cpp);
	//++cpp → 指向cp[1];
	//*++cpp 为cp[1]是c + 2 → 指向c[2];
	//**++cpp 为c[2];
	//则输出为"POINT"

	printf("%s\n", *-- * ++cpp + 3);
	//注意操作符优先级
	//++cpp → 指向cp[2];
	//* ++cpp为cp[2] → 指向c[1]
	//-- * ++cpp  → 指向c[0]
	//*-- * ++cpp 为c[0] → 指向 "ENTER" 的首地址
	//*-- * ++cpp + 3 → 指向 "ENTER" 的'E'的地址
	//则输出为"ER"

	printf("%s\n", *cpp[-2] + 3);
	//由于此时cpp → 指向cp[2]
	//cpp[-2]等效为*(cpp-2)
	//cpp-2 → 指向cp[0]
	//*(cpp-2)即为cp[0];
	//*(cp[0])即为c[3] → 指向 "FIRST" 的首地址。
	//+ 3 → 指向 "FIRST" 的'S'的地址
	//则输出为"ST"

	printf("%s\n", cpp[-1][-1] + 1);
	//cpp[-1][-1]+1等效为*(*(cpp-1)-1)+1;
	//先看cpp-1 → 指向cp[1];
	//*(cpp-1)则为cp[1] → 指向c[2];
	//*(cpp-1)-1 → 指向c[1];
	//*(*(cpp-1)-1)则为c[1] → 指向 "NEW" 的首地址。
	//+1 → 指向 "NEW" 的'E'的地址;
	//则输出为"EW"

	return 0;
}

总结:

指针到这就结束了,但我们的学习尚未结束,希望大家对指针都有各自的理解。 谢谢学习至此!!!

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

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

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

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言:
  • 指针笔试题
    • 笔试题1
    • 笔试题2
    • 笔试题3
    • 笔试题4
    • 笔试题5
    • 笔试题6
    • 笔试题7
    • 笔试题8
  • 总结:
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档