27

構造体メンバーの配置がわかっている場合、構造タイプの配置を見つけることができますか?

例えば。にとって:

struct S
{
 a_t a;
 b_t b;
 c_t c[];
};

S = max(alignment_of(a)、alignment_of(b)、alignment_of(c))のアライメントですか?

インターネットを検索すると、「構造化タイプの場合、その要素のいずれかの最大のアライメント要件が構造のアライメントを決定する」(すべてのプログラマーがメモリについて知っておくべきこと)がわかりましたが、標準(最新)ではリモートで類似したものは見つかりませんでしたより正確にドラフト)。


編集: すべての回答に感謝します。特に、元の質問に本当に良い回答を提供してくれたRobertGambleと他の人に感謝します。

要するに:

構造体部材の位置合わせ要件を確実にするには、構造体の位置合わせは、少なくともその最も厳密な部材の位置合わせと同じくらい厳密でなければなりません。

構造の配置を決定することに関して、いくつかのオプションが提示されました、そして少しの研究でこれは私が見つけたものです:

  • c ++ std :: tr1 :: alignment_of
    • まだ標準ではありませんが、近い(テクニカルレポート1)、C++0xにあるはずです
    • 最新のドラフトには、次の制限があります。前提条件:Tは、完全型、参照型、または未知の境界の配列である必要がありますが、関数型または(場合によってはcv修飾された)voidであってはなりません。
      • これは、C99フレキシブルアレイで提示したユースケースが機能しないことを意味します(フレキシブルアレイは標準のc ++ではないため、これはそれほど驚くべきことではありません)
    • 最新のc++ドラフトでは、新しいキーワードで定義されています-alignas(これには同じ完全な型の要件があります)
    • 私の意見では、c ++標準がC99フレキシブル配列をサポートする場合、要件を緩和することができます(構造とフレキシブル配列の配置は、配列要素の数に基づいて変更されるべきではありません)
  • c ++ boost :: alignment_of
    • 主にtr1の代替品
    • voidに特化しているようで、その場合は0を返します(これはc ++ドラフトでは禁止されています)
    • 開発者からの注意:厳密に言えば、ALIGNOF(T)の値はTの真のアラインメントの倍数であることにのみ依存する必要がありますが、実際には、私たちが知っているすべての場合に正しい値を計算します。
    • これが柔軟な配列で機能するかどうかはわかりません(一般的には機能しない可能性があります。これは私のプラットフォームに固有のコンパイラに解決されるため、一般的な場合の動作はわかりません)
  • Andrew Topは、回答の配置を計算するための簡単なテンプレートソリューションを提示しました
    • これはブーストが行っていることに非常に近いようです(私が見る限り、ブーストは計算された配置よりも小さい場合、オブジェクトサイズを配置として追加で返します)、おそらく同じ通知が適用されます
    • これは柔軟なアレイで機能します
  • Windbg.exeを使用して、シンボルの配置を確認します
    • コンパイル時ではなく、コンパイラ固有で、テストしませんでした
  • タイプを含む匿名構造でoffsetofを使用する
    • 答えを見る、信頼できない、c++非PODとの移植性がない
  • コンパイラ組み込み関数、例えば。MSVC __alignof
    • 柔軟なアレイで動作します
    • alignofキーワードは最新のC++ドラフトにあります

「標準」ソリューションを使用する場合は、std :: tr1 :: alignment_ofに制限されますが、c++コードをc99の柔軟な配列と混合すると機能しません。

私が見ているように、解決策は1つだけです-古い構造体ハックを使用してください:

struct S
{
 a_t a;
 b_t b;
 c_t c[1]; // "has" more than 1 member, strictly speaking this is undefined behavior in both c and c++ when used this way
};

この場合(および他のすべての場合)、cおよびc++標準の相違とそれらの違いの増大は残念なことです。


