C指针/数组值混淆

时间:2012-02-08 16:49:38

标签: c arrays pointers

假设我们有以下代码:

typedef union {
   float    e[4];
   __v4sf   v;
   float    *s;
} __vec4f;

float test[12];


int main(){ 
__vec4f one;

printf("adrs: &one.s = 0x%x, &one.e = 0x%x\n", &one.s, &one.e);
printf("vals: one.s = 0x%x, one.e = 0x%x\n",one.s,one.e);
one.s = test;
printf("adrs: &one.s = 0x%x, &one.e = 0x%x\n", &one.s, &one.e);
printf("vals: one.s = 0x%x, one.e = 0x%x\n",one.s,one.e);

return 0;
}

运行结果时如下:

adrs: &one.s = 0xbffff270, &one.e = 0xbffff270
vals: one.s = 0x927ff590, one.e = 0xbffff270
adrs: &one.s = 0xbffff270, &one.e = 0xbffff270
vals: one.s = 0x52a0, one.e = 0xbffff270

有人看到one.sone.e的地址相同 - 正如预期的那样 - 但值不是。 这让我很困惑。

5 个答案:

答案 0 :(得分:1)

数组的“值”是第一个元素的地址。您可以在结果中看到。如果你打印:

printf("vals: one.s = 0x%x, one.e[0] = 0x%x\n",one.s,one.e[0]);

然后你应该得到相同的答案。

答案 1 :(得分:1)

one.e是one.e数组的地址。 one.s是one.s.的值。如果你想要e的值你需要访问one.e [0] ... one.e [3]。

答案 2 :(得分:0)

e 的'值'是它的地址

数组的名称只是其地址的标记,因此arrayname&arrayname是相同的东西

答案 3 :(得分:0)

&运算符或sizeof运算符的操作数之外的任何上下文中的数组都会计算其第一个元素的地址。

此外,您的代码充满了未定义的行为,因为您使用%x来打印指针。 %x仅在传递的表达式的(提升的)类型为unsigned int时有效。

答案 4 :(得分:0)

编译器对静态分配的数组的处理方式与float ptr不同。您可以通过使用-S标志进行编译来在gcc中看到这一点。以下是第一个和第二个printf语句的代码:

printf("adrs: &one.s = 0x%x, &one.e = 0x%x\n", &one.s, &one.e);
    movl    $.LC0, %eax
    leaq    -16(%rbp), %rdx
    leaq    -16(%rbp), %rcx
    movq    %rcx, %rsi
    movq    %rax, %rdi
    movl    $0, %eax
    call    printf

printf("vals: one.s = 0x%x, one.e = 0x%x\n",one.s,one.e);
    movq    -16(%rbp), %rcx
    movl    $.LC1, %eax
    leaq    -16(%rbp), %rdx
    movq    %rcx, %rsi
    movq    %rax, %rdi
    movl    $0, %eax
    call    printf

您可以看到第一次调用有两个“加载有效地址”指令(& one.s和& one.e),但第二次printf调用只有一个这样的命令。 “leaq -16(%rbp),%rdx”命令将地址%rbp-16(比%rbp寄存器中的地址存储小2个字节)移动到%rdx寄存器中,然后由printf使用它命令填写one.e的输出。在第一个版本中,重复此命令以将相同的地址加载到%rcx。

在第二个版本中,%rcx寄存器通过“movq -16(%rbp),%rcx”命令填充。与“leaq”不同,“movq”是在内存中为指定地址(%rbp-16)的值执行查找的指令。