但是 a 本身并不指向另一个内存区域,它是内存区域本身。那么当编译器将其转换为指针时,它是把它(如 p)保存在内存中的某处还是隐式转换?
从逻辑上讲,这是一种隐式转换 - 不要求实现实现指针的永久存储。
在实现方面,这取决于编译器。例如,下面是一段简单的代码,它创建了一个数组并打印了它的地址:
#include <stdio.h>
int main( void )
{
int arr[] = { 1, 2, 3 };
printf( "%p", (void *) arr );
return 0;
}
当我在 Red Hat 系统上使用 gcc 为 x86-64 编译它时,我得到以下机器代码:
GAS LISTING /tmp/ccKF3mdz.s page 1
1 .file "arr.c"
2 .text
3 .section .rodata
4 .LC0:
5 0000 257000 .string "%p"
6 .text
7 .globl main
9 main:
10 .LFB0:
11 .cfi_startproc
12 0000 55 pushq %rbp
13 .cfi_def_cfa_offset 16
14 .cfi_offset 6, -16
15 0001 4889E5 movq %rsp, %rbp
16 .cfi_def_cfa_register 6
17 0004 4883EC10 subq $16, %rsp
18 0008 C745F401 movl $1, -12(%rbp)
18 000000
19 000f C745F802 movl $2, -8(%rbp)
19 000000
20 0016 C745FC03 movl $3, -4(%rbp)
20 000000
21 001d 488D45F4 leaq -12(%rbp), %rax
22 0021 4889C6 movq %rax, %rsi
23 0024 BF000000 movl $.LC0, %edi
23 00
24 0029 B8000000 movl $0, %eax
24 00
25 002e E8000000 call printf
25 00
26 0033 B8000000 movl $0, %eax
26 00
27 0038 C9 leave
28 .cfi_def_cfa 7, 8
29 0039 C3 ret
30 .cfi_endproc
31 .LFE0:
33 .ident "GCC: (GNU) 7.3.1 20180712 (Red Hat 7.3.1-6)"
34 .section .note.GNU-stack,"",@progbits
第 17 行通过从堆栈指针中减去 16 为数组分配空间(是的,数组中只有 3 个元素,应该只需要 12 个字节 - 我会让更熟悉 x86_64 架构的人解释原因,因为我会弄错)。
第 18、19 和 20 行初始化数组的内容。请注意,机器代码中没有 arr 变量 - 这一切都是根据当前帧指针的 offset 来完成的。
第 21 行是发生转换的地方 - 我们将数组第一个元素的有效地址(即存储在 %rbp 寄存器中的地址减去 12)加载到 %rax 寄存器中。然后将该值(连同格式字符串的地址)传递给printf。请注意,此转换的结果不会存储在寄存器以外的任何位置,因此下次写入 %rax 时它将丢失 - IOW,没有为其预留永久存储空间,就像设置存储空间一样除了数组内容。
再一次,这就是在 x86-64 上运行的 Red Hat 中的 gcc 的做法。不同架构上的不同编译器会做不同的事情。