我大部分时间都相信我遇到了一些g ++ 4.8.3错误,但我想我会先问这个列表,因为我对setjmp / longjmp的经验很少。我将我的代码简化为以下foo.cxx:
#include <setjmp.h>
#include <string.h>
// Changing MyStruct to be just a single int makes the compiler happy.
struct MyStruct
{
int a;
int b;
};
// Setting MyType to int makes the compiler happy.
#ifdef USE_STRUCT
typedef MyStruct MyType;
#elif USE_INT
typedef int MyType;
#endif
void SomeFunc(MyType val)
{
}
static void static_func(MyType val)
{
SomeFunc(val);
}
int main(int argc, char **argv)
{
jmp_buf env;
if (setjmp(env))
{
return 1;
}
MyType val;
#ifdef USE_STRUCT
val.a = val.b = 0;
#elif USE_INT
val = 0;
#endif
// Enabling the below memset call makes the compiler happy.
//memset(&val, 0, sizeof(val));
// Iterating 1 or 2 times makes the compiler happy.
for (unsigned i = 0; i < 3; i++)
{
// calling SomeFunc() directly makes the compiler happy.
static_func(val);
}
return 0;
}
我使用g ++ 4.8.3编译此代码。对我来说有趣的是,当我定义USE_STRUCT时,编译失败但成功使用USE_INT。代码中有注释进一步表明如何使用USE_STRUCT使编译成功。编译仅在g ++的-fPIC选项中失败,但这是我环境中的必需参数。
要查看编译错误:
g++ -DUSE_STRUCT -Wextra -Wno-unused-parameter -O3 -Werror -fPIC foo.cxx
foo.cxx: In function ‘int main(int, char**)’:
foo.cxx:26:5: error: variable ‘val’ might be clobbered by ‘longjmp’ or ‘vfork’ [-Werror=clobbered]
但是使用简单的int就可以了:
g++ -DUSE_INT -Wextra -Wno-unused-parameter -O3 -Werror -fPIC foo.cxx
有人可以向我解释为什么val可能会被破坏,如果它是一个结构但不是如果它是一个int?如编码中的注释所示,对结构进行编译的其他方法的任何见解都会成功吗?或者这是否指向编译器错误?
非常感谢任何见解和评论。
答案 0 :(得分:3)
setjmp()
保存当前堆栈。由于它是在val
声明之前调用的,因此该变量不在保存的堆栈中。
在setjmp()
之后,变量被初始化,如果代码稍后跳回setjmp
点,变量将再次初始化,破坏旧变量。如果应该在旧实例上调用一个非平凡的析构函数,那么这是未定义的行为(§18.10/ 4):
setjmp
/longjmp
调用对具有未定义的行为,如果将setjmp
和longjmp
替换为catch
,throw
会调用任何非 - 任何自动对象的通用析构函数。
可能不会调用旧实例的析构函数。我的猜测是gcc不会警告原始类型,因为它们没有析构函数,但警告更复杂的类型,这可能会有问题。
答案 1 :(得分:0)
这里有几个因素在起作用:
struct
代替int
memset
(我承认我不明白这会让事情变得更糟)-fPIC
命令行选项(这会生成与位置无关的代码)只有当所有这四个因素都存在时,编译器才会发出警告。对于优化者而言,它们似乎构成了一场完美的风暴,它有一种精神崩溃(见下文)。如果没有这些因素中的任何一个,编译器只会将所有内容优化为零,所以它可以忽略setjmp
。
这是否是一个bug是值得怀疑的 - 代码可能仍然有用(尽管我还没有测试过)。但无论如何,问题似乎已在4.9版本中修复,因此显而易见的解决方案是升级。
这是机器代码(NSFW):
SomeFunc(MyStruct):
rep; ret
main:
pushq %r12
pushq %rbp
pushq %rbx
subq $224, %rsp
leaq 16(%rsp), %rdi
call _setjmp@PLT
testl %eax, %eax
movl %eax, %ebp
jne .L5
movl $3, %ebx
movabsq $-4294967296, %r12
.L4:
movq 8(%rsp), %rdx
andq %r12, %rdx
movl %edx, %eax
movq %rax, %rdi
movq %rax, 8(%rsp)
call SomeFunc(MyStruct)@PLT
subl $1, %ebx
jne .L4
.L3:
addq $224, %rsp
movl %ebp, %eax
popq %rbx
popq %rbp
popq %r12
ret
.L5:
movl $1, %ebp
jmp .L3