2017-02-03 9 views
4

test_func次のスニペットは、2つの引数が部分的に重なっている場合、厳密なエイリアシング規則で未定義の動作を引き起こしますか?第二引数である構造体メンバへのポインタを使用する場合、厳密なエイリアシングが適用されますか?

は最初のメンバーである:

#include <stdio.h> 

typedef struct 
{ 
    //... Other fields 
    int x; 
    //... Other fields 
} A; 

int test_func(A *a, int *x) 
{ 
    a->x = 0; 
    *x = 1; 
    return a->x; 
} 

int main() 
{ 
    A a = {0}; 

    printf("%d\n", test_func(&a, &a.x)); 

    return 0; 
} 

A*int*がエイリアスではないだろうという仮定に基づいて、test_funcがちょうど0を返すだろうと思いを許可コンパイラですか? *xはメンバーを上書きできませんか?

+1

これはCのキーワード['restrict'](https://en.wikipedia.org/wiki/Restrict)の目的ではないと思います。 – Stargateur

+0

*厳密なエイリアシング*は、型が同じサイズであると仮定して、ポインタ型間で変換する場合例えば、 'int *'を 'float *'に変換するには、 'sizeof int == sizeof float'のプラットフォームで行います。そして、私は、この制限は、各プラットフォームに異なるアライメント要件があることが原因であると考えました。特定のシナリオでは未定義の動作につながる可能性があります。私はあなたのコード内にそれが見当たらない。 –

+2

大まかには、ポインタを別の型にキャストした場合、または 'void *'に変換して別の型に変換した場合、 ''厳密なエイリアシング ''違反しかないと言うことができます。ここでは、すべての型情報がコンパイラに存在するため、問題を作成してはいけません。 –

答えて

4

これは厳密なエイリアシングに違反していません。厳密なエイリアシング規則では、互換性のある型のlvalue式を使用してのみオブジェクトの値にアクセスできる(簡略化されています)。この場合、アクセスするオブジェクトはのmainaという変数です。このメンバーはタイプintです。また、それにアクセスするために使用する式(*x)には、タイプintがあります。だから問題はない。

厳密なエイリアシングをrestrictと混同している可能性があります。 restrictキーワードをポインタパラメータの宣言に使用した場合、restrictは異なるポインタを使用して同じオブジェクトにアクセスできないため、コードは無効になりますが、これは厳密なエイリアシングとは別の問題です。

+0

gccが 'struct foo {int x;};で指定された厳密なエイリアシングを解釈する方法。構造体バー{int x;};共用体u {struct foo vf;構造体バーfb;}; int test(struct foo * pf; struct bar * pb){pf-> x = 1; pb-> x = 2; pf-> xを返します。 'pf-> x'と' pb-> x'は、両方の構造体が完全な定義がアクセスポイントで見える同じ型のメンバであるにもかかわらず、エイリアスできないというコードを生成します。私は、標準がそのような振る舞いを許すために合理的に解釈できるとは思っていません(「完全な組合タイプ」が見えるという無意味なものになるでしょう)... – supercat

+0

... gccの作者は、 (たとえユニオンタイプが可視であっても、問題のポインタによって識別されたオブジェクトが、たとえユニオンタイプとして宣言されていても、そのように宣言されていても)、ユニオンタイプのポインタによって作られないアクセスのためのCISルールをサポートすること同じ組合のメンバー)。 – supercat

5

Strict aliasingは、ポインタが別のポインタ型に変換された後、その内容がアクセスされるときに参照されます。厳密なエイリアシングとは、関与するポイントタイプが互換性がなければならないことを意味します。それはここでは当てはまりません。

しかし、ポインタエイリアシングとは、2つのポインタが同じメモリを参照できるという意味です。コンパイラは、これがここに当てはまると想定することは許されません。あなたが記述したような最適化をしたいのであれば、それらが同じかどうかを判断するために、ポインタを互いに比較するマシンコードを追加する必要があります。それ自身では機能が少し遅くなります。

コンパイラがそのようなコードを最適化するのを助けるために、ポインタがrestrictとして宣言できます。コンパイラは、ポインタが同じメモリを指していないことをプログラマが保証します。基本的には「1にa.x設定」で(インライン化)関数全体が交換されたことを意味し

0x00402D09 mov $0x1,%edx 

:このマシンコードでgcc -O3結果でコンパイルさ

あなたの機能。

しかし、私は

int test_func(A* restrict a, int* restrict x) 
{ 
    a->x = 0; 
    *x = 1; 
    return a->x; 
} 

としてあなたの関数を書き換え、gcc -O3でコンパイルする場合、私はa->Xxが同じメモリを指していませんので、それができるように、コンパイラが今言ったので、それは0を返すん*x = 1;が結果に影響を与えず、*x = 1;行をスキップするか、またはa->x = 0;行の前にそれをシーケンスするとします。

制限バージョンの最適化されたマシンコードは、実際には関数呼び出し全体をスキップします。これは、値が初期化時にすでに0であることがわかっているためです。

これはもちろんバグですが、プログラマはrestrictの不注意な使用のためにそれを責めます。

関連する問題