当你这样做时,编译器会做什么?
struct something {
int number;
}
它在内存上与 int 有什么不同吗?
C 编译器是否使用单个成员优化结构?
这完全取决于编译器。以及当前的编译器设置。以及正在编译的代码。还有平台。以及天狼星和月球的相对位置。我们不知道。如果您询问特定的编译器,在特定的架构上,打开特定的编译器标志,当编译一段给定的代码时,我们可以编译它并查看生成的程序集以了解它的作用。但是,你也可以为自己做这件事。
也就是说,一般来说,我希望现代优化编译器在打开相当高水平的优化时进行这种转换。
编辑:至于可能存在什么样的差异:我不仅在谈论结构内成员的偏移量;正如其他人正确观察到的那样,在访问此类结构的成员以及在独立的int
.
但是,还有其他与结构相关的属性可能会有所不同。我可以想到两个例子。
第一个与分配有关。当您分配给 astruct
时,某些编译器将发出对该memcpy()
函数的调用,而对 an 的分配int
可能只需要一条机器指令而无需任何函数调用,例如mov [address], [value]
.
第二个行为差异与函数返回值有关。一些(旧)ABI 规定返回struct
push 的函数将其返回值放在堆栈上,而简单的原始返回值(即内置的核心语言类型)通常放置在寄存器中。因此,对于非优化编译器,理论上可以生成一条push
将单曲放入堆栈的指令int
,而不是使用返回值寄存器。
“优化”是什么意思?或者,更具体地说,在这种情况下,“未优化”的机器代码会是什么样子?
处理包含单个变量的结构的机器语义与独立变量的机器语义int
相同。int
这意味着无论编译器是否进行任何优化,机器代码通常都是相同的。即这样的结构本质上已经与独立int
变量相同。
换句话说,我不明白在这种情况下您会看到什么“未优化”代码。“非优化”的自由在哪里呢?
一个“非优化”机会可能是 struct 大小对齐,由于某种模糊的原因,它最终可能会大于 Standalone 的大小int
。但是,如果您没有明确要求,这通常不会在实践中发生。
我还可以想象一个编译器会不断尝试添加0
到结构的起始地址以获取唯一字段的地址。但是我从来没有见过一个编译器会在实践中做这样的事情。
要回答您的新问题:
它在内存上与 int 有什么不同吗?
在实践中,答案是否定的。允许实现对结构施加比对 plain 更高的对齐要求int
,因此也可以扩展结构的大小以匹配对齐。但是,这样做没有任何用处,而且我不知道有任何编译器这样做。(当然,所有主要 cpu 架构和操作系统的标准 ABI 都不允许这样做。)
至于其他方面:
编译器为结构类型的赋值生成效率较低的代码是可能的,但不太可能。正如 H2CO3 所提到的,它可能总是会生成memcpy
对结构分配的调用,即使单个单词加载/存储也会这样做。
没有人涉及的一个方面是调用约定中的参数传递和返回值。编写接受或返回结构类型作为参数或返回值的函数是很少见的(而且通常是不受欢迎的);通常,改为使用指针。但是在 C 中是允许的,并且用于传递或返回包含单个结构的调用约定是否int
匹配 type 对象的约定int
,或者使用其他一些泛化到更大结构的机制,是特定于架构/ABI 的。
尽管问题提出了一个包含单个 的int
结构,但包含单个浮点变量的结构更有趣。许多处理器都有一个浮点单元 (FPU),它在某种程度上与 CPU 的其余部分隔离。通常,主 CPU 将能够从内存中加载和存储其寄存器,FPU 也可以使用其寄存器,但在主 CPU 的寄存器和 FPU 的寄存器之间没有直接路径。
通常,在调用函数时,将参数和函数返回值传递到寄存器中会比让调用者将它们存储在内存中并让被调用函数读出它们更快。然而,使用主 CPU 寄存器来传递浮点参数和返回值可能是最糟糕的机制:如果像double foo(double x) {return x*2.0}
; 被调用y=foo(w+1.0)+3.0;
时,系统必须加载w
到 FPU 寄存器中,添加 1.0,将其存储到内存中,将其加载到 CPU 寄存器中并调用foo
,然后必须将其存储到内存中,以便将其加载到 FPU 中。然后它将乘以 2.0,将其存储回内存中,将其加载到 CPU 寄存器中,然后返回,因此主代码可以将其存储到内存中,将其加载到 FPU,加上 3.0,最后将其存储到 Y。讨厌。
如果使用 FPU 寄存器传递浮点参数,性能会更好。然而,一般来说,这仅适用于浮点基本类型。一个包含单个浮点变量的结构将在我所知道的每个系统上使用主 CPU 寄存器或内存进行传递。在调用者和/或被调用代码必须对值执行浮点数学运算的情况下,这可能是一件坏事,但在极少数情况下,这可能是一件好事,尤其是在主 CPU 必须做某事的情况下有问题的价值。例如,可以通过将值加载到主 CPU 寄存器并让它执行比较和交换来使用 4 字节浮点数进行比较和交换操作,但是在 FPU 寄存器中获取值的比较和交换例程会比在内存或主 CPU 寄存器中获取参数的例程要慢一些。我不知道性能优势是否足以证明使用结构的合理性,但了解它们可能会很好。