5

Speexを使用して発生している問題をデバッグしようとしているときに、Speexだけでなく、いくつかのサンプルコードも次のように動作することに気付きました。

  • 初期化関数からEncStateへのポインタを返します
  • そのポインタをvoidポインタにキャストします
  • voidポインタを格納します
  • (他の場所)
  • voidポインタをSpeexModeへのポインタへのポインタにキャストします
  • ポインタを間接参照します

の定義はEncStateタイプのフィールドで始まるSpeexMode *ため、最初のフィールドへのポインタと構造体へのポインタの整数値はたまたま同じです。逆参照は実行時に機能します。

しかし...言語は実際にこれを許可していますか?コンパイラがこれをコンパイルする場合、コンパイラは自由にやりたいことができますか?C`の場合、構造体T*を構造体のC*未定義動作にキャストしていますか?T''s first field is a

4

2 に答える 2

8

C11標準から:

(C11§6.7.2.1.15:「適切に変換された構造体オブジェクトへのポインターは、その最初のメンバーを指します...その逆も同様です。構造体オブジェクトとして名前のないパディングがある場合がありますが、最初はありません。」)

これは、表示される動作が許可され、保証されていることを意味します。

于 2013-01-24T21:20:06.023 に答える
-1

標準のすべてのバージョンは、多くのエイリアシング構造のサポートを実装品質の問題として扱ってきました。これは、すべての有用な構造をサポートし、有用な最適化をブロックせず、すべてのコンパイラでサポートできるルールを作成することは本質的に不可能だったためです。大幅な手直しなし。次の関数について考えてみます。

struct foo {int length; int *dat; };

int test1(struct foo *p)
{
  int *ip = &p->length;
  *ip = 2;
  return p->length;      
}

struct foo型のオブジェクトがへの割り当てによって影響を受ける可能性を処理するために、高品質のコンパイラが期待されるべきであることはかなり明らかだと思います*ip。一方、次の関数について考えてみます。

void test2(struct foo *p)
{
    int i;
    for (i=0; i < p->length; i++)
        p->dat[i] = 0;
}

コンパイラは、書き込みがp->dat[i]の値に影響を与える可能性を考慮に入れる必要がありますか?たとえば、少なくともループの最初の反復後にp->lengthの値をリロードすることによってですか?p->length

委員会の一部のメンバーは、コンパイラーがそのような許可をすることを要求することを意図したと思いますが、すべてがそうだったとは思いません。タイプのオブジェクトにアクセスしますがstruct foointその中にはありません。省略は偶然だと思う人もいるかもしれませんが、コンパイラーは、あるコンテキストで特定のタイプとしてアクセスされるオブジェクトには、目に見える関連性を持つ左辺値によってアクセスする必要があるとルールを解釈するという期待に基づいていたと思います。そのコンテキスト内で、リストされたタイプの1つのオブジェクトを使用します。何が「目に見える関連」を構成するのかという問題は、標準の管轄外のQoIの問題として残されましたが、コンパイラの作成者は、実用的な場合に関連を認識するために合理的な努力をすることが期待されていました。

のような関数内ではtest1、型の左辺値がpを導出するためipに使用され、形成とその最後の使用の間でpアクセスするために他の方法で使用されることはありません。したがって、コンパイラーは、関連のない構造体のメンバーにアクセスするために型のポインターを使用するための包括的な許可を与える一般的な規則がなくても、後で読み取るためにストアを並べ替えることができないことを認識するのに問題はありません。ただし、内では、のアドレスをポインタの計算に使用できた可能性のある目に見える手段はありません。したがって、最も一般的な目的を目的としたコンパイラを最適化して、p->lengthip*ipp->lengthint*inttest2p->lengthp->datp->lengthその値が変更されないことを期待してループの前。

clangとgccは、ポインターの派生元のオブジェクトのタイプを認識するための努力をするのではなく、代わりに、標準がそれらのタイプのポインターを使用してstruct(unionではない!)メンバーにアクセスする一般的な許可を与えるかのように動作することを選択します。これは許容されますが、標準では必須ではありません(準拠しているが、ガベージ品質の実装はtest1任意の無意味な方法で処理できます)が、ポインターの導出に対する盲目は、プログラマーが使用できる構造の範囲を不必要に制限し、有用なものを放棄する必要がありますによって例示されるような最適化test2()

全体として、Cでのエイリアシングに関連するほとんどすべての質問に対する正解は、「実装の品質の問題です」です。clangとgccが何をするかについての観察は-fstrict-aliasing、それらのコンパイラーのモードをなだめる必要があるが、標準が実際に言っていることとはほとんど関係がない人々にとって役立つかもしれません。

于 2021-02-20T19:30:58.620 に答える