You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

GCC中混合C++11与C++20编译是否合规?结构体大小异常问题

混合C11与C20编译导致结构体大小不一致的问题

我在头文件中声明了结构体C,分别用-std=c++11和-std=c++20编译两个不同的编译单元,结果该结构体在两个单元中的sizeof值不同。请问这种混合不同C++标准版本编译代码的做法是否被允许?还是我的GCC使用方式有误?

补充信息:

  • 使用的是同一版本GCC,未混用不同编译器版本;
  • 代码中未使用不同标准下不兼容的特性;
  • 仅使用了输出所需的标准库,问题并非源于此;
  • 运行环境为Debian 11系统上的GCC 10.2.1 20210110(Debian 10.2.1-6,Buster版本),怀疑是该版本GCC存在bug。

问题复现代码

File 1.h

#ifndef DEF1
#define DEF1

#include "c.h"

void f1(C* c);
C* g1();
#endif

File 2.h

#ifndef DEF2
#define DEF2

#include "c.h"

void f2 (C* c);
C* g2();
#endif

File 1.cpp

#include "c.h"
#include <stdio.h>

void f1 (C* c)
{
  printf("sizeof(C)=%ld\n", sizeof(C));
  printf("i2=%d\n", c->i2);
  printf("&i2-&i1=%ld\n", ((char*)&c->i2) - ((char*)&c->i1));
}

C* g1()
{
  C* c = new C;
  c->i2 = 1;
  return c;
}

File 2.cpp

#include "c.h"
#include <stdio.h>

void f2 (C* c)
{
  printf("sizeof(C)=%ld\n", sizeof(C));
  printf("i2=%d\n", c->i2);
  printf("&i2-&i1=%ld\n", ((char*)&c->i2) - ((char*)&c->i1));
}

C* g2()
{
  C* c = new C;
  c->i2 = 2;
  return c;
}

File c.h

#ifndef CDEF
#define CDEF

#include <stdint.h>

struct A
{
    double d1{0.0};
    double d2{0.0};
    double d3{0.0};
    double d4{0.0};
    double d5{0.0};
    double d6{0.0};
    double d7{0.0};
    int32_t i1{};
};

#ifdef DER
struct C : public A
{
    int32_t i2{};
};
#else
struct C
{
    double d1{0.0};
    double d2{0.0};
    double d3{0.0};
    double d4{0.0};
    double d5{0.0};
    double d6{0.0};
    double d7{0.0};
    int32_t i1{};
    int32_t i2{};
};
#endif
#endif

File main.cpp

#include "c.h"
#include "1.h"
#include "2.h"

int main()
{
  f1(g2());
  f2(g1());
}

File bar.sh

g++ -std=c++20 -DDER -c 2.cpp
g++ -std=c++11 -DDER -c 1.cpp
g++ -std=c++20 -DDER -c main.cpp
g++ -std=c++20 2.o 1.o main.o -o prog
./prog

执行结果:

sizeof(C)=64
i2=0
&i2-&i1=4
sizeof(C)=72
i2=0
&i2-&i1=8

问题分析与解答

  1. 混合不同C++标准版本编译属于未定义行为
    即使使用同一编译器的同一版本,跨编译单元混合不同C标准版本编译也是不安全的,不符合C标准的要求。因为不同C++标准可能会修改类型的内存布局规则,导致同一类型在不同编译单元中出现大小、成员偏移不一致的情况,进而引发内存访问错误,就像你遇到的i2值异常的问题。

  2. 具体原因:C++20的尾填充复用特性
    你的案例中,结构体大小差异的根源是C++20新增的派生类成员复用基类尾填充的规则:

    • 基类A的内存布局:7个double(共56字节)+1个int32_t(4字节),总60字节。由于double的对齐要求是8字节,A会被填充到64字节(尾部有4字节的空闲填充空间)。
    • 在C++20中,派生类C的int32_t i2可以直接放入基类A的尾部填充空间,因此C的总大小保持为64字节。
    • 在C++11中,派生类成员无法复用基类的尾填充空间,i2必须放在A的后面,加上对齐要求(C的对齐标准与A一致为8字节),64+4=68字节会被填充到72字节。
  3. 解决方案
    所有参与链接的编译单元必须使用相同的C++标准版本编译,确保同一类型的内存布局在整个程序中完全一致。修改你的编译脚本,统一使用-std=c++11或-std=c++20编译所有.cpp文件即可解决问题。


内容的提问来源于stack exchange,提问作者SR_

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 10:05:22