指针类型的幻觉

本文通过具体实例解析了C/C++中不同类型指针相减的原理,揭示了指针运算背后的秘密,并通过汇编语言展示了编译器如何处理指针运算。

关于指针,我最初的想法就是,所有类型的指针都是一样的,都是4个字节长度。这句话是对的,但是往往会给我们带来“幻觉”,从而误导我们的判断!就像“幻城”中的幻术师所施展的幻术,真假难辨!

 

首先,先让我们看看以下情况:

问题:

C/C++ code
int a[]={1,2,3,4,5,6,7,8,9};

short *p=(short*)(a+2);
short *q=(short*)a;

a[p-q]=?

最后,让你判断a[p-q]是多少?当你没经过大脑思考时,你的判断可能是:这还不简单,相差2呗,那就是a[2]呗,就是3呗!当你呗呗呗的时候,你已经范了一个致命的错误,答案是a[4],即5。别忘了p和q都是short*的指针类型!可能你会喋喋不休,说:short*怎么了,指针都是4个字节长,p存储的地址就是a+2后的地址啊,q就是存储的a的地址啊!那你有没有想过相同类型的地址相减的操作返回的是什么?其过程并不是简单的指针所指的地址进行相减,然后返回地址值的差值;指针想相减过程其实是地址相减求出所指向的内存地址所间隔的字节数,然后再与指针多对应类型大小进行相比,即(地址相减得到的字节数)/sizeof(short); 这样,p和q所指向的地址相差的字节数数2×4=8个字节,然后再8/sizeof(short) = 4。所以最后a[p-q] = a[4] = 5。

让我们看看汇编语言是怎么处理的:

 

  int a[]={1,2,3,4,5,6,7,8,9};
0040135E  mov         dword ptr [a],1
00401365  mov         dword ptr [ebp-24h],2
0040136C  mov         dword ptr [ebp-20h],3
00401373  mov         dword ptr [ebp-1Ch],4
0040137A  mov         dword ptr [ebp-18h],5
00401381  mov         dword ptr [ebp-14h],6
00401388  mov         dword ptr [ebp-10h],7
0040138F  mov         dword ptr [ebp-0Ch],8
00401396  mov         dword ptr [ebp-8],9

 short *p=(short*)(a+2);
0040139D  lea           eax,[ebp-20h]
004013A0  mov         dword ptr [p],eax
 short *q=(short*)a;
004013A3  lea         eax,[a]
004013A6  mov         dword ptr [q],eax
 

 printf("k=%d/n", a[p-q]);
004013A9  mov         eax,dword ptr [p]
004013AC  sub         eax,dword ptr [q]     // 相减后寄存器eax中的值是8(……1000)
004013AF  sar           eax,1                       // 将其进行右移操作,eax的值变为4(……0100)
004013B1  mov         esi,esp
004013B3  mov         ecx,dword ptr a[eax*4]
004013B7  push        ecx 
004013B8  push        offset string "k=%d/n" (404228h)
004013BD  call          dword ptr [__imp__printf (404124h)]
004013C3  add         esp,8
004013C6  cmp         esi,esp
004013C8  call          _RTC_CheckEsp (401BC0h)

 

由此可见,编译器编译后的代码也是这么处理的,支持了我们对指针类型的观点!你是否有点慧眼是真的感觉?哈哈,那好,来猜猜下面的结果,如果你没在3秒钟说出答案,那你就自个找个墙,面壁思过吧!O(∩_∩)O哈哈~

 

C/C++ code
int a[]={1,2,3,4,5,6,7,8,9};

char*p=(char*)(a+2);
char*q=(char*)a;

a[p-q]=?

 

一秒……

两秒……

三秒……

你有答案了吗?

 


 

 

#include <stdio.h> void int16(int num) { char hex[32]; int i = 0; if (num == 0) { printf("0x0\n"); return; } unsigned int unum = (unsigned int)num; while (unum > 0) { int remainder = unum % 16; if (remainder < 10) { hex[i] = remainder + '0'; // 0-9 } else { hex[i] = remainder - 10 + 'A'; // A-F } unum /= 16; i++; } printf("0x"); for (int j = i - 1; j >= 0; j--) { printf("%c", hex[j]); } printf("\n"); } int arr[10001]; int main(){ int T; scanf("%d",&T); int sum=0; char s[2]={'7','2'}; for(int i=1;i<=T;i++){ scanf("%d",&arr[i]); int16(arr[i]); } } 接下来用C语言要怎么做(只给出方案,先不用写): 题目描述 【没有签名的】「周星星」潜入学校执行秘密任务失败被发现,一路上要从操场跑到教学楼。 他每跑一步,都会经过一个整数,代表地砖上奇奇怪怪的各种东西。 每个整数在内存中由 4 个字节组成,而编号为 114 的字节代表着「香蕉皮」。给出一串整数,表示「周星星」跑过的所有地砖。 现求所有地砖上的香蕉皮总数,即求在给出的整数序列中所有整数的所有字节中,值为 114 的字节数量。 本题有多种解法。推荐尝试使用指针的解法,感受指针的优越性。 输入 第一行,一个正整数 n ,表示地砖的数量(1≤n≤10000 )。 第二行,包含 n 个整数 a1,a2,…,an ,每个整数在 int 范围内。 输出 输出一个整数,表示所有地砖的字节中,香蕉皮(字节值为 114)出现的总次数。 输入样例 3 114 291 7471106 输出样例 2 样例解释 三个整数在内存中的四个字节如下(假设为小端序): 114 : 72 00 00 00 291 : 23 01 00 00 7471106 : 02 00 72 00 共有两个字节的值为 114 (十六进制 0x72),因此输出 2。 Hint 同学A:这题好像可以用位运算做诶,但是我做了半个小时 T_T 同学B:【没有签名的】「周星星」这个名字好奇怪,怀疑出题学长的deepseek出幻觉了 @w@
最新发布
11-07
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值