指针笔试题(C语言进阶)

本文通过五个C语言指针的实际案例,详细解析了指针的运算、解引用以及结构体指针的使用,帮助读者深化对指针概念的理解。
摘要由CSDN通过智能技术生成

目录

前言

1、案例一

1.1 答案

1.2 解析

2、案例二

2.1 答案

2.2 解析

3、案例三

3.1 答案

3.2 解析 

4、案例四

4.1 答案

4.2 解析

5、案例五

5.1 答案

5.2 解析

总结


前言

        “纸上得来终觉浅,绝知此事要躬行”。本篇通过对指针实际案例的分析,由浅入深,来加强我们对指针的理解。

1、案例一

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

        先自己想想,下面公布答案。

1.1 答案

        答案是2,5。

1.2 解析

        其实第一个输出的2比较好理解,主要是第二个输出可能会有疑问。

        对于*(a+1),本身a表示数组的首元素地址,a+1表示数组的第二个元素的地址,因此解引用a+1得到的结果就是数组的第二个元素的值:2。(解引用就是*)

        对于*(ptr-1),首先我们看看ptr是什么,前面定义 ptr = (int*)(&a + 1); 首先,&a表示将整个数组的地址取出来,其类型为 (int*)[5],大小是5个整型的大小,所以&a+1直接跳过整个数组,指向数组后面一个位置的地址,但是最后又通过(int*)强制将其转化成了int*类型,步长变回一个整型的大小,所以ptr其实就是指向数组最后一个元素后面一个位置地址的指针,类型为(int*)类型,步长为1个整型数据的大小。因此,输出时,ptr-1其实只往回跳了一个整型大小的长度,指向了数组第五个元素(最后一个元素),再解引用得到的答案就是5啦。

2、案例二

#include <stdio.h>
struct Test
{
	int Num;
	char* pcName;
	short cha[2];
	short sBa[4];
}*p = (struct Test*)0x100000;
//假设p 的值为0x100000。如下表达式的值分别为多少。
//已知,结构体Test类型的变量大小是20字节。
//x86环境
int main()
{
	printf("%p\n", p + 0x1);
	printf("%p\n", (unsigned long)p + 0x1);
	printf("%p\n", (unsigned int*)p + 0x1);
	return 0;
}

        这是一道结构体指针的运算,定义结构体*p,其值设定为0x100000。在x86环境下,进行了三种不同的运算,分析结果。

2.1 答案

        答案是:00100014   00100001  00100004

2.2 解析

        对于 p+0x1,是将结构体指针p+1,由于p是结构体指针,所以,其步长为1个结构体的长度,题目说这样一个结构体长度为20(其实可以通过自己计算结构体的内容得出),所以加1直接跳过20个字节,因此输出结果为00100014。(16进制20就是14)。

        对于(unsigned long)p + 0x1,将p强制转换成了无符号长整型,p已经不再是指针,变成了一个长整型1,048,567。加1变成1,048,568。输出时用的%p,也就是地址类型的输出,因此是16进制,1,028,568转换回16进制就是00100001。

        对于(unsigned int*)p + 0x1,将p转化成了无符号整型指针,那其步长就变为了4,p+1就指向4字节后的地址,因此对p+1输出结果为00100004。

3、案例三

        

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

       %x代表16进制输出,来想想这题的答案吧

3.1 答案

        怎么样,算对了吗?

3.2 解析 

        这道题难点在第二个输出,下面我们一个个分析:

        对于ptr1来说,定义时,(int*)(&a + 1);&a取出了整个数组的地址,+1跳过整个数组指向数组最后一个元素后面一个位置的地址, 然后再将它强制类型转换回(int*)类型,步长变回一个整型数据的大小。输出时,ptr[-1]其实就是输出ptr前一个地址的访问值,ptr前一个地址指向的就是数组最后一个元素,因此输出为4.

        对于ptr2来说,定义时,(int*)((int)a + 1);先将a强制转换成int类型,已经不是指针了,因此+1就是存粹往a上加个1。

        在内存中a[4]的存放是如上图的,强制类型转换后+1指向的其实是如上图的位置,那么再对其进行解引用得到的就是02000000(因为我们用的编译器是小端存储,数据的低位是放在地址的低位的,存储时反着存,读取时要反着读取)

4、案例四

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

4.1 答案

        

        答案是1,是不是没想到

4.2 解析

        其实这题在初始化是有坑的,仔细看我们在初始化的时候用到了(),我举个例子:

(0,1)这个式子其实就是1,这是个逗号表达式,逗号表达式的值就是最后一个','后面的那个值,在这里就是1.其实a[3][2]这个数组只初始话了3个值{1 , 3 , 5}。

        好了,现在再来分析p指针,对于p来讲,p=a[0],将a第一行赋值给p其实就是第一行元素的首地址给了p,最后打印时p[0]其实就是访问a第一行第一个元素的值,相当于a[0][0],最后打印出来就是1。

5、案例五

#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]);
	return 0;
}

5.1 答案

答案是:FFFFFFFC,-4

5.2 解析

        这题,p是一个数组指针,其步长为4个整型数据大小,比a数组的每一行长度少1个整型数据大小,p和a一开始指向的是同一个地址,每当p+1时,p只能跳过4个整型数据大小的地址,而对于a来说,a+1可以跳过5个整型数组的大小的地址。所以,p[4][2]和a[4][2]比较,它们都跳过了4行,因此a[4][2]在地址上比p[4][2]多4个整型大小的值,而我们知道,指针和指针相减,得到的是两个指针之间的元素个数,因此是-4,%p是输出地址,其没有原码反码补码的概念,直接将内存中-4的补码输出,就是FFFFFFFC。

总结

        本篇通过五个例子来加强我们对指针和数据存储的理解,希望对你学习c语言的指针有所帮助!

  • 38
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

彭逍遥

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

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

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

打赏作者

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

抵扣说明:

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

余额充值