在32位和64位系统上引用指针

Eya*_*lAr 2 c arrays pointers

以下代码在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只交换指针在第一要素ab.所以在打电话之后swapArray,a应该指向2并且b应该指向1.
出于这个原因a[0]应该产生2,并且a[1]应该在内存中的下一个字节之后引用2,其中包含4.

有人可以解释一下吗?

编辑:
感谢评论和答案,我现在注意到&a并且&b是类型int (*)[]而不是int **.这显然使代码不正确(实际上我得到了编译器警告).但是,令人感兴趣的是,为什么编译器(gcc)只是发出警告而不是错误.
我仍然留下了在不同系统上导致不同结果的问题,但由于代码不正确,因此不太相关.

编辑2:
至于不同系统的不同结果,我建议阅读AndreyT的评论.

oua*_*uah 5

swapArray(&a, &b); 
Run Code Online (Sandbox Code Playgroud)

&a并且&b不是类型int **而是类型int (*)[2].BTW你的编译器很友好地接受你的程序,但编译器有权拒绝翻译它.


Aft*_*nix 5

在回答你的问题之前,让我们看看在指针操作期间发生了什么.我使用一个非常简单的代码来演示这个:

#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什么?

  1. 它加载到$rax存储的值中[rbp - 0x18].价值$rdi保存的地方.它反过来保存第一个数组的第一个元素的地址.
  2. 通过使用存储的地址$rax来获取另一个间接并获取QWARD并加载它$rax.那会是什么回归?它会返回一个QWARD0x7fffffffe100.这实际上将从那里保存的两个四字节数量形成一个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个字节.所以这里的事情会有所不同.语义错误代码的主要问题源于整数类型宽度和本机指针类型的差异.如果两者都相同,您仍然可以解决代码问题.

但是你永远不应该编写假定本机类型大小的代码.这就是标准的原因.这就是你的编译器给你警告的原因.

从语言的角度来看,它的类型不匹配已在前面的答案中指出,所以我不会进入那个.