为什么变量可以在没有声明和定义的情况下被初始化(和使用)"being run"?

Why can variables be initialized(and used) without its declaration and definition "being run"?

C++ 不允许“转到定义上:”

goto jumpover;
int something = 3;
jumpover:
std::cout << something << std::endl;

这将按预期引发错误,因为“某物”不会被声明(或定义)。

不过,我是用汇编代码跳过的:

#include<iostream>
using namespace std;
int main(){
    asm("\njmp tag\n");
    int ptr=9000;//jumped over
    cout << "Ran" << endl;
    asm("\ntag:\n");
    cout << ptr << endl;
    return 0;
}

它打印了 9000,虽然 int ptr=9000;//jumped over 行没有被执行,因为程序没有打印 Ran。我预计它会在使用 ptr 时导致内存 corruption/undefined 值,因为内存未分配(尽管编译器认为它是,因为它不理解 ASM)。它怎么知道ptr是9000?

这是否意味着 ptr 是在 main() 的开头创建和分配的(因此没有跳过,由于某些优化或其他原因)或其他原因?

GCC 不支持在 asm() 语句之间跳转;
你的代码有未定义的行为。
从字面上看,任何事情都是允许发生的。

它后面没有 __builtin_unreachable(),您甚至没有使用 asm goto("" ::: : "label") (GCC manual) 来告诉它有关 asm 语句可能会或可能不会跳转到的 C 标签.

无论您在实践中使用不同版本的 gcc/clang 和不同的优化级别发生什么,都是巧合/实现细节/优化器实际执行的结果。

例如,启用优化后,它会 constant-propagation 假设会到达 int ptr=9000; 语句,因为它允许假设执行是在第一个 asm 语句的结尾。

您必须查看编译器的完整 asm 输出才能了解实际发生的情况。例如https://godbolt.org/z/MbGhEnK3b 显示 GCC -O0 和 -O2。使用 -O0 你确实可以读取未初始化的堆栈 space 因为它跳过 mov DWORD PTR [rbp-4], 9000,使用 -O2 你可以得到 constant-propagation: mov esi, 9000call std::basic_ostream<char,... 运算符 <<(int) 重载之前。

because the memory isn't allocated

Space因为它实际上在函数序言中分配的;编译器不会在每次遇到范围内的声明时生成代码来移动堆栈指针。他们在函数开始时分配一次 space 。即使 one-pass Tiny C 编译器也是这样工作的,而不是使用单独的 push 来分配和初始化单独的 int 变量。 (在某些情况下,这实际上是一个错过的优化,当 push 用于在一条指令中分配 + init 时:


甚至比大多数其他类型的 C 未定义行为更重要,这是 而不是 编译器实际上可以在 run-time 检测到的东西来警告你。 asm 语句只是将文本插入到 GCC 的 asm 输出中,该输出被馈送到汇编器。您需要向编译器准确描述 asm 的作用(使用约束和 asm goto 之类的东西),以便为编译器提供足够的信息来围绕您的 asm 语句生成正确的代码。

GCC 解析 asm 模板中的指令,它只是将其直接复制到 asm 输出。 (或者对于扩展 asm,用根据操作数约束生成的文本替换 %0%1 等操作数。)