以下代码在32位和64位系统上打印不同的结果:
#include <stdio.h>
void swapArray(int **a, int **b)
{
int *temp = *a;
*a = *b;
*b = temp;
}
int main()
{
int a[2] = {1, 3};
int b[2] = {2, 4};
swapArray(&a, &b);
printf("%d\n", a[0]);
printf("%d\n", a[1]);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
在32位系统中编译后,输出为:
2
3
Run Code Online (Sandbox Code Playgroud)
在64位上输出是:
2
4
Run Code Online (Sandbox Code Playgroud)
据我了解,该功能swapArray
只交换指针在第一要素a
和b
.所以在打电话之后swapArray
,a
应该指向2
并且b
应该指向1
.
出于这个原因a[0]
应该产生2
,并且a[1]
应该在内存中的下一个字节之后引用2
,其中包含4
.
有人可以解释一下吗?
编辑:
感谢评论和答案,我现在注意到&a
并且&b
是类型int (*)[]
而不是int **
.这显然使代码不正确(实际上我得到了编译器警告).但是,令人感兴趣的是,为什么编译器(gcc)只是发出警告而不是错误.
我仍然留下了在不同系统上导致不同结果的问题,但由于代码不正确,因此不太相关.
编辑2:
至于不同系统的不同结果,我建议阅读AndreyT的评论.
swapArray(&a, &b);
Run Code Online (Sandbox Code Playgroud)
&a
并且&b
不是类型int **
而是类型int (*)[2]
.BTW你的编译器很友好地接受你的程序,但编译器有权拒绝翻译它.
在回答你的问题之前,让我们看看在指针操作期间发生了什么.我使用一个非常简单的代码来演示这个:
#include <stdio.h>
int main() {
int *p;
int **p2;
int x = 3;
p = &x;
p2 = &p;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
现在看看反汇编:
(gdb) disassemble
Dump of assembler code for function main:
0x0000000000400474 <+0>: push rbp
0x0000000000400475 <+1>: mov rbp,rsp
0x0000000000400478 <+4>: mov DWORD PTR [rbp-0x14],0x3
0x000000000040047f <+11>: lea rax,[rbp-0x14]
0x0000000000400483 <+15>: mov QWORD PTR [rbp-0x10],rax
0x0000000000400487 <+19>: lea rax,[rbp-0x10]
0x000000000040048b <+23>: mov QWORD PTR [rbp-0x8],rax
=> 0x000000000040048f <+27>: mov eax,0x0
0x0000000000400494 <+32>: leave
0x0000000000400495 <+33>: ret
Run Code Online (Sandbox Code Playgroud)
拆卸非常明显.但是需要在这里添加一些注意事项,
我的函数的堆栈框架从这里开始:
0x0000000000400474 <+0>: push rbp
0x0000000000400475 <+1>: mov rbp,rsp
Run Code Online (Sandbox Code Playgroud)
那就让他们现在拥有的东西
(gdb) info registers $rbp
rbp 0x7fffffffe110 0x7fffffffe110
Run Code Online (Sandbox Code Playgroud)
在这里,我们将价值3
放在[rbp - 0x14]
地址上.让我们看看内存映射
(gdb) x/1xw $rbp - 0x14
0x7fffffffe0fc: 0x00000003
Run Code Online (Sandbox Code Playgroud)
重要的是要注意使用的DWORD
数据类型是32位宽.所以在旁注中,整数文字3
处理被视为4字节单位.
下一条指令用于lea
加载刚刚保存在早期指令中的值的有效地址.
0x000000000040047f <+11>: lea rax,[rbp-0x14]
Run Code Online (Sandbox Code Playgroud)
这意味着现在$rax
将具有价值0x7fffffffe0fc
.
(gdb) p/x $rax
$4 = 0x7fffffffe0fc
Run Code Online (Sandbox Code Playgroud)
接下来我们将使用这个地址保存到内存中
0x0000000000400483 <+15>: mov QWORD PTR [rbp-0x10],rax
Run Code Online (Sandbox Code Playgroud)
重要的是要注意QWORD
这里使用的一个.因为64位系统具有8字节的本机指针大小.0x14 - 0x10 = 4
在早期的mov
指令中使用了字节.
接下来我们有:
0x0000000000400487 <+19>: lea rax,[rbp-0x10]
0x000000000040048b <+23>: mov QWORD PTR [rbp-0x8],rax
Run Code Online (Sandbox Code Playgroud)
这又是第二次间接.总是所有与地址相关的价值都是QWORD
.注意这一点很重要.
现在让我们来看看你的代码.
在调用swaparray之前,你有:
=> 0x00000000004004fe <+8>: mov DWORD PTR [rbp-0x10],0x1
0x0000000000400505 <+15>: mov DWORD PTR [rbp-0xc],0x3
0x000000000040050c <+22>: mov DWORD PTR [rbp-0x20],0x2
0x0000000000400513 <+29>: mov DWORD PTR [rbp-0x1c],0x4
0x000000000040051a <+36>: lea rdx,[rbp-0x20]
0x000000000040051e <+40>: lea rax,[rbp-0x10]
0x0000000000400522 <+44>: mov rsi,rdx
0x0000000000400525 <+47>: mov rdi,rax
Run Code Online (Sandbox Code Playgroud)
这非常简单.初始化您的数组,&
当数组开始的有效地址加载到$rdi
和时,操作符的效果可见$rsi
.
现在让我们看看它在做什么swaparray()
.
数组的开头保存到$rdi
和$rsi
.让我们看看他们的内容
(gdb) p/x $rdi
$2 = 0x7fffffffe100
(gdb) p/x $rsi
$3 = 0x7fffffffe0f0
0x00000000004004c8 <+4>: mov QWORD PTR [rbp-0x18],rdi
0x00000000004004cc <+8>: mov QWORD PTR [rbp-0x20],rsi
Run Code Online (Sandbox Code Playgroud)
现在,第一个语句int *temp = *a
按照以下说明执行.
0x00000000004004d0 <+12>: mov rax,QWORD PTR [rbp-0x18]
0x00000000004004d4 <+16>: mov rax,QWORD PTR [rax]
0x00000000004004d7 <+19>: mov QWORD PTR [rbp-0x8],rax
Run Code Online (Sandbox Code Playgroud)
现在是决定性的时刻,你发生了*a
什么?
$rax
存储的值中[rbp - 0x18]
.价值$rdi
保存的地方.它反过来保存第一个数组的第一个元素的地址.$rax
来获取另一个间接并获取QWARD
并加载它$rax
.那会是什么回归?它会返回一个QWARD
从0x7fffffffe100
.这实际上将从那里保存的两个四字节数量形成一个8字节的数量.详细说明, 那里的记忆如下.
(gdb) x/2xw $rdi
0x7fffffffe100: 0x00000001 0x00000003
Run Code Online (Sandbox Code Playgroud)
现在,如果你拿一个 QWORD
(gdb) x/1xg $rdi
0x7fffffffe100: 0x0000000300000001
Run Code Online (Sandbox Code Playgroud)
所以你已经被搞砸了.因为你的边界不正确.
其余的代码可以用类似的方式解释.
现在为什么它在32位平台上有所不同?因为在32位平台中,本机指针宽度是4个字节.所以这里的事情会有所不同.语义错误代码的主要问题源于整数类型宽度和本机指针类型的差异.如果两者都相同,您仍然可以解决代码问题.
但是你永远不应该编写假定本机类型大小的代码.这就是标准的原因.这就是你的编译器给你警告的原因.
从语言的角度来看,它的类型不匹配已在前面的答案中指出,所以我不会进入那个.
归档时间: |
|
查看次数: |
1056 次 |
最近记录: |