特定の型 (たとえばint
、char
、float
、 ..) へのポインターがインクリメントされると、その値はそのデータ型のサイズだけ増加します。void
サイズのデータを指すポインターがインクリメントされる場合x
、どのようにしてx
バイト先を指すようになりますか? x
コンパイラーは、ポインターの値に追加することをどのように認識しますか?
9 に答える
最終的な結論: a の演算はvoid*
、C と C++ の両方で違法です。
GCC では、拡張機能としてこれを使用できます。 - および関数ポインターの算術演算をvoid
参照してください(このセクションは、マニュアルの「C 拡張機能」の章の一部であることに注意してください)。Clang と ICCvoid*
では、GCC との互換性のために算術演算が許可されている可能性があります。他のコンパイラ (MSVC など) は での算術演算を許可しません。GCCは、フラグが指定されている場合、またはフラグが指定されvoid*
ている場合は許可しません(このフラグは、コード ベースも MSVC でコンパイルする必要がある場合に役立ちます)。-pedantic-errors
-Werror-pointer-arith
C標準は語る
引用は n1256 ドラフトから取得されます。
加算操作の標準の説明には、次のように記載されています。
6.5.6-2: 加算の場合、両方のオペランドが算術型であるか、一方のオペランドがオブジェクト型へのポインタで、もう一方が整数型である必要があります。
したがって、ここでの問題void*
は、 が「オブジェクト型」へのポインタであるかどうか、または同等に「オブジェクト型」であるかどうかvoid
です。「オブジェクト タイプ」の定義は次のとおりです。
6.2.5.1: 型は、オブジェクト型(オブジェクトを完全に記述する型)、関数型(関数を記述する型)、および不完全型(オブジェクトを記述するが、サイズを決定するために必要な情報が不足している型) に分割されます。
また、標準では次のように定義void
されています。
6.2.5-19:
void
型は値の空のセットで構成されています。完成できない不完全型です。
は不完全型なのでvoid
、オブジェクト型ではありません。したがって、これは加算演算の有効なオペランドではありません。
したがって、ポインターに対してポインター演算を実行することはできませんvoid
。
ノート
当初void*
は、C 標準の次のセクションにより、算術演算が許可されていると考えられていました。
6.2.5-27: voidへのポインターは、文字型へのポインターと同じ表現およびアラインメント 要件を持たなければなりません。
でも、
同じ表現とアラインメントの 要件は、関数への引数、関数からの戻り値、および共用体のメンバーとしての互換性を暗示するものです。
したがって、これはが型またはをprintf("%s", x)
持っていても同じ意味を持つことを意味しますが、 で算術演算を実行できるという意味ではありません。x
char*
void*
void*
ポインタ演算はポインタでは許可されていませんvoid*
。
それを char ポインターにキャストし、ポインターを x バイト先にインクリメントします。
void *
まさにこの理由で、型に対してポインター演算を行うことはできません!
void ポインタは、任意のメモリ チャンクを指すことができます。したがって、コンパイラは、void ポインターでポインター演算を試行するときに、インクリメント/デクリメントするバイト数を知りません。したがって、void ポインターは、ポインター演算に関与する前に、まず既知の型に型キャストする必要があります。
void *p = malloc(sizeof(char)*10);
p++; //compiler does how many where to pint the pointer after this increment operation
char * c = (char *)p;
c++; // compiler will increment the c by 1, since size of char is 1 byte.
ポインター演算を行う前に、別の型のポインターにキャストする必要があります。
コンパイラは型キャストで認識します。与えられたvoid *x
:
x+1
に 1 バイトを加算しx
、ポインタはバイトに移動しますx+1
(int*)x+1
バイトを追加しsizeof(int)
、ポインターはバイトに移動しますx + sizeof(int)
(float*)x+1
アドレスsizeof(float)
バイトなど
最初の項目は移植性がなく、C/C++ の Galateo に反していますが、それでも C 言語に正確です。つまり、ほとんどのコンパイラで何かにコンパイルされ、適切なフラグ (-Wpointer-arith など) が必要になる可能性があります。