2013-10-23 68 views
8

我想设置一个类的填充字节为0,因为我在字节级保存/加载/比较/散列实例,并且垃圾初始化填充在每个操作中引入了非确定性。我怎样才能零等于一个类的填充字节?

我知道,这将实现我想要什么(对于平凡的可复制的类型):

struct Example 
{ 
    Example(char a_, int b_) 
    { 
     memset(this, 0, sizeof(*this)); 
     a = a_; 
     b = b_; 
    } 
    char a; 
    int b; 
}; 

我不喜欢这样做,虽然,有两个原因:我喜欢构造函数初始化器列表,我知道,将这些位设置为0并不总是与零初始化相同(例如,指针和浮点数不一定具有全零位的零值)。顺便说一下,它显然局限于可以复制的类型,但这对我来说不是问题,因为我上面列出的操作(在字节级别加载/保存/比较/散列)需要无论如何都可复制的类型。

我想什么是这样的[魔法]片段:

struct Example 
{ 
    Example(char a_, int b_) : a(a_), b(b_) 
    { 
     // Leaves all members alone, and sets all padding bytes to 0. 
     memset_only_padding_bytes(this, 0); 
    } 
    char a; 
    int b; 
}; 

我怀疑这样的事情是可能的,因此,如果任何人都可以提出一个非丑替代......我所有的耳朵:)

+1

我的建议:1,只是做了'memset的()',然后初始化明确(基本上是你的第一种方法),2。排除从哈希完全填充字节。 – 2013-10-23 15:12:56

+1

“我不喜欢这样做,原因有两个......”请注意,第二个原因是无关紧要的,因为分配给成员的字节将由构造函数写入。 – dasblinkenlight

+1

我的建议是,而不是使用'字节垫;'创建的填充类型,它可以自动调零本身(例如模板化的类型,这样你就可以控制大小'PadData 垫;') – benjymous

回答

7

我不知道在纯C++中完全自动完成此操作。我们使用一个自定义的代码生成系统来实现这一点(除其他外)。你可以使用一个宏来为你提供所有的成员变量名称;它只会在offsetof(memberA)+ sizeof(memberA)和offsetof(memberB)之间寻找漏洞。

或者,以成员为基础序列化/散列,而不是二进制BLOB。这是十种清洁剂。

哦,另外一个选项 - 你可以提供一个operator new,它在返回之前明确地清除了内存。我不是那种方法的粉丝,虽然.....它不适用于堆栈分配。

+1

“一个成员逐一的基础上序列化/散列” - 我绝对知道这是正确的做法,我只是不想输出每个类的所有成员名称,对于每个操作......但是,讽刺的是,我已经输入了更多,只是通过问这个问题:) –

+0

附注:我真的用C++进行了反思。 –

+0

https://github.com/sk-havok/clang-extract – Sneftel

2

在二进制写入/读取它们时,不应该使用填充的结构。只是因为填充可能会因平台而异,从而导致二进制不兼容。

使用一些编译器选项,比如#pragma pack (push, 1)禁用填充定义那些写结构时,它与#pragma pack(pop)恢复。

这可悲意味着你失去了它提供的优化。 如果这是一个问题,通过仔细设计您的结构,您可以通过插入虚拟变量手动“填充”它们。然后零初始化变得明显,您只需将零分配给这些假人。我不建议使用“手动”方法,因为它非常容易出错,但是当您使用二进制blob编写时,您可能已经担心。但通过一切手段,基准以前的无衬垫结构。

+0

我已经尝试过使用编译指示和其他编译器的类似选项来在不同平台上的程序之间共享序列化数据,但发现它们不可靠,最终改为执行成员序列化。如果它对所有我关心的平台都以同样的方式工作,那么这肯定会成为一种方式(就我而言)。 –

+0

这很奇怪你在说什么,因为打包选项应该是可靠的,它们对于二进制写入是必不可少的。也许你应该问一个关于在你使用的编译器中打包命令的问题。 –

+0

@BenHymers在这里他们说'#pragma pack是直接支持许多主流编译器:http://stackoverflow.com/questions/13927273/how-to-translate-struct-packing-from-vc-to-gcc OFC则存在另一个问题,如果目标体系结构中的每个人都支持未对齐的变量 - 即使在x86上,使用SSE时也可能会出现这种变量 –

2

我遇到了类似的问题 - 只是说这是一个糟糕的设计决定(根据dasblinkenlight的评论)并不一定有帮助,因为您可能无法控制散列代码(在我的情况下,我使用的是外部图书馆)。

一个解决方案是为您的类编写一个自定义迭代器,它遍历数据的字节并跳过填充。然后,您修改您的散列算法以使用您的自定义迭代器而不是指针。一个简单的方法就是对指针进行模板化,以便它可以接受迭代器 - 因为指针和迭代器的语义是相同的,所以不应该在模板化之外修改任何代码。

编辑:Boost提供了一个很好的库,可以很容易地向您的容器添加自定义迭代器:Boost.Iterator

你去解决哪种,这是非常可取的,以避免散列填充,因为这样做意味着你的散列算法是高度加上你的数据结构。如果您切换数据结构(或作为Agent_L提及,在不同的平台上使用相同的数据结构,其填充方式不同),则会产生不同的哈希值。另一方面,如果你只散列实际的数据本身,那么无论你以后使用什么数据结构,总是会产生相同的散列值。

相关问题