c - GCC 和 G++ struct packing 的区别?

标签 c gcc g++ alignment

我在我的 C++ 项目中使用 C 代码,我使用 extern "C"将其包括在内。然后,我在传递给 C 代码(使用 gcc 编译)的 C++ 类中定义结构。问题是,当该 C 代码访问在 C++ 类中定义的结构时,我得到了不同的对齐方式,并且代码读取和写入了错误的数据。这看起来很奇怪,但我已经调试了 3 个小时,基本上当我从 C++ 代码打印指向该结构的深层成员的指针时,一切都很好。但是如果我从 C 代码中打印指针,地址就不同了!

为什么会发生这种情况,我如何确保对齐一致?

(我通过重新编译所有内容来仔细检查这不是一个过时的编译问题。结果仍然相同。在 C 中,地址是 C++ 代码中报告的成员地址之前的 8 个字节)

编辑:问题在于 g++ 和 gcc 会以不同方式处理空结构。如果我的代码中有一个空的 struct foo {},那么在 g++ 下,我的结构的 sizeof 会大 1 个字节,并且成员的所有偏移量都会被弄乱。所以我将一个虚拟的 uint8_t 放入空结构中,这样它就不会再为空了,代码库中的所有此类问题似乎都消失了。现在对齐似乎工作相同并且结构大小也相同(对于每个空结构 g++ 以前会添加 1 个字节)。我不是 100% 确定 gcc 发生了什么,所以如果有人有任何要补充的,请随时告诉我这个

我的 gcc 版本是:

Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 5.4.0-6ubuntu1~16.04.5' --with-bugurl=file:///usr/share/doc/gcc-5/README.Bugs --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-5 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-5-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-5-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-5-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 5.4.0 20160609

我在 gcc 下的编译标志是:

-std=gnu99 -Wchar-subscripts -Wformat -Wformat-nonliteral -Wformat-security -Wmissing-braces -Wparentheses -Wsequence-point -Wswitch -Wtrigraphs -Wno-unused-function -Wunused-label -Wno-unused-parameter -Wunused-variable -Wunused-value -Wuninitialized -Wdiv-by-zero -Wfloat-equal -Wdouble-promotion -fsingle-precis
ion-constant -Wshadow -Wpointer-arith -Wwrite-strings -Wconversion -Wredundant-decls -Wunreachable-code -Winline -Wenum-compare -Wlong-long -Wchar-subscripts -Wextra -Werror -g -Os -ffunction-sections -fdata-sections -m32 -fstack-protector -D_XOPEN_SOURCE=700 -D_GNU_SOURCE -ftest-coverage -fprofile-arcs

g++

-std=c++11 -Wextra -Werror -g -Os -ffunction-sections -fdata-sections -m32 -fstack-protector -D_XOPEN_SOURCE=700 -D_GNU_SOURCE -ftest-coverage -fprofile-arcs

Edit2:将 -pedantic 添加到我的 cflags中,使 gcc 对空结构发出警告。之前放弃它是因为它破坏了许多其他东西和第三方 header (我们必须使用 -Werror)。幸运的是,诸如宏 ({}) 语句之类的东西可以以 __extension__ 为前缀,使它们通过 -pedantic。在代码库中还有其他一些需要更改的内容。还不知道在启用迂腐的情况下是否很难实现某些东西。有谁知道如何只对空结构启用警告或禁用迂腐不允许的某些特定内容?

最佳答案

正确的解决方案似乎是永远不要在将跨 C++ -> C 边界使用的代码中定义空结构。我有一个:

struct spinlock { } 

针对单个处理器目标构建时,它是空的。所以 g++ 会生成这个 1 字节,而 gcc 只会丢弃它。因此,我的结构将具有不同的大小,offsetof 甚至会返回不同的结果,具体取决于代码是使用 gcc 还是 g++ 构建的。

所以我改成了

struct spinlock { uint8_t dummy; }

现在我的结构完全对齐并且一切正常,无需任何特殊的 C++ 标准规范。

关于c - GCC 和 G++ struct packing 的区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48068142/

相关文章:

如果我不使用 Code::Blocks 但使用 cygwin 进行编译,c++ compare std::string 将不起作用

c++ - 在 Mac 上使用 FFMPEG 而不是 Quicktime 编译 OpenCV

c - 如何编译使用 Lua 5.1 的 C API 的代码?

c - 在 C 中复制二进制文件的神秘问题

c - Windows 和 Unix 的文件分隔符

似乎无法使用 C 的 getopt() 访问多个选项

c - 在 gcc 编译期间获取内联失败警告

c - 属性构造函数样式函数的参数

c++ - libtorrent-rasterbar7 : g++ linker unable to find libtorrent/session. hpp

c - 通过 TCP 发送一个整数(C 语言编程)