简体   繁体   English

GCC和G ++ struct打包差异?

[英]GCC and G++ struct packing differences?

I'm using C code in my C++ project which I include using extern "C". 我在C ++项目中使用的是C代码,其中包括使用extern“ C”。 I then define structs in my C++ class which are passed to the C code (compiled using gcc). 然后,我在C ++类中定义结构,这些结构将传递给C代码(使用gcc编译)。 The problem is that when that C code accesses the struct that was defined in the C++ class, I get different alignment and the code reads and writes WRONG data. 问题是,当该C代码访问C ++类中定义的结构时,我得到了不同的对齐方式,并且该代码读取和写入了WRONG数据。 This seems bizarre but I have been debugging this for 3 hours and basically when I print pointer to a deep member of that struct from C++ code, everything is fine. 这似乎很奇怪,但是我已经调试了3个小时,基本上,当我从C ++代码打印指向该结构的深层成员的指针时,一切都很好。 But if I print the pointer from within C code, the address is different! 但是,如果我从C代码中打印指针,则地址会有所不同!

Why is this happening and how do I ensure that my alignment is consistent? 为什么会发生这种情况,如何确保对齐一致?

(I have double checked that this is NOT an out of date compilation issue by recompiling everything. Still same result. In C the address is 8 bytes before the address to the member as reported in C++ code) (通过重新编译所有内容,我再次检查了这是否不是过期的编译问题。仍然是相同的结果。在C中,地址是C ++代码中报告的成员地址之前的8个字节)

Edit: the problem was that g++ and gcc would treat empty structs differently. 编辑:问题是g ++和gcc将以不同的方式对待空结构。 If I had an empty struct foo {} in my code, sizeof my struct would be 1 byte bigger under g++ and all offsets of members would be messed up. 如果我的代码中有一个空的foo {}结构,则在g ++下,我的结构的sizeof会大1个字节,并且所有成员的偏移量都会被弄乱。 So I put a dummy uint8_t into the empty struct so that it would not be empty anymore and all such problems across the codebase seem to have disappeared. 因此,我将一个虚拟uint8_t放入空结构中,以使其不再为空,并且整个代码库中的所有此类问题似乎都已消失。 Now alignment seems to work the same and struct size is the same as well (for each empty struct g++ would previously add 1 byte). 现在对齐似乎可以正常工作,并且结构大小也相同(对于每个空结构,g ++之前会添加1个字节)。 I am not 100% sure what is going on with gcc so if anyone is has anything to add then feel free to fill me in on this 我不是100%知道gcc发生了什么,所以如果有人要添加任何内容,请随时填写

My gcc version is: 我的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

My compilation flags under gcc are: 我在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: added -pedantic to my C flags to make gcc warn about empty structs. Edit2:在我的C标志中添加了-pedantic,以使gcc对空结构发出警告。 Dropped it previously because it broke many other things and third party headers (we have to use -Werror). 之前将其删除是因为它破坏了许多其他内容和第三方标头(我们必须使用-Werror)。 Luckily things like macro ({}) statements can be prefixed with __extension__ to make them pass -pedantic. 幸运的是,诸如宏({})语句之类的东西可以加上__extension__前缀,以使它们通过-pedantic。 Got a few other things that need to be changed in the codebase. 在代码库中还有其他一些需要更改的内容。 Don't know yet if something will be hard to implement with pedantic enabled. 尚不清楚启用pedantic是否会难以实现。 Does anyone know how to only enable warning for empty structs or disable some specific things that pedantic does not allow? 有谁知道如何仅对空结构启用警告或禁用pedantic不允许的某些特定操作?

The proper solution seems to be to never define empty structs in code that will be used across C++ -> C boundary. 正确的解决方案似乎是永远不要在将跨C ++-> C边界使用的代码中定义空结构。 I had a: 我曾有一个:

struct spinlock { } 

that was empty when building against a single processor target. 针对单个处理器目标进行构建时为空。 So g++ would make this 1 byte while gcc would just discard it. 因此,g ++会将其设为1个字节,而gcc会将其丢弃。 Therefore my structs would be different size and offsetof would even return different results depending on whether the code was built with gcc or g++. 因此,根据代码是使用gcc还是g ++构建的,我的结构将具有不同的大小,offsetof甚至会返回不同的结果。

So I changed it to 所以我改成

struct spinlock { uint8_t dummy; }

and now my structs align perfectly and everything works without any special C++ standard specification. 现在我的结构可以完美对齐,并且不需要任何特殊的C ++标准规范即可正常运行。

Empty structures gets you a couple of implementation defined or non-standard behaviors. 空结构会为您提供一些实现定义的或非标准的行为。 First of all empty structures aren't supported in standard C. GCC supports them in as an extension with a size == 0. 首先,标准C不支持空结构。GCC支持将它们作为大小== 0的扩展。

C++ supports empty structs, but mandates that they have a non-zero size when used as a "most derived object". C ++支持空结构,但要求当用作“最派生对象”时,它们的大小必须为非零。 However, C++ also allows classes/structs that inherit from empty structs to optimize that size away for the subclass. 但是,C ++还允许从空结构继承的类/结构为子类优化该大小。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM