在变量定义之前转到 - 其值会发生什么
Goto prior to a variable definition - what happens with its value?
这是我想知道的一些问题。给定以下代码,我们能确定它的输出吗?
void f() {
int i = 0;
z: if(i == 1) goto x; else goto u;
int a;
x: if(a == 10) goto y;
u: a = 10; i = 1; goto z;
y: std::cout << "finished: " << a;
}
这是否保证根据C++标准输出finished: 10
?或者编译器可以在goto
a
之前的位置时占用存储a
的寄存器?
6.7/3 说
从局部变量具有 自动存储持续时间不在范围内,无法达到它所在的点 除非变量具有 POD 类型 (3.9( 并且 声明时不带初始值设定项 (8.5(。
所以这应该没问题。
然后在 6.6/2 中:
从作用域退出时(无论如何完成(,析构函数 (12.4( 为 调用具有自动存储持续时间的所有构造对象 (3.7.2((命名对象或临时对象(中声明的 范围,与其声明的顺序相反。
现在这对我来说意味着,当你跳回z
时,a
是烤面包,你不能保证 a
的无初始值设定项声明在第二次执行时的行为方式。
见6.7/2:
具有自动存储持续时间 (3.7.2( 的变量将分别初始化 执行声明语句的时间。具有自动功能的变量 块中声明的存储持续时间在退出时销毁 块 (6.6(。
所以在我看来,不能保证你会得到 10 个,尽管似乎很难想象
注意:请先阅读对此的评论。约翰内斯或多或少地用一个恰当的标准引用来驳斥我的整个论点。;-)
我没有可用的C++标准,所以我必须从C标准进行推断。
令人惊讶的是(对我来说(,第 6.2.1 章标识符的范围没有说明标识符的范围从其声明点开始(正如我所猜测的那样(。 在您的示例中,int a
具有块范围,它"在关联块的末尾终止",这就是关于它的全部内容。第 6.8.6.1 章 goto 语句说">goto 语句不得从具有可变修改类型的标识符的范围之外跳转到该标识符的范围内"——但由于您的goto
仅在块内跳转(因此,int a
的范围(,就 ISO/IEC 9899:1999 而言,这似乎是可以的。
我对此感到非常惊讶...
编辑#1:稍后快速谷歌,我得到了C++0x最终草案。我认为,相关声明是这样的(6.7声明声明,突出我的声明(:
可以转移到一个块中,但不能以 通过初始化绕过声明。 从具有自动变量的点跳转的程序 存储持续时间不在范围内,以至于它在范围内 格式不正确,除非变量具有标量类型,类类型具有 一个普通的默认构造函数和一个普通析构函数,一个符合 CV 条件的构造函数 这些类型之一的版本,或其中一种类型的数组 前面的类型,声明时不带初始值设定项。
我认为按照标准的标准,您的代码还可以。但是屁股丑陋,请注意。;-)
编辑#2:阅读您关于由于向后跳转而可能破坏int a
的评论,我发现了这个(6.6 跳转语句,突出显示我的(:
转出循环、转出块或返回初始化变量 具有自动存储持续时间涉及破坏对象 在传输点在范围内的自动存储持续时间,但 不在转移到的点。
第一,int a
不是"初始化"的,如果我正确理解标准术语,它就不是一个对象。
不允许放弃变量定义。这应该是一个错误。
是否保证按照C++标准输出finished: 10
?
我认为是的,它必须!
为什么?因为a
从它的声明一直存在到它的作用域结束(函数的结束(,并且根据定义,它只能初始化一次,从那里开始保留其值,直到函数结束时的销毁。
为什么不用汇编来确定呢?
结论:局部变量只会声明一次;但是,每次都会执行赋值。
理由如下。
<小时 />我简化了问题:
主.cpp:
int f() {
//asm(";start f()");
//asm(";begin: ");
begin:
int i = 0;
if (i == 0)
{
i++;
goto begin;
}
//asm(";after goto");
return i + 2;
}
int main()
{
//asm(";before f()");
int i = f();
//asm(";after f()");
return i;
}
然后我把它编译成Linux上的汇编(asm
未注释(如下:
g++ -std=c++20 -I/usr/include/c++/11 -I/usr/include/x86_64-linux-gnu/c++/11 -S main.cpp
其中输出 main.s(main.cpp 的程序集输出(。这是相关部分;我添加了一堆破折号以使其更明显:
#APP
# 2 "main.cpp" 1
-----------------------------------;start f()
# 0 "" 2
# 4 "main.cpp" 1
-----------------------------------;begin:
# 0 "" 2
#NO_APP
.L2:
movl $0, -4(%rbp)
cmpl $0, -4(%rbp)
jne .L3
addl $1, -4(%rbp)
jmp .L2
.L3:
#APP
# 13 "main.cpp" 1
-----------------------------------;after goto
# 0 "" 2
#NO_APP
movl -4(%rbp), %eax
addl $2, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size _Z1fv, .-_Z1fv
.globl main
.type main, @function
main:
.LFB1:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
#APP
# 19 "main.cpp" 1
-----------------------------------;before f()
# 0 "" 2
#NO_APP
call _Z1fv
movl %eax, -4(%rbp)
#APP
# 21 "main.cpp" 1
-----------------------------------;after f()
然后,我编译了main.cpp使用:
g++ -std=c++20 -I/usr/include/c++/11 -I/usr/include/x86_64-linux-gnu/c++/11 main.cpp -omain
它编译得很好。然后我用:
./main
它陷入了一个无限循环。
- #定义c-预处理器常量..我做错了什么
- 当类在C++中定义时,有什么方法可以"register"类吗?
- 有没有什么方法可以使用一个函数中定义的常量变量,也可以由c++中同一程序中的其他函数使用
- 在C/C++中将变量名定义为__00000001有什么好处吗
- 在 ubuntu3 上C++ goto 定义有什么解决方案吗16.04?
- C++中"dependent name"的定义是什么?
- 什么是自定义比较器以及如何在 C++ 的排序函数中使用它?
- Qt - QVector 和模型视图 - 从列表视图获取自定义类的最佳方法是什么?
- 这个失败的测试是将零添加到空指针未定义的行为、编译器错误还是其他什么?
- 在模板类之外定义友元函数的正确方法是什么?
- 在自定义 std::vector-like 容器中处理指针和非指针模板类型的最佳方法是什么?
- 具有相同特征的两个对象是否只在内存中存储一次?无论定义它们的函数是什么,都是不同的
- 自定义数据结构的优点是什么?
- [未定义提及'SetLastError@4']是什么意思?
- 在C++中,创建'n'数量的对象的推荐方法是什么,其中n是用户定义的。我该怎么做?
- 为了从自定义目录使用 CMake,我需要做什么?
- 此模板定义在 C++ 中是什么意思?
- 根据 c++ 标准在该宏定义中推送/弹出宏时的行为是什么
- 有什么方法可以在标头中定义和声明 extern 对象吗?
- 我可以在可移植库中使用什么定义来判断编译器是否Microsoft?