1. ホーム
  2. c++

[解決済み] 構造体へのインデックス作成は合法ですか?

2022-10-20 17:57:40

質問

コードがどれだけ「悪い」かに関わらず、またアライメントなどがコンパイラやプラットフォームの問題ではないと仮定して、これは未定義または壊れた動作なのでしょうか?

もし私がこのような構造体を持っているとしたら :-)

struct data
{
    int a, b, c;
};

struct data thing;

それは 法的 にアクセスすることは a , bc として (&thing.a)[0] , (&thing.a)[1] そして (&thing.a)[2] ?

すべてのケースで、私が試したすべてのコンパイラとプラットフォームで、私が試したすべての設定で、それは「動きました」。私はただ、コンパイラーが b thing[1] は同じもので、'b'へのストアはレジスタに入れられ、thing[1]はメモリから間違った値を読み取るかもしれません(例えば)。しかし、私が試したすべてのケースでそれは正しいことをしました。(もちろん、それはあまり証明されていないことを認識しています)。

これは私のコードではなく、私が作業しなければならないコードなのですが、これが 悪い コードなのか 壊れた のコードを変更する優先順位に大きな影響を与えるためです :)

タグ付けされたCとC++ 。私は主にC + +に興味があるが、それが異なっている場合は、単に興味のために、Cも。

どのように解決するのですか?

違法です 1 . これはC++のUndefinedな動作です。

メンバーを配列方式で取っていますが、C++の規格では以下のように書かれています(強調)。

[dcl.array/1]のようになります。 : ...arrayタイプのオブジェクトには 連続的に の空でない集合を含む。 サブオブジェクトが含まれます.

しかし、メンバーについては、そのような 連続した の要件はありません。

[class.mem/17]を参照してください。 : ...;実装アライメント要件 は、隣接する2つの メンバが互いにすぐには割り当てられないかもしれません。 ...

上記の2つの引用は、なぜ struct へのインデックス付けが C++ 標準で定義された動作でないことを示すのに十分ですが、1 つの例を挙げてみましょう。 (&thing.a)[2] - 添え字演算子について。

[expr.post//expr.sub/1]です。 : 後置式の後に角括弧で囲まれた式が続くのは 後置表現である。式の一方は "Tの配列 "型のglvalueでなければならない。 「Tの配列 "または "Tへのポインタ "型のprvalueでなければならず,他方はスコープされていない列挙型または は,スコープされていない列挙型又は積分型のprvalueでなければならない。その結果は は,型 "T "である。型 "T "は,完全に定義されたオブジェクト型でなければならない。 式は E1[E2] とは(定義上)同一である。 ((E1)+(E2))

上の引用文の太字部分を掘り下げると、ポインタ型に積分型を追加することについて(ここで強調することに注意してください)...。

[expr.add/4]です。 : 積分型を持つ式がポインタに加算されたり、ポインタから減算されたりすると、結果はポインタのオペランドの型を持ちます。 ポインタに加算または減算されると、結果はポインタオペランドの型になります。 もし が 式 P は要素を指す x[i] 配列 オブジェクト x をn個の要素で表現すると P + JJ + P (ここで J には の値は j ) は(仮説の可能性がある)要素を指す。 x[i + j] もし 0 ≤ i + j ≤ n ; さもなければ の場合、動作は未定義です。...

注意点として 配列 の要件に注意してください。 もし 句の要件を満たし、それ以外の場合は でなければ を引用しています。という表現は (&thing.a)[2] は明らかに もし 節に該当しません。したがって、未定義の動作です。


余談ですが、私はこのコードとそのバリエーションをさまざまなコンパイラで広範囲に実験しましたが、ここではパディングは導入されませんでした(それは は機能します。 メンテナンスの観点からは、このコードは非常に壊れやすいものです。これを行う前に、実装が連続的にメンバーを割り当てていることを確認する必要があります。そして、インバウンズを維持することです :-)。 でも、まだ未定義のままなんですよね...。

いくつかの実行可能な回避策 (定義された動作を伴う) が他の回答によって提供されています。



コメントで正しく指摘されているように [basic.lval/8]です。 は、前の編集にあったものは適用されません。2501さん、@M.M.さんありがとうございます。

1 : この質問に対する @Barry の回答で、唯一合法的にアクセスできるケースを紹介しています。 thing.a メンバにアクセスできる唯一の合法的なケースについては、@Barry の回答を参照してください。