もう1つの興味深い質問は、(移植可能な方法で構造の位置合わせを見つけることができない場合)可能な限り最も厳しい位置合わせ要件は何かということです。私が見つけることができるいくつかの解決策があります:

  • boost(内部)はさまざまなタイプのユニオンを使用し、その上でboost::alignment_ofを使用します
  • 最新のc++ドラフトにはstd::aligned_storageが含まれています
    • default-alignmentの値は、サイズがLen以下のC++オブジェクトタイプの最も厳しいアライメント要件です。
      • だからstd::alignment_of< std::aligned_storage<BigEnoughNumber>>::value私たちに最大のアライメントを与えるはずです
      • ドラフトのみで、まだ標準ではありません(あるとしても)、tr1::aligned_storageこのプロパティはありません

これについての考えもいただければ幸いです。

新しいサブ質問の可視性と入力を増やすために、受け入れられた回答のチェックを一時的にオフにしました

4

10 に答える 10

28

ここには、密接に関連する2つの概念があります。

  1. 特定のオブジェクトにアクセスするためにプロセッサが必要とする配置
  2. コンパイラがオブジェクトをメモリに配置するために実際に使用する配置

構造体部材の位置合わせ要件を確実にするには、構造体の位置合わせは、少なくともその最も厳密な部材の位置合わせと同じくらい厳密でなければなりません。これは標準で明示的に説明されているとは思いませんが、次の事実から推測できます(標準では個別に説明されています)。

  • 構造体は、そのメンバー間(および最後に)にパディングを付けることができます
  • 配列の要素間にパディングを含めることはできません
  • 任意の構造タイプの配列を作成できます

構造体の配置が少なくともその各メンバーほど厳密でない場合、一部の構造体メンバーの一部の要素が適切に配置されないため、構造体の配列を作成できません。

ここで、コンパイラーは、メンバーの配置要件に基づいて構造体の最小配置を確保する必要がありますが、必要以上に厳密な方法でオブジェクトを配置することもできます。これは、パフォーマンス上の理由から行われることがよくあります。たとえば、最近の多くのプロセッサでは、任意の配置で32ビット整数へのアクセスが許可されますが、4バイト境界に配置されていない場合、アクセスは大幅に遅くなる可能性があります。

コンパイラはターゲットプロセッサのアライメント要件を明らかに知っているため、この情報を拡張機能として公開できますが、これは言語によって公開されないため、特定のタイプに対してプロセッサによって強制されるアライメントを決定する移植可能な方法はありません。

また、(少なくともCでは)コンパイラが実際にオブジェクトを整列する方法を決定する移植可能な方法はありませんが、多くのコンパイラには、整列をある程度制御するオプションがあります。

于 2008-12-12T23:29:53.030 に答える
14

この型特性コードを作成して、任意の型の配置を決定しました(すでに説明したコンパイラー規則に基づいています)。あなたはそれが役に立つと思うかもしれません:

template <class T>
class Traits
{
public:
    struct AlignmentFinder
    {
        char a; 
        T b;
    };

    enum {AlignmentOf = sizeof(AlignmentFinder) - sizeof(T)};
};

だから今あなたは行くことができます:

std::cout << "The alignment of structure S is: " << Traits<S>::AlignmentOf << std::endl;
于 2008-12-13T00:29:56.200 に答える
7

次のマクロは、任意の型 (構造体であっても) のアラインメント要件を返します。

#define TYPE_ALIGNMENT( t ) offsetof( struct { char x; t test; }, test )

注: このアイデアは、過去のある時点でマイクロソフトのヘッダーから借用したものと思われます...


編集: Robert Gamble がコメントで指摘しているように、このマクロの動作は保証されていません。実際、コンパイラが要素を構造体にパックするように設定されていると、うまく機能しません。したがって、使用する場合は注意して使用してください。

一部のコンパイラには、型のアラインメントを取得できる拡張機能があります (たとえば、VS2002 以降、MSVC には__alignof()組み込み関数があります)。利用可能な場合は、それらを使用する必要があります。

于 2008-12-13T01:44:14.397 に答える
3

