更正答案
不,它不默认为 0。这是未定义的行为。在这种情况下,这种优化和这种编译器恰好是 0。尝试访问未初始化或未分配的内存是未定义的行为。
因为它实际上是“未定义的”并且标准对此无话可说,所以您的程序集输出不会是一致的。编译器可能会将数组存储在 SIMD 寄存器中,谁知道输出会是什么?
引用示例答案:
并且第四个循环打印默认数组值零,因为没有为元素 3 初始化任何内容
这是有史以来最错误的说法。我猜代码中有错字,他们想写
int x[4] = {120, 200, 16};
并误将其x[4]
变为just 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:
阅读代码中的注释以获得解释。
所以,你可以看到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++ 来提高可读性。