特定の型へのポインター(たとえば、int
、char
、float
、..)が増分されると、そのデータ型のサイズだけ値が増加します。サイズvoid
のデータを指すx
ポインターがインクリメントされた場合、x
バイト先を指す方法は?コンパイラは、x
をポインターの値に追加することをどのように知っていますか?
最終的な結論:void*
の算術は、CとC++の両方でillegalです。
GCCはこれを拡張機能として許可しています。 void
-およびFunction-Pointersの算術演算 を参照してください(このセクションはマニュアルの「C拡張機能」の章の一部です)。 ClangとICCでは、GCCとの互換性のためにvoid*
算術演算を許可する可能性があります。他のコンパイラ(MSVCなど)は、void*
での算術を許可しません。また、-pedantic-errors
フラグが指定されている場合、または-Werror-pointer-arith
フラグが指定されている場合、GCC baseもMSVCでコンパイルする必要があります)。
引用は、n1256ドラフトから取られています。
加算操作状態の規格の説明:
6.5.6-2:さらに、両方のオペランドが算術型を持つか、一方のオペランドがオブジェクト型へのポインタで、もう一方が整数型でなければなりません。
したがって、ここでの質問は、void*
が「オブジェクト型」へのポインタであるかどうか、または同等に、void
が「オブジェクト型」であるかどうかです。 「オブジェクトタイプ」の定義は次のとおりです。
6.2.5.1:型は、オブジェクト型(オブジェクトを完全に記述する型)、関数型(関数を記述する型)に分割されます、および不完全なタイプ(オブジェクトを説明するが、サイズを決定するために必要な情報を欠くタイプ)。
また、標準ではvoid
を次のように定義しています。
6.2.5-19:
void
タイプは、空の値セットで構成されます。完了できない不完全なタイプです。
void
は不完全な型であるため、オブジェクト型ではありません。したがって、加算演算の有効なオペランドではありません。
したがって、void
ポインターに対してポインター演算を実行することはできません。
もともと、C標準の以下のセクションのために、void*
算術が許可されていると考えられていました。
6.2.5-27:voidへのポインタには、文字型へのポインタと同じ同じ表現と配置要件が必要です。
しかしながら、
同じ表現とアライメント要件は、関数への引数、関数からの戻り値、および共用体のメンバーとしての互換性を意味するものです。
つまり、printf("%s", x)
は、x
の型がchar*
であるかvoid*
であるかを問わず同じ意味を持ちますが、void*
で算術を実行できるという意味ではありません。
編集者注:この回答は、最終的な結論を反映するために編集されています。
void*
ポインターではポインター演算は許可されません。
それをcharポインターにキャストして、ポインターをxバイト先に進めます。
まさにこの理由で、void *
型に対してポインター演算を行うことはできません!
C標準はvoidポインター演算を許可しません。ただし、void is 1
のサイズを考慮することにより、GNU Cが許可されます。
C11標準§6.2.5
段落-19
void
タイプは、空の値セットで構成されます。 不完全なオブジェクトタイプであり、完了できません。
GCCコンパイラで正常に動作する次のプログラム。
#include<stdio.h>
int main()
{
int arr[2] = {1, 2};
void *ptr = &arr;
ptr = ptr + sizeof(int);
printf("%d\n", *(int *)ptr);
return 0;
}
他のコンパイラがエラーを生成する場合があります。
ポインター演算を行う前に、別のタイプのポインターにキャストする必要があります。
ボイドポインターは、任意のメモリチャンクを指すことができます。したがって、コンパイラーは、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
はx
に1バイトを追加し、ポインターはバイトx+1
に移動します(int*)x+1
はsizeof(int)
バイトを追加し、ポインターはバイトx + sizeof(int)
に移動します(float*)x+1
addres sizeof(float)
バイトなど.最初の項目は移植性がなく、C/C++のGalateoに反しますが、それでもC言語が正しいため、ほとんどのコンパイラで適切なフラグが必要になる可能性があります(-Wpointer-arithなど)