gcc / clang将派生结构的字段布置在基本结构的后填充中 [英] gcc/clang lay out fields of a derived struct in the back-padding of base struct
问题描述
当我涉及到填充和继承时,我对gcc和clang如何构造结构感到困惑。以下是一个示例程序:
#include< string.h>
#include< stdio.h>
struct A
{
void * m_a;
};
struct B:A
{
void * m_b1;
char m_b2;
};
struct B2
{
void * m_a;
void * m_b1;
char m_b2;
};
struct C:B
{
short m_c;
};
struct C2:B2
{
short m_c;
};
int main()
{
C c;
memset(& c,0,sizeof(C));
memset((B *)& c,-1,sizeof(B));
printf(
c.m_c =%d; sizeof(A)=%d sizeof(B)=%d sizeof(C)=%d\\\
,
c.m_c,sizeof(A),sizeof(B),sizeof(C)
);
C2 c2;
memset(& c2,0,sizeof(C2));
memset((B2 *)& c2,-1,sizeof(B2));
printf(
c2.m_c =%d; sizeof(A)=%d sizeof(B2)=%d sizeof(C2)=%d\\\
,
c2.m_c,sizeof(A),sizeof(B2),sizeof(C2)
);
return 0;
}
输出:
$ ./a.out
c.m_c = -1; sizeof(A)= 8 sizeof(B)= 24 sizeof(C)= 24
c2.m_c = 0; sizeof(A)= 8 sizeof(B2)= 24 sizeof(C2)= 32
C2布局不同。在C1中,m_c被分配在结构B1的后填充中,因此被第二memset()覆盖;与C2不会发生。
使用的编译器:
$ clang --version
Ubuntu clang版本3.3-16ubuntu1(branches / release_33)(基于LLVM 3.3)
目标:x86_64-pc-linux-gnu
线程模型:posix
$ c ++ - -version
c ++(Ubuntu 4.8.2-19ubuntu1)4.8.2
版权所有(C)2013自由软件基金会,
这是免费软件;请参阅复制条件的来源。有NO
保修;甚至不适用于适销性或特定用途的适用性。
使用-m32选项也会发生同样的情况(显然,输出中的大小会有所不同) p>
Microsoft Visual Studio 2010 C ++编译器的x86和x86_64版本没有这个问题(即他们同样布置了结构体С1和C2)
如果它不是一个错误,并且是设计,那么我的问题是:
- 分配或不分配派生结构
的字段(例如为什么它不会发生在C2?) - 有任何方法来覆盖这个行为
$ b
$ bVladimir
解决方案不同之处在于,编译器允许使用上一个对象的填充已经是不只是数据,并且不支持
memcpy
。
B
结构不仅仅是数据,因为它是一个派生对象,因此可以使用它的松散空间,因为如果你memcpy
B2 / code>而是只是一个结构和向后兼容性要求其大小(包括松弛空间)只是内存您的代码允许使用
memcpy
。I'm confused with how gcc and clang lay out structs when both padding and inheritance are involved. Here's a sample program:
#include <string.h> #include <stdio.h> struct A { void* m_a; }; struct B: A { void* m_b1; char m_b2; }; struct B2 { void* m_a; void* m_b1; char m_b2; }; struct C: B { short m_c; }; struct C2: B2 { short m_c; }; int main () { C c; memset (&c, 0, sizeof (C)); memset ((B*) &c, -1, sizeof (B)); printf ( "c.m_c = %d; sizeof (A) = %d sizeof (B) = %d sizeof (C) = %d\n", c.m_c, sizeof (A), sizeof (B), sizeof (C) ); C2 c2; memset (&c2, 0, sizeof (C2)); memset ((B2*) &c2, -1, sizeof (B2)); printf ( "c2.m_c = %d; sizeof (A) = %d sizeof (B2) = %d sizeof (C2) = %d\n", c2.m_c, sizeof (A), sizeof (B2), sizeof (C2) ); return 0; }
Output:
$ ./a.out c.m_c = -1; sizeof (A) = 8 sizeof (B) = 24 sizeof (C) = 24 c2.m_c = 0; sizeof (A) = 8 sizeof (B2) = 24 sizeof (C2) = 32
Structs C1 and C2 are laid out differently. In C1 m_c is allocated in the back-padding of struct B1 and is therefore overwritten by the 2nd memset (); with C2 it doesn't happen.
Compilers used:
$ clang --version Ubuntu clang version 3.3-16ubuntu1 (branches/release_33) (based on LLVM 3.3) Target: x86_64-pc-linux-gnu Thread model: posix $ c++ --version c++ (Ubuntu 4.8.2-19ubuntu1) 4.8.2 Copyright (C) 2013 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
The same happens with -m32 option (sizes in the output will be different, obviously).
Both x86 and x86_64 versions of Microsoft Visual Studio 2010 C++ compiler don't have this issue (i.e. they lay out structs С1 and C2 identically)
If it's not a bug and is by design, then my questions are:
- what are the precise rules for allocating or not allocating fields of a derived struct in the back-padding (e.g. why it doesn't happen with C2?)
- is there any way to override this behaviour with switches/attributes (i.e. lay out just like MSVC does)?
Thanks in advance.
Vladimir
解决方案The difference is that the compiler is allowed to use the padding of a previous object if that object is already "not just data" and manipulating it say with
memcpy
is not supported.The
B
structure is not just data, because it's a derived object and therefore the slack space of it can be used because if you'rememcpy
-ing aB
instance around you're already violating the contract.B2
instead is just a structure and backward compatibility requires that its size (including the slack space) is just memory your code is allowed to play with usingmemcpy
.这篇关于gcc / clang将派生结构的字段布置在基本结构的后填充中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!