2016-03-16 11 views
17

Cで定義されている割り切れないポインタアドレスの減算ですか? C++で?割り切れないポインタアドレスの減算

void* p = malloc(64); 

int* one = (int*)((char*)p); 
int* two = (int*)((char*)p + 7); 

printf("%x %x %d %d\n", one, two, sizeof(int), two - one); 

Ideone link

は、ここでの例です。

出力が8a94008 8a9400f 4 1になるので、除算を行い残りを切り捨てるように見えます。行動は定義されていますか?

+8

間違った型のデータを 'printf()'に渡して、*未定義の動作*を呼び出しました。 printf( "%p%p%zu%td \ n"、(void *)one、(void *)two、sizeof(int)、two - one); ' – MikeCAT

+1

私はしていません引用がある。ポインタ演算は整数演算に基づいており、よく定義されています。ですから、私は 'two-one'がよく定義されていると思います。合理的な人のようにポインタが詰め込まれていなくても、期待するでしょう。一方、これをしないでください、誰もあなたのコードベースでこれを見つけることはありません。 – Johannes

+3

あなたのプログラムでポインタ 'two'を保持していても、ポインタが' int'の整列制約を満たさないため無効です(実際にはほとんどのハードウェアでは、実際に試して参照解除するまで – Thomas

答えて

20

これは5.7.6に従って未定義の動作である:

同じ配列オブジェクトの要素への2つのポインタが減算される場合、結果は2つの配列要素の添字の差です。 [...]両方のポインタが同じ配列オブジェクトの要素を指していないか、または が配列オブジェクトの最後の要素を1つ超えている場合を除き、その動作は未定義です。コードで

、ポインタtwoポインタone同じint配列の要素を指していません。実際には、要素のうちの1つ(それ自体は未定義の振る舞いです)の「中央」を指しているため、pの配列要素を指していません。いくつかの仮定の下

+4

これは当てはまりますが、UBは '(int *)((char *)p + 7)'で初めて発生します。 – user694733

+0

@ user694733無効なキャストによって参照がなくてもUBが発生することは確かですか?私は標準でこの効果に何かを見つけようとしていましたが、決定的な答えは見つかりませんでした。 – dasblinkenlight

+0

mallocから任意の組み込みポインタ型にポインタを変換できます。しかし、 '(char *)p + 7'のために' char * 'から' int * 'への変換は無効です。 – user694733

19

C 1、三行目:ポインタpが正しく、それが参照されるタイプのために整列されていないため

int* two = (int*)((char*)p + 7); 

は既に、未定義の動作を引き起こします。


仮定はint型のための位置合わせ要件がchar型の場合よりも高くなることです。これは、ほとんどのモダンアーキテクチャに当てはまります。すべての整列は2つの累乗でなければならないので、ポインタpにその値を加えてもint型の整列要件と同じ厳密な整列を持つポインタを生成することはできません。

(より引用:ISO/IEC 9899:201X 6.3.2.3ポインタ7)オブジェクトタイプに
ポインタが別のオブジェクト型へのポインタに変換することができます。結果のポインタが の参照先の型に対して正しく整列されていない場合、動作は 未定義です。
すべての有効 アライメント値は、2つの非負整数乗でなければならない

は(オブジェクト4の201X 6.2.8アラインメント:ISO/IEC 9899から引用します)。

+1

私は現時点では章と節を見つけることができませんが、C++にも同じことが起こります。 'two'の計算は未定義の動作になります。 (単語ベースのマシンにポインタ用の特別なレジスタがあるとすると、「2」を計算するとこれらのレジスタの1つを通過し、ポインタはミスアライメントされたポインターでバーフするかもしれません - そのようなマシンは過去に存在していました。 –