11

セクション §6.5.3.2「アドレスと間接演算子」¶3 は述べています (関連するセクションのみ):

単項 & 演算子は、そのオペランドのアドレスを返します。... オペランドが単項演算子の結果である場合、*その演算子も演算子も&評価されず、演算子に対する制約が適用され、結果が左辺値ではないことを除いて、結果は両方が省略されたかのようになります。同様に、オペランドが演算子の結果である場合、演算[]子もによって暗示される&単項も評価されず、結果は、演算子が削除され、演算子が演算子に変更されたかのようになります。...*[]&[]+

これは、次のことを意味します。

#define NUM 10
int tmp[NUM];
int *i = tmp;
printf("%ti\n", (ptrdiff_t) (&*i - i) );
printf("%ti\n", (ptrdiff_t) (&i[NUM] - i) );

完全に合法で、0 とNUM(10) を出力する必要があります。標準は、これらのケースの両方を最適化する必要があることを非常に明確に示しています。

ただし、以下を最適化する必要はないようです。

struct { int a; short b; } tmp, *s = tmp;
printf("%ti\n", (ptrdiff_t) (&s->b - s) );

これはひどく矛盾しているようです。sizeof(int)上記のコードがプラスの (ありそうにない) パディング (おそらく 4)を出力してはならない理由はわかりません。

式を単純化することは、単純なアドレスとオフセットで&->ある と概念的に (IMHO) 同じになります。これは、オペレーター&[]を使用して実行する可能性があるのではなく、コンパイル時に決定できるオフセットですらあります。[]

これが一見矛盾しているように見える理由について、何か根拠はありますか?

4

3 に答える 3

4

あなたの例では、&i[10]は実際には合法ではありません。それはi + 10、になりNULL + 10、はになり、nullポインタに対して算術演算を実行することはできません。(6.5.6 / 8は、ポインタ演算を実行できる条件を示しています)

とにかく、このルールはC99で追加されました。C89には存在しませんでした。私の理解では、次のような明確に定義されたコードを作成するために大部分が追加されました。

int* begin, * end;
int v[10];

begin = &v[0];
end = &v[10];

この最後の行は、C89(およびC ++)では技術的に無効ですが、このルールのためにC99では許可されています。一般的に使用される構成を明確にしたのは、比較的小さな変更でした。

ヌルポインタに対して算術演算を実行できないため、例(&s->b)はとにかく無効になります。

なぜこの「矛盾」があるのか​​、私には推測できます。一貫性を持たせることを考えた人はいないか、このための説得力のあるユースケースを見た人はいない可能性があります。これが考慮され、最終的に拒否された可能性があります。理論的根拠&*の削減についての意見はありません。WG14の論文でいくつかの決定的な情報を見つけることができるかもしれませんが、残念ながら、それらは非常に不十分に編成されているように見えるので、それらをトロールするのは面倒かもしれません。

于 2011-02-05T07:11:52.617 に答える
2

このルールは最適化の目的で追加されたのではなく (as-if ルールが追加しないことで何がもたらされるのでしょうか?)、それがなければ未定義の動作になる可能&t[sizeof(t)/sizeof(*t)]&*(t+sizeof(t)/sizeof(*t))があります (そのようなものを直接記述するのはばかげているように思えるかもしれませんが、レイヤーを 1 つまたは 2 つ追加すると、それが理にかなっている可能性があります)。特別なケーシング &p->m がそのような利点をもたらすケースは見当たりません。James が指摘したように&p[10]、 pa null ポインターはまだ未定義の動作であることに注意してください。&p->mpa ヌル ポインターを使用した場合も同様に無効のままでした (また、p がヌル ポインターの場合は使用が見られないことを認めなければなりません)。

于 2011-02-05T07:51:26.900 に答える
1

コンパイラーはさまざまな方法でパックすることを選択できると思います。おそらく、構造体のメンバー間にパディングを追加して、メモリーアクセス速度を向上させます。これは、それが常に4 のオフセットにbなると断言できないことを意味します。単一の値には同じ問題はありません。

また、コンパイラは最適化フェーズ中にメモリ内の構造体のレイアウトを認識しない場合があるため、構造体メンバー アクセスとその後のポインター キャストに関するあらゆる種類の最適化が妨げられます。


編集:

私には別の理論があります...

多くの場合、コンパイラは字句解析と解析の直後に抽象構文ツリーを最適化します。これは、キャンセルする演算子や定数に評価される式などを見つけて、ツリーのそれらのセクションを 1 つのノードに減らすことを意味します。これは、構造体に関する情報が利用できないことも意味します。一部のコード生成後に発生する最適化パスでは、追加情報があるためこれを考慮できる場合がありますが、AST のトリミングなどについては、その情報はまだありません。

于 2011-02-05T06:54:24.170 に答える