在C中,编译器将按照声明它们的顺序布置结构的成员,在成员之间插入可能的填充字节,或者在最后一个成员之后插入,以确保每个成员正确对齐.
gcc提供了一种语言扩展,__attribute__((packed))它告诉编译器不要插入填充,允许结构成员不对齐.例如,如果系统通常要求所有int对象都具有4字节对齐,则__attribute__((packed))可能导致int在奇数偏移处分配struct成员.
引用gcc文档:
`packed'属性指定变量或结构字段应该具有尽可能小的对齐 - 变量的一个字节和字段的一个位,除非您使用`aligned'属性指定更大的值.
显然,使用此扩展可以导致更小的数据要求但代码更慢,因为编译器必须(在某些平台上)生成代码,以便一次一个字节地访问未对齐的成员.
但有任何不安全的情况吗?编译器是否始终生成正确(但速度较慢)的代码来访问打包结构的未对齐成员?在所有情况下都可以这样做吗?
指针只是一个地址吗?或者我错过了什么?
我测试了几种类型的指针:
但是指向成员函数的指针更大 - 我的平台上有16B.
三件事:
void*必须能够"包含"任何指针类型.换句话说,任何指针必须能够被转换为void*,对吧?如果是这样,那么为什么sizeof( void* )是8,而sizeof指向成员函数的指针是16?如果struct/class有一些填充,是否有一种方法(trait左右)可以检测?
我不需要跨平台或标准化解决方案,我需要它用于MSVC2013.
我可以检查一下
namespace A
{
struct Foo
{
int a;
bool b;
};
}
#pragma pack(push, 1)
namespace B
{
struct Foo
{
int a;
bool b;
};
}
#pragma pack(pop)
static const bool has_padding = sizeof(A::Foo) != sizeof(B::Foo);
Run Code Online (Sandbox Code Playgroud)
但是C++不允许(据我所知)生成这种非侵入性(不触及现有的结构)
理想情况下,我希望得到这样的工作
template <typename T>
struct has_padding_impl
{
typedef __declspec(align(1)) struct T AllignedT;
};
template <typename T>
struct has_padding : typename std::conditional<sizeof(typename has_padding_impl<T>::AllignedT) == sizeof(T),
std::false_type,
std::true_type>::type{};
Run Code Online (Sandbox Code Playgroud)
编辑 - 为什么我需要这个?
我正在使用现有的序列化系统,它存储一些结构只需要void*它们(在通用函数内)和存储sizeof(T)字节数......这样的二进制文件在我们所针对的平台上是不可移植的,因为使用了不同的编译器,所以那里不保证如何插入填充.如果我可以静态检测所有T带有填充的结构,我可以强制用户手动插入填充(一些控制填充,例如不仅仅是随机垃圾),因此没有"随机"填充.另一个优点是,当我对两个相同scenerio的保存文件进行差异化时,它们看起来是一样的.
编辑2 我想的越多,我就越意识到我需要跨平台的解决方案.我们主要在msvc2013上开发,但我们的应用程序最终在msvc2012和clang中构建.但是,如果我在msvc2013中检测到并删除了所有编译器生成的填充,则无法保证其他编译器不插入填充...(因此msvc2013检测不够)
有一个非常微妙的问题.有一个用MS VS 2013 c ++编译器编译的类,32位平台的大小为4个字节.函数指针的大小为4个字节.但是当这个类用同样的编译器编译但包含在不同的项目中来生成库时,也就是针对32位平台,那么这个类有*m_Function指针占用16个字节!当然,当我从主项目中实例化这个类时,它认为该类占用4个字节并分配这个非常大的内存大小,而实际上它占用16个字节并导致内存溢出.
class CC1
{
public:
CC1();
void (CC1:: *m_Function) ();
};
Run Code Online (Sandbox Code Playgroud)
我知道指向成员函数的大小可能会有所不同.但qustion是 - 哪个编译器设置控制这个?我不在乎它是4或16字节 - 只需要它们是相同的.两个项目的Struct成员对齐设置相同./ vmm/vmg选项?在两个项目的编译器设置中都没有提到它们.
顺便说一下,我尝试构建x64目标,在这种情况下,sizeof*m_Function总是8字节,来自main和libray项目.
谢谢.
有没有一种方法可以编写一个编译时断言来检查某种类型是否有填充?
例如:
struct This_Should_Succeed
{
int a;
int b;
int c;
};
struct This_Should_Fail
{
int a;
char b;
// because there are 3 bytes of padding here
int c;
};
Run Code Online (Sandbox Code Playgroud) c++ ×4
padding ×2
pointers ×2
visual-c++ ×2
c ×1
gcc ×1
pragma-pack ×1
sizeof ×1
struct ×1
type-traits ×1