为什么变量可以在没有声明和定义的情况下被初始化(和使用)"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, 9000
在 call 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
等操作数。)
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, 9000
在 call 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
等操作数。)