2013-06-18 13 views
7

私は配列計算のセクションをK & Rで読んでいて、好奇心をそそられました。文脈全体を掲載しましたが、主に大胆な部分に焦点を当てています。同じ配列のメンバーへC配列のためのポインタ演算

pとqのポイント場合は、==、 !=、<、> =、などのような関係は、正常に動作します。たとえば、pが をqよりも配列の以前のメンバを指す場合、p < qは真です。すべてのポインタは、同等性またはゼロとの不一致を有意義に比較した とすることができます。しかし、 の振る舞いは、同じ配列のメンバーを指していないポインタを持つ算術または比較では定義されていません。 (1つの例外があります: 配列の最後を過ぎた最初の要素のアドレスがポインタ演算で を使用することができます。)

は、この例外の理由は何ですか?サイズが定義されている場合、配列の最後に余分なメモリが割り当てられていますか?もしそうなら、どんな目的のために?ヌル文字で配列を終了しますか?

+1

これを理解する最も簡単な方法は、a [b] = a + bです。説明については、98ページをご覧ください。 – SheetJS

+2

@Nirk:ハァッか。それはこれと何が関係していますか? – jason

答えて

8

理由は、あなたがこのようなループ内でポインタをインクリメントすることができます:

char a[42], *p; 

for (p = a; p < &a[sizeof a]; p++) // or p != &a[sizeof a] 
{ 
    /* ... */ 
} 

余分なルールがなければ、ポインタが無効になるので、これは未定義の動作になります。

0

アレイの最後に余分なメモリが割り当てられていません。これは、あなたがあなたにポインターの算術演算の下で 'End'とマークされたアドレスができるということだけです。開始点は配列の最初の要素を指します。終わりは最初の要素を指し、末尾はです。そのサイズが定義されている場合

----------------- 
| | | | | 
----------------- 
^    ^
Begin   End 
4

は、任意の配列の最後に割り当てられたメモリの余分な部分ですか?

いいえ引用した文脈は重要です。あなたが太字であることの例外は、ポインタ算術(および関係)を参照することです。 でないポインタ間のポインタ関係をとすると、同じ配列のメンバを指していると、udbとなります。ただし、ポインタのどちらかが配列の最後を過ぎた最初の要素を指している場合は、1つの例外があります。

もしそうなら、どのような目的のためにですか?

null答えが誤っていると仮定しているためです。

ヌル文字で配列を終了しますか?

アレイの端との比較が合法であるように、この理由はaが配列である場合、すなわち、&a[sizeof a]との比較です。 &a[sizeof a]は、配列の最後を過ぎた最初の要素です。paの要素へのポインタである場合、または配列の末尾を過ぎた最初の要素の場合は、p&a[sizeof a]と比較することができます。

C99 specification、セクション6.5.8.5から引用します。

2つのポインタを比較すると、結果は、指し示されたオブジェクトのアドレス空間内の相対的な位置によって異なります。オブジェクト型または不完全型の2つのポインタが両方とも同じオブジェクトを指している場合、または両方が同じ配列オブジェクトの最後の要素を指している場合、それらは等しいと比較されます。指し示されたオブジェクトが同じ集約オブジェクトのメンバである場合、後で宣言された構造体メンバへのポインタは、構造体の前に宣言されたメンバへのポインタよりも大きく、添字値が大きい配列要素へのポインタは、添字の値は低くなります。式Pが配列オブジェクトの要素を指しており、式Qが同じ配列オブジェクトの最後の要素を指している場合、ポインタ式Q + 1Pより大きいです。他のすべてのケースでは、その動作は未定義です。

0

あなたは、アレイの終わりから離れたオブジェクトのアドレスを計算することが許されているだけで、問題を起こすことはないと約束しています。あなたはそのポインタを逆参照できません。

この約束が重要なところの一例は、オブジェクトがメモリの最後に割り当てられる場合があるため、アドレスを計算したときに末尾のアドレスが算術オーバーフローを引き起こす場合があります。その配列を通してポインタを反復する場合、最後の反復の後、算術オーバーフローのためにポインタが折り返し、NULLを指すことになります。

この結果、比較結果が反転し、配列境界チェッカーであらゆる種類のアラームベルをトリップする可能性があります。または、CPUが飽和演算などを使用している場合は、単純に間違ったアドレスを計算できます。

コンパイラとリンカは、このようなことが起こらないようにする必要があります。コンパイラとリンカの責任がその単純なケースに限定されていることを保証することはプログラマの義務であり、 n要素を最後まで実行した場合、同じ保証を維持する必要があります。