不一致的警告:变量可能会被“longjmp”或“vfork”破坏
inconsistent warning: variable might be clobbered by ‘longjmp’ or ‘vfork’
我基本上说服自己遇到了一些 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,则不会被破坏? 关于使用结构体成功编译的其他方法的任何见解,如代码中的注释所示? 或者这指向编译器错误?
任何见解和意见将不胜感激。
setjmp()
保存当前堆栈。由于它是在声明val
之前调用的,因此该变量不会在保存的堆栈中。
setjmp()
之后,变量被初始化,如果代码稍后跳回到setjmp
点,变量将再次初始化,破坏旧变量。如果应该在旧实例上调用一个非平凡的析构函数,则这是未定义的行为 (§18.10/4):
如果将
setjmp
和longjmp
替换为catch
,则setjmp
/longjmp
调用对具有未定义的行为,并且throw
会为任何自动对象调用任何非平凡析构函数。
可能不会调用旧实例的析构函数。我的猜测是 gcc 不会警告基元类型,因为它们没有析构函数,但会警告更复杂的类型,这可能会有问题。
这里有几个因素在起作用:
-
struct
而不是int
- 不使用
memset
(我承认我不明白这如何使事情变得更糟) - 迭代循环两次以上 - 如果只迭代两次,编译器将展开循环
-fPIC
命令行选项(生成与位置无关的代码)
仅当所有这四个因素都存在时,编译器才会生成警告。它们似乎构成了优化器的完美风暴,优化器有一种精神崩溃(见下文)。如果缺少这些因素中的任何一个,编译器只会将所有内容优化为零,因此它可以忽略setjmp
。
这是否是一个错误还有待商榷——代码可能仍然有效(尽管我还没有测试过它)。但无论如何,该问题似乎已在 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
- 将成员变量添加到共享库中的类中,不会破坏二进制兼容性吗
- 看起来is_nothrow_constructible_v()在MSVC中被破坏了,我错了吗
- 未声明的标识符编译暗黑破坏神 2 程序"muleview"
- 为什么返回 NULL 不会破坏函数?
- 为什么Q_OBJECT会破坏QDoc?
- 如何在不破坏现有应用程序的情况下更改 API 中 stl 容器的数据类型?
- 为什么显式模板实例化不会破坏 ODR?
- 插入一个基本的单向链表节点似乎破坏了我的 c++ 代码?
- [longjmp/setjmp]c++ 相同的代码窗口有异常 Linux 没有错误并且运行良好
- 为什么 Glib::VariantBase::store 方法破坏了给定缓冲区的开始
- 为什么 setjmp/longjmp 的这种用法是未定义的行为?
- 在调用其析构函数之前,是否有任何实际理由检查某些东西是否可破坏?
- ostream 运算符<< 为获取 STL 容器而过载,传递 std::string 会破坏它?
- 0 的 2D 数组 不相关循环的破坏逻辑 - C++
- 在等待时破坏condition_variable
- 为什么添加析构函数(甚至是空的)会破坏我的结构,该结构使用 ref 转发和折叠来保存 ref 或值的副本?
- std::p air 会破坏其动态分配的对象吗?
- 为什么std::string在初始值被破坏后仍然有效
- 为什么匿名unique_ptr值会立即被破坏
- 不一致的警告:变量可能会被“longjmp”或“vfork”破坏