背景
最近在学习c++编译原理,好奇当两个结构体具有相同的名称,但定义不同时会发生什么?于是写了个测试小例子测试一下。先说结论:不同编译器行为可能不一样,有可能会按照错误的方式对结构体变量的内存布局进行解释,因此不推荐给一个结构体名称多个不同的定义。 下面是测试案例
代码
// a.h
#pragma once
struct A {
int x;
char c;
float y;
};
// a.cpp
#include "a.h"
A a{ 1,'x',2.0 };
// main.cpp
#include <iostream>
//#include "a.h"
struct A {
float y;
};
extern A a;
int main() {
std::cout << "a.y" << a.y << "\n";
return 0;
}
输出乱值
分析
编译器能够编译和运行这个程序,可能有几个原因,但关键在于未定义行为的处理方式。在 C++ 中,未定义行为(Undefined Behavior, UB)意味着编译器没有义务在这种情况下生成任何特定的行为,因此具体的结果取决于编译器、平台以及编译器实现细节。在你的特定情况下,编译器可能生成了一个可以运行的程序,但这并不意味着程序的行为是正确的或可移植的。
编译和运行通过的原因
- 内存布局:在这个简单的例子中,编译器可能分配了足够的内存以容纳两个成员变量(
x和y),即使main.cpp中只定义了一个x。当访问a.x时,可能正好访问了正确的内存位置,导致程序没有崩溃。 - 对齐和填充:在大多数现代计算平台上,数据结构的对齐和填充可能导致
struct A的内存布局在两个文件中相同或相似。这种情况下,访问a.x可能不会引起显著的错误。 - 优化器行为:编译器的优化器可能对代码进行了某些处理,使得代码在运行时看似正确。例如,优化器可能会内联一些操作或者简化对内存的访问,从而避免直接的内存冲突。
与内存布局有关
在main.cpp中对外部变量a进行解析时,float A::y会按照a.cpp中A的内存布局,将int A::x,解释为float A::y