【发布时间】:2016-08-06 02:33:32
【问题描述】:
我正在研究在 OSX 上使用的 x86_64 的调用约定,并且正在阅读 the System V x86-64 ABI standard 中名为“聚合和联合”的部分。它提到了数组,我认为这就像一个固定长度的 c 数组,例如int[5].
我继续阅读“3.2.3 参数传递”以了解数组是如何传递的,如果我理解正确,应该在寄存器中传递像 uint8_t[3] 这样的东西,因为它小于规则强加的四个八字节限制1 聚合类型的分类(第 18 页靠近底部)。
编译后我看到它被作为指针传递。 (我在 OSX 10.11.6 上使用 Xcode 7.3.1 中的 clang-703.0.31 进行编译)。
我用来编译的示例源码如下:
#include <stdio.h>
#define type char
extern void doit(const type[3]);
extern void doitt(const type[5]);
extern void doittt(const type[16]);
extern void doitttt(const type[32]);
extern void doittttt(const type[40]);
int main(int argc, const char *argv[]) {
const char a[3] = { 1, 2, 3 };
const char b[5] = { 1, 2, 3, 4, 5 };
const char c[16] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 1, 1, 1, 1, 1 };
const char d[32] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 1, 1, 1, 1, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 1, 1, 1, 1, 1 };
const char e[40] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 1, 1, 1, 1, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
doit(a);
doitt(b);
doittt(c);
doitttt(d);
doittttt(e);
}
我将它转储到一个名为 a.c 的文件中,并使用以下命令进行编译:clang -c a.c -o a.o。我使用 otool 分析生成的程序集(通过运行otool -tV a.o)并得到以下输出:
a.o:
(__TEXT,__text) section
_main:
0000000000000000 pushq %rbp
0000000000000001 movq %rsp, %rbp
0000000000000004 subq $0x10, %rsp
0000000000000008 leaq _main.a(%rip), %rax
000000000000000f movl %edi, -0x4(%rbp)
0000000000000012 movq %rsi, -0x10(%rbp)
0000000000000016 movq %rax, %rdi
0000000000000019 callq _doit
000000000000001e leaq _main.b(%rip), %rdi
0000000000000025 callq _doitt
000000000000002a leaq _main.c(%rip), %rdi
0000000000000031 callq _doittt
0000000000000036 leaq _main.d(%rip), %rdi
000000000000003d callq _doitttt
0000000000000042 leaq _main.e(%rip), %rdi
0000000000000049 callq _doittttt
000000000000004e xorl %eax, %eax
0000000000000050 addq $0x10, %rsp
0000000000000054 popq %rbp
0000000000000055 retq
或者等效地,这里是Godbolt compiler explorer with clang3.7,它针对使用相同 ABI 的 Linux。
所以,我想知道是否有人可以引导我了解 C11 中的哪些数据类型适用于数组。 (看起来 clang 默认使用 C11 - 请参阅 C99 内联函数下方的简介 here)。
我也对 ARM 进行了类似的调查,发现了类似的结果,尽管 ARM standard 也指定存在数组聚合类型。
另外,在某些标准中是否有规定将固定长度的数组视为指针?
【问题讨论】:
-
@PeterCordes:在大多数但不是所有上下文中,数组都会衰减为指针。更重要的是,C 不允许数组类型的参数。在像
void func(int param[]);这样的声明中,param的类型从int[]到int*被调整。 (这与将数组表达式隐式转换为指针的规则不同。) -
@PeterCordes 那么在 c11 或 c99 中是否没有数据类型被视为 x86_64/arm 标准中定义的数组?
-
@KeithThompson 你有关于 C 不允许数组类型参数的参考吗?我很想阅读更多内容!
-
@DanZimm:C 当然有数组,我认为它们对应于 x64_64/arm 标准中定义的“数组”。参考:N1570 第 6.7.6.3 节第 7 段。
-
@PeterCordes:“衰减”是一个常用术语,尽管标准没有这么称呼它;它只是说它是“转换的”。请注意,衰变并非特定于函数参数。除了 3 个上下文(
sizeof、&和用于初始化数组对象的初始化程序中的字符串文字),数组表达式被隐式转换为指针;参数传递恰好是发生转换的上下文之一。
标签: c assembly types x86-64 calling-convention