环境:
ubuntu 20.04
g++ gcc version 9.4.0
1.问题
这里有个更简练的说明,觉得挺好的,附上:
遵循 One Definition Rule - C/C++ 安全规则
开发时遇到一个奇怪的问题,调用某个结构体变量的函数,没有得到预期的结果。但是,每个数据成员的值又是对的。
增加日志打印后,最终发现调用的是另一个同名结构体的同名函数。也就是发生了 ODR (One Definition Rule) 违规问题。
在这里用最小代码,复现下这个问题。
a.h:
#pragma once
#include <iostream>
struct A
{
int x;
int y;
//构造函数
A()
{
std::cout << __FILE__ << ":" << __FUNCTION__ << std::endl;
}
void function()
{
std::cout << __FILE__ << ":" << __FUNCTION__ << std::endl;
std::cout << "x" << ":" << x << std::endl;
std::cout << "y" << ":" << y << std::endl;
}
};
b.h
#pragma once
#include <iostream>
struct A
{
int y;
A()
{
std::cout << __FILE__ << ":" << __FUNCTION__ << std::endl;
}
void function()
{
std::cout << __FILE__ << ":" << __FUNCTION__ << std::endl;
std::cout << "y" << ":" << y << std::endl;
}
};
c.h
#pragma once
class C{
public:
void dosomething();
};
c.cpp
#include "c.h"
#include "b.h"
void C::dosomething()
{
A a;
a.y = 3;
a.function();
}
main.cpp
#include "a.h"
#include "c.h"
int main()
{
A a;
a.x = 1;
a.y = 2;
a.function();
C c;
c.dosomething();
return 0;
}
目前的发现是,对于g++编译,哪个的定义放在前面,就用哪个。
然后,在代码阶段和编译阶段,在具体的每个cpp中,各自只能看到对应的那个结构体定义,所以可以访问对应的数据和函数成员。
但是,在运行时,就是未定义行为了。可能会出现运行时错误,或者运行时行为不可预期。
$ g++ c.cpp main.cpp -o test
$ ./test
b.h:A
b.h:function
y:1
b.h:A
b.h:function
y:3
$ g++ main.cpp c.cpp -o test
guo@localhost:/mnt/c/mine/bugs$ ./test
a.h:A
a.h:function
x:1
y:2
a.h:A
a.h:function
x:3
y:-1921781760
2.原因
这其实是一种未定义的行为。不同的编译器的处理不同。
为什么没有报重定义错误:因为对于每个编译单元,各自独立编译,都是有效的。但是在链接成可执行文件时,就会出现歧义了,因为各自都有一个
同一类型的数据定义。
运行行为是不可预测的。
3.解决
1.如果源码可控,还是要分别独立命名,避免重复。
2.使用命名空间:如果确实需要两种不同的结构体,应该使用不同的名称或放在不同命名空间中
1310

被折叠的 条评论
为什么被折叠?



