更正答案
不,它不默认为 0。这是未定义的行为。在这种情况下,这种优化和这种编译器恰好是 0。尝试访问未初始化或未分配的内存是未定义的行为。
因为它实际上是“未定义的”,并且标准对此没有其他要说的,所以您的程序集输出不会是一致的。编译器可能会将数组存储在 SIMD 寄存器中,谁知道输出会是什么?
引用示例答案:
第四个循环打印默认数组值零,因为没有为元素 3 初始化任何内容
这是有史以来最错误的说法。我猜代码中有错字,他们想写出来
int x[4] = {120, 200, 16};
并错误地将 x[4] 变成了 x[]。如果不是,而且是故意的,我不知道该说什么。他们错了。
为什么不是错误?
这不是错误,因为堆栈就是这样工作的。您的应用程序不需要在堆栈中分配内存来使用它,它已经是您的了。你可以随心所欲地用你的堆栈做任何事情。当你像这样声明一个变量时:
int a;
您所做的只是告诉编译器,“我希望堆栈的 4 个字节用于 a,请不要将这些内存用于其他任何事情。”在编译时。看这段代码:
#include <stdio.h>
int main() {
int a;
}
组装:
.file "temp.c"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6 /* Init stack and stuff */
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret /* Pop the stack and return? Yes. It generated literally no code.
All this just makes a stack, pops it and returns. Nothing. */
.cfi_endproc /* Stuff after this is system info, and other stuff
we're not interested. */
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 11.1.0-1ubuntu1~20.04) 11.1.0"
.section .note.GNU-stack,"",@progbits
.section .note.gnu.property,"a"
.align 8
.long 1f - 0f
.long 4f - 1f
.long 5
0:
.string "GNU"
1:
.align 8
.long 0xc0000002
.long 3f - 2f
2:
.long 0x3
3:
.align 8
4:
阅读代码中的cmets进行解释。
所以,你可以看到int x; 什么都不做。如果我打开优化,编译器甚至不会费心制作堆栈并做所有这些事情,而是直接返回。 int x; 只是一个编译时命令给编译器说:
x 是一个带符号整数的变量。它需要4个字节,请跳过这4个字节(和对齐)后继续声明。
(堆栈的)高级语言中的变量的存在只是为了使堆栈的“分布”更加系统化并且以一种可读的方式存在。变量的声明不是运行时过程。它只是教编译器如何在变量之间分配堆栈并相应地准备程序。执行时,程序分配一个堆栈(这是一个运行时进程),但它已经硬编码了哪些变量获得了堆栈的哪个部分。例如。变量a 可能将-0(%rbp) 变为-4(%rbp),而b 将-5(%rbp) 变为-8(%rbp)。这些值是在编译时确定的。变量的名称在编译时也不存在,它们只是教编译器如何准备程序以使用其堆栈的一种方式。
您作为用户可以随意使用堆栈;但你可能不会。您应该始终声明变量或数组以让编译器知道。
边界检查
在像 Go 这样的语言中,即使您的堆栈是您的,编译器也会插入额外的检查以确保您不会意外使用未声明的内存。出于性能原因,在 C 和 C++ 中没有这样做,它会导致可怕的未定义行为和分段错误更频繁地发生。
堆和数据部分
堆是存储大数据的地方。这里没有存储变量,只有数据;并且您的一个或多个变量将包含指向该数据的指针。如果您使用尚未分配的内容(在运行时完成),则会出现分段错误。
数据部分是另一个可以存储东西的地方。变量可以存储在这里。它与您的代码一起存储,因此超出分配非常危险,因为您可能会不小心修改程序的代码。由于它与您的代码一起存储,因此显然也是在编译时分配的。我实际上对数据部分的内存安全知之甚少。显然,您可以在操作系统不抱怨的情况下超过它,但我不知道更多,因为我不是系统黑客,也没有可疑的目的将其用于恶意意图。基本上,我不知道在数据部分超出分配。希望有人对此发表评论(或回答)。
上面显示的所有程序集都是在 Ubuntu 机器上由 GCC 11.1 编译的 C。它使用 C 而不是 C++ 来提高可读性。