23

我和一位同事进行了这次谈话,结果很有趣。假设我们有以下 POD 类

struct A { 
  void clear() { memset(this, 0, sizeof(A)); } 

  int age; 
  char type; 
};

clear旨在清除所有成员,设置为0(按字节)。如果我们A用作基类会出现什么问题?这里有一个微妙的错误来源。

4

5 回答 5

17

编译器可能会向 A 添加填充字节。因此会sizeof(A)超出char type(直到填充结束)。但是,在继承的情况下,编译器可能不会添加填充字节。所以调用memset将覆盖子类的一部分。

于 2011-08-18T20:06:09.757 回答
4

除了其他注释之外,sizeof它是一个编译时运算符,因此clear()不会将派生类添加的任何成员归零(由于填充怪异而注明除外)。

这并没有什么真正“微妙”的地方。memset在 C++ 中使用是一件可怕的事情。在极少数情况下,您确实可以用零填充内存并期望正常行为,并且您确实需要用零填充内存,通过初始化列表对所有内容进行零初始化,文明的方式在某种程度上是不可接受的,请std::fill改用。

于 2011-08-18T20:19:04.157 回答
2

理论上,编译器可以对基类进行不同的布局。C++03 §10 第 5 段说:

基类子对象的布局 (3.7) 可能与相同类型的最派生对象的布局不同。

正如StackedCrooked 提到的,当基类作为自己的对象存在时,编译器可能会在基类的末尾添加填充A,但是当它是基类时编译器可能不会添加该填充。这将导致A::clear()覆盖子类成员的前几个字节。

但是在实践中,我无法通过 GCC 或 Visual Studio 2008 实现这一点。使用此测试:

struct A
{
  void clear() { memset(this, 0, sizeof(A)); }

  int age;
  char type;
};

struct B : public A
{
  char x;
};

int main(void)
{
  B b;
  printf("%d %d %d\n", sizeof(A), sizeof(B), ((char*)&b.x - (char*)&b));
  b.x = 3;
  b.clear();
  printf("%d\n", b.x);

  return 0;
}

并且将 , 或两者都修改AB“打包”(#pragma pack在 VS 和__attribute__((packed))GCC 中),无论如何我都无法b.x被覆盖。启用了优化。为尺寸/偏移量打印的 3 个值始终为 8/12/8、8/9/8 或 5/6/5。

于 2011-08-18T21:29:08.833 回答
0

基类的clear方法只会设置类成员的值。

根据对齐规则,允许编译器插入填充,以便下一个数据成员将出现在对齐的边界上。type因此数据成员之后会有填充。后代的第一个数据成员将占据这个槽并且不受 的影响memset,因为sizeof基类不包括后代的大小。父级的大小!= 子级的大小(除非子级没有数据成员)。 见切片

结构的打包不是语言标准的一部分。希望通过一个好的编译器,压缩结构的大小不包括最后一个字节之后的任何额外字节。即便如此,从打包父级继承的打包后代应该产生相同的结果:父级仅设置父级中的数据成员。

于 2011-08-19T00:05:08.917 回答
0

简而言之:在我看来,唯一的一个潜在问题是我在 C89、C2003 标准中找不到任何关于“填充字节”保证的信息......他们是否有一些非同寻常的易失性或只读行为 - 我找不到甚至标准的术语“填充字节”是什么意思......

详细

对于 POD 类型的对象,C++2003 标准保证:

  • 当您将对象的内容 memcpy 到一个 char 或 unsigned char 数组中,然后将内容 memcpy 回您的对象时,该对象将保持其原始值
  • 保证在 POD 对象的开头不会有填充

  • 可以打破 C++ 规则:goto 语句、生命周期

对于 C89,还存在一些关于结构的保证:

  • 当用于联合结构的混合时,如果结构具有相同的开头,则第一个组件具有完美的数学运算

  • C中的sizeof结构体等于存储所有组件的内存量,组件之间的padding下的地方,下面的结构体下的padding

  • 在 C 中,结构的组件被赋予了地址。保证地址的组成部分是按升序排列的。并且第一个组件的地址与结构的起始地址一致。无论程序运行的计算机是哪个字节序

所以在我看来,这样的规则也适用于 C++,一切都很好。我真的认为在硬件级别没有人会限制你为非常量对象写入填充字节。

于 2015-07-28T02:36:23.630 回答