私は同僚とこの会話をしましたが、それは興味深いものでした。次のPODクラスがあるとします
struct A {
void clear() { memset(this, 0, sizeof(A)); }
int age;
char type;
};
clear
0
(バイト単位)に設定して、すべてのメンバーをクリアすることを目的としています。A
基本クラスとして使用すると、何が問題になる可能性がありますか? ここにバグの微妙なソースがあります。
コンパイラは、パディング バイトを A に追加する可能性が高いため、(パディングの最後まで)sizeof(A)
拡張します。char type
ただし、継承の場合、コンパイラはパディングされたバイトを追加しない場合があります。したがって、への呼び出しmemset
はサブクラスの一部を上書きします。
他の注意事項に加えて、sizeof
はコンパイル時の演算子であるためclear()
、派生クラスによって追加されたメンバーをゼロにすることはありません (パディングの奇妙さのために注意されている場合を除きます)。
これについて本当に「微妙」なことは何もありません。memset
C++ で使用するのは恐ろしいことです。ごくまれに、メモリをゼロで埋めるだけで正常な動作が期待でき、メモリをゼロで埋める必要があり、初期化子リストを介してすべてをゼロで初期化する文明化された方法が何らかの形で受け入れられない場合は、代わりに使用してください。std::fill
理論的には、コンパイラーは基本クラスを異なる方法でレイアウトできます。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;
}
そして、、、、またはその両方を「パック」するように変更すると(A
VSとGCCで)、どのような場合でも上書きされることはありませんでした。最適化が有効になりました。サイズ/オフセットに対して印刷された3つの値は、常に8/12 / 8、8 / 9/8、または5/6/5でした。B
#pragma pack
__attribute__((packed))
b.x
基本クラスのclear
メソッドは、クラスメンバーの値のみを設定します。
アラインメントルールに従って、コンパイラはパディングを挿入して、次のデータメンバーがアラインメントされた境界で発生するようにすることができます。type
したがって、データメンバーの後にパディングがあります。基本クラスには子孫のサイズが含まれていないためmemset
、子孫の最初のデータメンバーはこのスロットを占有し、の影響を受けません。sizeof
親のサイズ!=子のサイズ(子にデータメンバーがない場合を除く)。 スライスを参照してください。
構造体のパッキングは、言語標準の一部ではありません。うまくいけば、優れたコンパイラでは、パックされた構造のサイズに、最後のバイト以降の余分なバイトが含まれていません。それでも、パックされた親から継承するパックされた子孫は同じ結果を生成するはずです。親は親のデータメンバーのみを設定します。
簡単に言うと、唯一の潜在的な問題は、C89、C2003標準での「パディングバイト」の保証に関する情報が見つからないことです....それらには異常な揮発性または読み取り専用の動作がありますか-見つけられません「パディングバイト」という用語は、標準で何を意味しますか...
詳細:
POD 型のオブジェクトについては、C++2003 標準によって次のことが保証されています。
POD オブジェクトの先頭にパディングがないことを保証
goto ステートメント、ライフタイムに関する C++ ルールを破ることができます
C89 の場合、構造に関するいくつかの保証も存在します。
構造体の始まりが同じ場合、共用体構造の混合物に使用すると、最初の構成要素は完全に計算されます
C の構造体のサイズは、すべてのコンポーネントを格納するためのメモリの量に等しく、コンポーネント間のパディングの下に配置し、次の構造体の下にパディングを配置します。
C では、構造体のコンポーネントにアドレスが与えられます。アドレスのコンポーネントが昇順であるという保証があります。また、最初のコンポーネントのアドレスは、構造体の開始アドレスと一致します。プログラムが実行されるコンピューターのエンディアンに関係なく
したがって、そのような規則は C++ にも適しているように思われ、すべて問題ありません。ハードウェアレベルでは、非 const オブジェクトのパディングバイトの書き込みを制限する人は誰もいないと本当に思います。