17

次のコードを検討してください。

typedef struct {
  int type;
} object_t;

typedef struct {
  object_t object;
  int age;
} person_t;

int age(object_t *object) {
  if (object->type == PERSON) {
    return ((person_t *)object)->age;
  } else {
    return 0;
  }
}

これは合法的なコードですか、それとも C99 の厳密なエイリアシング規則に違反していますか? 合法/違法の理由を教えてください。

4

4 に答える 4

17

厳密なエイリアシングルールは、メモリ内の同じ場所を参照する異なるタイプの2つのポインタ(ISO / IEC9899 / TC2)です。この例では、のアドレスをのアドレスとして再解釈しますがobject_t object、の境界を越えて配置されているため、再解釈されたポインタを介してperson_t内部のメモリ位置を参照しません。ポインタを介して参照されるメモリ位置は同じではないため、厳密なエイリアシング規則に違反していないと言えます。FWIWは、その評価に同意しているようであり、警告を生成しません。object_tageobject_tgcc -fstrict-aliasing -Wstrict-aliasing=2 -O3 -std=c99

ただし、これは正当なコードであると判断するのに十分ではありません。この例では、ネストされた構造のアドレスがその外部構造のアドレスと同じであると想定しています。ちなみに、これはC99標準に従って行うのに安全な仮定です。

6.7.2.1-13。適切に変換された構造体オブジェクトへのポインタは、その初期メンバーを指します

上記の2つの考慮事項により、あなたのコードは合法であると私は思います。

于 2011-12-07T14:42:47.437 に答える
3

http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html

受け入れられた回答への追加として、標準からの完全な引用を次に示します。重要な部分は強調されており、他の回答が省略されています。

6.7.2.1-13: 構造体オブジェクト内で、ビットフィールド以外のメンバーとビットフィールドが存在するユニットには、宣言された順序で増加するアドレスがあります。適切に変換された構造体オブジェクトへのポインターは、その最初のメンバー (または、そのメンバーがビットフィールドの場合は、それが存在するユニット) を指し、その逆も同様です。構造体オブジェクト内に名前のないパディングがある場合がありますが、先頭にはありません。

6.3.2.3-7: オブジェクトまたは不完全型へのポインタは、別のオブジェクトまたは不完全型へのポインタに変換される場合があります。結果のポインターがポイント先の型に対して正しく配置されていない場合、動作は未定義です。それ以外の場合、再度変換すると、結果は元のポインターと等しくなります。[...]

あなたの例は、void ポインターに最適な場所であることがわかりました。

int age(void *object) {

なんで?あなたの明らかな意図は、そのような関数にさまざまな「オブジェクト」タイプを与えることであり、エンコードされたタイプに従って情報を取得するためです。あなたのバージョンでは、関数を呼び出すたびにキャストが必要です: age((object_t*)person);。間違ったポインタを渡してもコンパイラは文句を言わないので、型安全性は関係ありません。次に、void ポインターを使用して、関数を呼び出すときにキャストを回避することもできます。

age(&person->object)もちろん、関数を で呼び出すこともできます。あなたがそれを呼び出すたびに。

于 2011-12-07T16:59:06.257 に答える
2

厳密なエイリアシング ルールは、オブジェクト (メモリ領域) にアクセスする型によって制限します。コード内でルールが発生する可能性がある場所がいくつかあります: 内age()および呼び出し時age()です。

内で、検討ageする必要がありますobject((person_t *)object)オブジェクト型を持ち、オブジェクト (メモリの領域) を指定するため、左辺値式です。ただし、分岐は の場合object->type == PERSONにのみ到達するため、(おそらく) オブジェクトの有効な型は aperson_t*であるため、キャストは厳密なエイリアシングに違反しません。特に、厳密なエイリアシングにより、次のことが可能になります。

  • オブジェクトの有効な型と互換性のある型、

を呼び出すときage()、おそらく、object_t*または から派生した型:最初のメンバーとしてobject_tを持つ構造体を渡します。object_tこれは次のように許可されます。

  • 前述の型の 1 つをメンバーに含む集約型または共用体型

さらに、厳密なエイリアシングのポイントは、レジスタへの値のロードを最適化できるようにすることです。オブジェクトが 1 つのポインターを介して変更された場合、互換性のない型のポインターが指すものはすべて変更されていないと見なされるため、再ロードする必要はありません。コードは何も変更しないため、最適化の影響を受けません。

于 2011-12-07T14:54:26.680 に答える
0

標準で明示的に認められている許容可能な方法の1つは、次のように、同じ初期セグメントを持つ構造体の結合を作成することです。

struct tag  { int value;                };
struct obj1 { int tag;    Foo x; Bar y; };
struct obj2 { int tag;    Zoo z; Car w; };

typedef union object_
{
  struct tag;
  struct obj1;
  struct obj2;
} object_t;

これで、合格して免責でobject_t * p審査p->tag.valueし、目的の組合員にアクセスできます。

于 2011-12-07T14:43:30.993 に答える