具有相同名称但不同定义的结构:带有 -O2 的分段错误
Structure with same name, different definitions: segmentation fault with -O2
当两个 C++ 文件一起编译时,我在 C++ 程序中遇到了分段错误,每个文件都包含不同的结构定义(具有相同的名称)。
根据 ,我了解到结构定义仅限于翻译单元(文件及其包含物)。
但是,在编译时启用 -O1 或更多时我遇到了崩溃。
以下最少的代码重现了段错误。
代码在 3 个简短的 C++ 文件和 2 headers:
// td_collision1.cc
#include <iostream>
#include <vector>
#include <cstdlib>
#include "td1.h"
struct Data
{
long a;
double m1;
double m2;
};
void sz1(void) {
std::cout << "Size of in collision1: " << sizeof(struct Data) << std::endl;
}
void collision1(void) {
struct Data tmp;
std::vector<struct Data> foo;
for (int i=0; i<10; i++) {
tmp.a = 1;
tmp.m1 = 0;
tmp.m2 = 0;
foo.push_back(tmp);
}
}
// td1.h
#include <iostream>
void collision1(void);
void sz1(void);
// td_collision2.cc
#include <iostream>
#include <vector>
#include <cstdlib>
#include "td2.h"
struct Data {
long a;
double m1; // note that there is one member less here
};
void sz2(void) {
std::cout << "Size of in collision2: " << sizeof(struct Data) << std::endl;
}
void collision2(void) {
struct Data tmp2;
std::vector<struct Data> bar;
for (int i=0; i<100; i++) {
tmp2.a = 1;
tmp2.m1 = 0;
bar.push_back(tmp2); // errors occur here
}
}
// td2.h
#include <iostream>
void collision2(void);
void sz2(void);
// td_main.cc
#include <iostream>
#include <cstdlib>
#include "td1.h"
#include "td2.h"
int main(void) {
sz1();
sz2();
collision2();
}
此代码使用带有 -O0 标志的 GCC 6.3 编译,在 valgrind 下运行良好且没有错误。
然而,运行 它与 -O1 或 O2 导致以下输出:
Size of in collision1: 24
Size of in collision2: 16
==326== Invalid write of size 8
==326== at 0x400F6C: construct<Data, const Data&> (new_allocator.h:120)
==326== by 0x400F6C: construct<Data, const Data&> (alloc_traits.h:455)
==326== by 0x400F6C: push_back (stl_vector.h:918)
==326== by 0x400F6C: collision2() (td_collision2.cc:22)
==326== by 0x400FE8: main (td_main.cc:10)
==326== Address 0x5aba1f0 is 0 bytes after a block of size 96 alloc'd
==326== at 0x4C2E1FC: operator new(unsigned long) (vg_replace_malloc.c:334)
==326== by 0x400DE9: allocate (new_allocator.h:104)
==326== by 0x400DE9: allocate (alloc_traits.h:416)
==326== by 0x400DE9: _M_allocate (stl_vector.h:170)
==326== by 0x400DE9: void std::vector<Data, std::allocator<Data> >::_M_emplace_back_aux<Data const&>(Data const&) (vector.tcc:412)
==326== by 0x400F7E: push_back (stl_vector.h:924)
==326== by 0x400F7E: collision2() (td_collision2.cc:22)
==326== by 0x400FE8: main (td_main.cc:10)
==326==
==326== Invalid write of size 8
==326== at 0x400F69: construct<Data, const Data&> (new_allocator.h:120)
==326== by 0x400F69: construct<Data, const Data&> (alloc_traits.h:455)
==326== by 0x400F69: push_back (stl_vector.h:918)
==326== by 0x400F69: collision2() (td_collision2.cc:22)
==326== by 0x400FE8: main (td_main.cc:10)
==326== Address 0x5aba1f8 is 8 bytes after a block of size 96 alloc'd
==326== at 0x4C2E1FC: operator new(unsigned long) (vg_replace_malloc.c:334)
==326== by 0x400DE9: allocate (new_allocator.h:104)
==326== by 0x400DE9: allocate (alloc_traits.h:416)
==326== by 0x400DE9: _M_allocate (stl_vector.h:170)
==326== by 0x400DE9: void std::vector<Data, std::allocator<Data> >::_M_emplace_back_aux<Data const&>(Data const&) (vector.tcc:412)
==326== by 0x400F7E: push_back (stl_vector.h:924)
==326== by 0x400F7E: collision2() (td_collision2.cc:22)
==326== by 0x400FE8: main (td_main.cc:10)
==326==
==326==
==326== HEAP SUMMARY:
==326== in use at exit: 0 bytes in 0 blocks
==326== total heap usage: 5 allocs, 5 frees, 73,896 bytes allocated
==326==
==326== All heap blocks were freed -- no leaks are possible
==326==
==326== For counts of detected and suppressed errors, rerun with: -v
==326== ERROR SUMMARY: 191 errors from 2 contexts (suppressed: 0 from 0)
当 libc 重新分配 std::vector<struct Data> bar
时,push_back()
函数失败。 (在我的例子中,它的大小最初是 4 个项目,然后在循环中调用 push_back() 时进一步调整向量的大小。)
当 td_collision1.cc 中的 struct Data
与 td_collision2.cc 中的大小相同时,程序不会崩溃。
因此,这两个结构定义之间似乎存在冲突。事实上,如果我重命名一个结构,错误显然就消失了。
但是,如上所述,我认为这不可能发生。我误会了什么?
另外,如果我去掉函数 collision1()
,段错误就会消失(collision1 中的 struct Data
可能因为未使用而被编译器丢弃)
我的理解是,这两个 CC 文件之间存在明显的分离,如果 header.
中不存在结构,则 "crosstalk" 应该是不可能的
编辑:添加缺失的 td2.h
好吧,您链接的是 C 答案,但您的问题是关于 C++ 的。两种语言,两种标准,两种答案。
也就是说,我相信 C 答案应该 也 是不允许的,根据单一定义规则(两种语言都有)。违反的是未定义的行为,其中包括分段错误。
来自basic.def.odr,(...被我省略了):
There can be more than one definition of a class type (Clause [class]), ..... Given such an entity named D defined in more than one translation unit, then:
- each definition of D shall consist of the same sequence of tokens; and
- ...
If D is a template and is defined in more than one translation unit, then the preceding requirements shall apply both to names from the template's enclosing scope used in the template definition ([temp.nondep]), and also to dependent names at the point of instantiation ([temp.dep]). If the definitions of D satisfy all these requirements, then the behavior is as if there were a single definition of D. If the definitions of D do not satisfy these requirements, then the behavior is undefined.
在您的程序中,td_collision1.cc
和 td_collision2.cc
中 struct Data
的定义彼此不匹配,因此 struct Data
的定义不满足那些要求,则行为未定义。
你链接的答案是针对C语言的,C不是C++。
在C++中(标准引用自en.cppreference, see ),规则如下:
There can be more than one definition in a program, as long as each definition appears in a different translation unit, of each of the following: class type [...], as long as all of the following is true:
each definition consists of the same sequence of tokens (typically, appears in the same header file)
[...]
If all these requirements are satisfied, the program behaves as if there is only one definition in the entire program. Otherwise, the behavior is undefined.
你的两个定义明显违反了第一个条件,所以行为未定义。
当两个 C++ 文件一起编译时,我在 C++ 程序中遇到了分段错误,每个文件都包含不同的结构定义(具有相同的名称)。
根据
但是,在编译时启用 -O1 或更多时我遇到了崩溃。 以下最少的代码重现了段错误。
代码在 3 个简短的 C++ 文件和 2 headers:
// td_collision1.cc
#include <iostream>
#include <vector>
#include <cstdlib>
#include "td1.h"
struct Data
{
long a;
double m1;
double m2;
};
void sz1(void) {
std::cout << "Size of in collision1: " << sizeof(struct Data) << std::endl;
}
void collision1(void) {
struct Data tmp;
std::vector<struct Data> foo;
for (int i=0; i<10; i++) {
tmp.a = 1;
tmp.m1 = 0;
tmp.m2 = 0;
foo.push_back(tmp);
}
}
// td1.h
#include <iostream>
void collision1(void);
void sz1(void);
// td_collision2.cc
#include <iostream>
#include <vector>
#include <cstdlib>
#include "td2.h"
struct Data {
long a;
double m1; // note that there is one member less here
};
void sz2(void) {
std::cout << "Size of in collision2: " << sizeof(struct Data) << std::endl;
}
void collision2(void) {
struct Data tmp2;
std::vector<struct Data> bar;
for (int i=0; i<100; i++) {
tmp2.a = 1;
tmp2.m1 = 0;
bar.push_back(tmp2); // errors occur here
}
}
// td2.h
#include <iostream>
void collision2(void);
void sz2(void);
// td_main.cc
#include <iostream>
#include <cstdlib>
#include "td1.h"
#include "td2.h"
int main(void) {
sz1();
sz2();
collision2();
}
此代码使用带有 -O0 标志的 GCC 6.3 编译,在 valgrind 下运行良好且没有错误。 然而,运行 它与 -O1 或 O2 导致以下输出:
Size of in collision1: 24
Size of in collision2: 16
==326== Invalid write of size 8
==326== at 0x400F6C: construct<Data, const Data&> (new_allocator.h:120)
==326== by 0x400F6C: construct<Data, const Data&> (alloc_traits.h:455)
==326== by 0x400F6C: push_back (stl_vector.h:918)
==326== by 0x400F6C: collision2() (td_collision2.cc:22)
==326== by 0x400FE8: main (td_main.cc:10)
==326== Address 0x5aba1f0 is 0 bytes after a block of size 96 alloc'd
==326== at 0x4C2E1FC: operator new(unsigned long) (vg_replace_malloc.c:334)
==326== by 0x400DE9: allocate (new_allocator.h:104)
==326== by 0x400DE9: allocate (alloc_traits.h:416)
==326== by 0x400DE9: _M_allocate (stl_vector.h:170)
==326== by 0x400DE9: void std::vector<Data, std::allocator<Data> >::_M_emplace_back_aux<Data const&>(Data const&) (vector.tcc:412)
==326== by 0x400F7E: push_back (stl_vector.h:924)
==326== by 0x400F7E: collision2() (td_collision2.cc:22)
==326== by 0x400FE8: main (td_main.cc:10)
==326==
==326== Invalid write of size 8
==326== at 0x400F69: construct<Data, const Data&> (new_allocator.h:120)
==326== by 0x400F69: construct<Data, const Data&> (alloc_traits.h:455)
==326== by 0x400F69: push_back (stl_vector.h:918)
==326== by 0x400F69: collision2() (td_collision2.cc:22)
==326== by 0x400FE8: main (td_main.cc:10)
==326== Address 0x5aba1f8 is 8 bytes after a block of size 96 alloc'd
==326== at 0x4C2E1FC: operator new(unsigned long) (vg_replace_malloc.c:334)
==326== by 0x400DE9: allocate (new_allocator.h:104)
==326== by 0x400DE9: allocate (alloc_traits.h:416)
==326== by 0x400DE9: _M_allocate (stl_vector.h:170)
==326== by 0x400DE9: void std::vector<Data, std::allocator<Data> >::_M_emplace_back_aux<Data const&>(Data const&) (vector.tcc:412)
==326== by 0x400F7E: push_back (stl_vector.h:924)
==326== by 0x400F7E: collision2() (td_collision2.cc:22)
==326== by 0x400FE8: main (td_main.cc:10)
==326==
==326==
==326== HEAP SUMMARY:
==326== in use at exit: 0 bytes in 0 blocks
==326== total heap usage: 5 allocs, 5 frees, 73,896 bytes allocated
==326==
==326== All heap blocks were freed -- no leaks are possible
==326==
==326== For counts of detected and suppressed errors, rerun with: -v
==326== ERROR SUMMARY: 191 errors from 2 contexts (suppressed: 0 from 0)
当 libc 重新分配 std::vector<struct Data> bar
时,push_back()
函数失败。 (在我的例子中,它的大小最初是 4 个项目,然后在循环中调用 push_back() 时进一步调整向量的大小。)
当 td_collision1.cc 中的 struct Data
与 td_collision2.cc 中的大小相同时,程序不会崩溃。
因此,这两个结构定义之间似乎存在冲突。事实上,如果我重命名一个结构,错误显然就消失了。
但是,如上所述,我认为这不可能发生。我误会了什么?
另外,如果我去掉函数 collision1()
,段错误就会消失(collision1 中的 struct Data
可能因为未使用而被编译器丢弃)
我的理解是,这两个 CC 文件之间存在明显的分离,如果 header.
中不存在结构,则 "crosstalk" 应该是不可能的编辑:添加缺失的 td2.h
好吧,您链接的是 C 答案,但您的问题是关于 C++ 的。两种语言,两种标准,两种答案。
也就是说,我相信 C 答案应该 也 是不允许的,根据单一定义规则(两种语言都有)。违反的是未定义的行为,其中包括分段错误。
来自basic.def.odr,(...被我省略了):
There can be more than one definition of a class type (Clause [class]), ..... Given such an entity named D defined in more than one translation unit, then:
- each definition of D shall consist of the same sequence of tokens; and
- ...
If D is a template and is defined in more than one translation unit, then the preceding requirements shall apply both to names from the template's enclosing scope used in the template definition ([temp.nondep]), and also to dependent names at the point of instantiation ([temp.dep]). If the definitions of D satisfy all these requirements, then the behavior is as if there were a single definition of D. If the definitions of D do not satisfy these requirements, then the behavior is undefined.
在您的程序中,td_collision1.cc
和 td_collision2.cc
中 struct Data
的定义彼此不匹配,因此 struct Data
的定义不满足那些要求,则行为未定义。
你链接的答案是针对C语言的,C不是C++。
在C++中(标准引用自en.cppreference, see
There can be more than one definition in a program, as long as each definition appears in a different translation unit, of each of the following: class type [...], as long as all of the following is true:
each definition consists of the same sequence of tokens (typically, appears in the same header file)
[...]
If all these requirements are satisfied, the program behaves as if there is only one definition in the entire program. Otherwise, the behavior is undefined.
你的两个定义明显违反了第一个条件,所以行为未定义。