但是a本身并没有指向另一个内存区域,它就是内存区域本身。因此,当编译器将其转换为指针时,它会将其保存在内存中的某个位置,还是隐式转换呢?
从逻辑上讲,这是一种隐式转换 - 实现没有要求为指针实现永久储存空间。
在实现方面,这取决于编译器。例如,下面是一个简单的代码片段,创建一个数组并打印其地址:
#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
变量 - 它全部用当前帧指针的偏移量表示。
第21行是转换发生的地方 - 我们将数组的第一个元素的有效地址(即存储在%rbp
寄存器减去12的地址)加载到%rax
寄存器中。然后将该值(以及格式字符串的地址)传递给printf
。请注意,这种转换的结果除了寄存器外没有存储在任何其他地方,因此下一次写入%rax
时它将被覆盖 - 换句话说,没有像为数组内容设置存储空间那样为其设置永久存储空间。
同样,这就是Red Hat上运行的x86-64上的gcc
的方式。在不同体系结构上的不同编译器将以不同的方式执行。
int *p = a
之后,p
指向数组a
的第一个元素。如果您仅编写a
,则a
会衰减为指向a
的第一个元素的指针。 - Jabberwockya
没有被存储。这只是编译器的“技巧”。a
只是&a[0]
的简写。 - Jabberwocky1
是一个int
值,但它没有地址。&i
是一个指针值,但它没有地址。数组名的衰减会产生一个指针值,但这个指针值没有地址。 - Ian Abbott