使用されているコンパイラオプションの詳細がわかっている場合は、構造アラインメントを想定することができます。たとえば、#pragma pack(1)は、一部のコンパイラのバイトレベルでのアライメントを強制します。

補足:質問は位置合わせに関するものでしたが、副次的な問題はパディングです。組み込みプログラミング、バイナリデータなどの場合-一般に、可能であれば構造アラインメントについては何も想定しないでください。構造体で必要に応じて、明示的なパディングを使用してください。パディング要素を追加せずに、あるコンパイラで使用されている正確な配置を別のプラットフォームのコンパイラに複製することが不可能な場合がありました。これは、構造内の構造の配置に関係しているため、パディング要素を追加すると修正されました。

于 2008-12-12T23:31:37.503 に答える
3

他の人が述べたように、その実装は依存しています。Visual Studio 2005は、デフォルトの構造アラインメントとして8バイトを使用します。内部的には、アイテムはサイズによって整列されます-floatは4バイトの整列、doubleは8を使用するなどです。

#pragmapackを使用して動作をオーバーライドできます。GCC(およびほとんどのコンパイラ)には、同様のコンパイラオプションまたはプラグマがあります。

于 2008-12-12T23:36:02.657 に答える
2

Windows の特定のケースでこれを見つけたい場合は、windbg を開きます。

Windbg.exe -z \path\to\somemodule.dll -y \path\to\symbols

次に、実行します。

dt somemodule!CSomeType
于 2008-12-13T01:57:44.557 に答える
1

私は主にポール・ベッツ、ライアン、ダンに同意します。実際、それは開発者次第です。Robertが指摘したデフォルトの配置symanicを維持するか(Robertの説明は単なるデフォルトの動作であり、強制または必須ではありません)、または任意の配置を設定できます/ Zp [# #]。

これが意味するのは、float、long double、ucharなどのtypedefがある場合、さまざまな配列の組み合わせが含まれているということです。次に、これらの奇妙な形のメンバーのいくつかを持ち、1バイト、次に別の奇妙なメンバーを持つ別のタイプがあります。これは、make/solutionファイルが定義する任意の設定で単純に整列されます。

前述のように、実行時にwindbgのdtコマンドを使用すると、コンパイラがメモリ内の構造をどのようにレイアウトしたかを確認できます。

dia2dumpなどの任意のpdb読み取りツールを使用して、pdbから静的にこの情報を抽出することもできます。

于 2009-02-21T02:54:17.790 に答える
1

どのC規格でも、メモリレイアウトは保証されていないと思います。これはベンダーとアーキテクトに大きく依存します。90%のケースで機能する方法があるかもしれませんが、それらは標準ではありません。

間違っていることが証明されてとてもうれしいですが=)

于 2008-12-12T23:28:29.087 に答える
1

Peeter Joot のブログから変更

C 構造体のアラインメントは、少なくとも一般的に、構造体の最大サイズのネイティブ型に基づいています (例外は、32 ビット アラインメントのみが必要な win32 で 64 ビット整数を使用するようなものです)。

char と char の配列しかない場合、int を追加すると、その int は 4 バイトの境界から開始されます (int メンバーの前に隠しパディングがある可能性があります)。さらに、構造体が sizeof(int) の倍数でない場合、隠しパディングが最後に追加されます。short 型と 64 ビット型の場合も同じです。

例:

struct blah1 {
    char x ;
    char y[2] ;
};

sizeof(blah1) == 3

struct blah1plusShort {
    char x ;
    char y[2] ;
    // <<< hidden one byte inserted by the compiler here
    // <<< z will start on a 2 byte boundary (if beginning of struct is aligned).
    short z ;
    char w ;
    // <<< hidden one byte tail pad inserted by the compiler.
    // <<< the total struct size is a multiple of the biggest element.
    // <<< This ensures alignment if used in an array.
};

sizeof(blah1plusShort) == 8

于 2012-10-20T17:55:38.023 に答える