次のような標準レイアウト メンバを持つ標準レイアウト クラスがあるとします。
struct foo {
int n;
int m;
unsigned char garbage;
};
標準によれば、 と のメモリ領域に書き込むことなく構造体の最後のバイトに書き込むことは常に安全n
ですかm
(ただし、 に書き込む可能性がありgarbage
ます)? 例えば、
foo f;
*(static_cast<unsigned char *>(static_cast<void *>(&f)) + (sizeof(foo) - 1u)) = 0u;
C++11 標準を読むのに時間を費やした後、答えは「はい」のように思えます。
9.2/15 から:
同じアクセス制御 (第 11 節) を持つ (非共用体) クラスの非静的データ メンバーは、後のメンバーがクラス オブジェクト内でより高いアドレスを持つように割り当てられます。アクセス制御が異なる非静的データメンバーの割り当て順序は規定されていません (11)。実装のアライメント要件により、隣接する 2 つのメンバーが互いの直後に割り当てられない場合があります。仮想関数 (10.3) および仮想基本クラス (10.1) を管理するためのスペースの要件も同様です。
したがって、メンバーのアドレスは他の 2 つのメンバー (標準レイアウトであるため、連続して格納されます) よりも高く、したがって、構造体の最後のバイトは最終パディングにgarbage
属するか、その一部である必要があります。garbage
この推論は正しいですか?f
ここでオブジェクトの有効期間をいじっていますか? パディングバイトへの書き込みは問題ですか?
編集
コメントへの返信として、ここで達成しようとしているのは、私が作成しているバリアントのようなクラスと関係があります。
単純な方法 (つまり、格納されている型を記録するために int メンバーをバリアント クラスに配置する) で処理を進めると、パディングによってクラスが必要以上に 50% 近く大きくなります。
私がやろうとしているのは、バリアントに保存しようとしている各クラス型の最後のすべてのバイトが書き込み可能であることを確認することです。これにより、ストレージ フラグを raw ストレージ (整列された raw char 配列) に組み込むことができます。バリアント。私の特定のケースでは、これにより無駄なスペースのほとんどが排除されます。
編集2
実際の例として、次の 2 つのクラスが一般的な 64 ビット マシンのバリアントに格納されているとします。
// Small dynamic vector class storing 8-bit integers.
class first {
std::int8_t *m_ptr;
unsigned short m_size_capacity; // Size and capacity packed into a single ushort.
};
// Vector class with static storage.
class second {
std::int8_t m_data[15];
std::uint8_t m_size;
};
class variant
{
char m_data[...] // Properly sized and aligned for first and second.
bool m_flag; // Flag to signal which class is being stored.
};
これら 2 つのクラスのサイズは、私のマシンでは 16 です。バリアント クラスで必要な余分なメンバーにより、サイズは 24 になります。最後にガベージ バイトを追加すると、次のようになります。
// Small dynamic vector class storing 8-bit integers.
class first {
std::int8_t *m_ptr;
unsigned short m_size_capacity; // Size and capacity packed into a single ushort.
unsigned char m_garbage;
};
// Vector class with static storage.
class second {
std::int8_t m_data[14]; // Note I lost a vector element here.
std::uint8_t m_size;
unsigned char m_garbage;
};
両方のクラスのサイズは 16 のままですが、各クラスの最後のバイトを自由に使用できるようになった場合、バリアントのフラグ メンバーを廃止でき、最終的なサイズは 16 のままです。