void は英語では名詞として次のように解釈されます虚無、空間、空隙、一方C言語では void は次のように翻訳されます「無型」、それに対応してvoid * ために「無型ポインタ」。

void は「注釈」とプログラムを制限する役割だけがあるように思われます。もちろん、ここでの「注釈」は人間のための注釈ではなく、コンパイラに対するいわば注釈を提供するものです。


void の役割

1. 関数の戻り値の制限。このケースはよく見られます。

2. 関数のパラメータの制限。このケースもよく見られます。

一般的によく見られるのはこの2つのケースです:

  • 関数が戻り値を必要としない場合、void で制限する必要があります。これが最初のケースです。例:void func(int a,char *b)。
  • 関数がパラメータを受け付けない場合、void で制限する必要があります。これが2番目のケースです。例:int func(void)。

void ポインタの使用規則

1. void ポインタは任意の型のデータを指すことができます。つまり、任意の型のポインタを void ポインタに代入できるということです。例えば:

int *a;
void *p;
p=a;

void ポインタ p を他の型のポインタに代入する場合は、強制型変換が必要です。この例では:a=(int *)p。メモリ割り当てでは void ポインタの使用を見ることができます:メモリ割り当て関数 malloc 関数が返すポインタはvoid *型です。ユーザーがこのポインタを使用する際には、強制型変換を行う必要があります。つまり、そのポインタが指すメモリにどの型のデータが格納されているかを明示的に示すことです。(int *)malloc(1024)これは、malloc が返す void* ポインタが指すメモリに int 型のデータが格納されていることを強制的に指定することを表します。

2. ANSI C 標準では、void ポインタに対する p++ や p+=1 などの算術演算は許可されていません。void は無型であるため、算術演算のたびに何バイト操作すればよいかわからないからです。例えば、char 型は sizeof(char) バイトを操作し、int 型は sizeof(int) バイトを操作します。一方、GNU では許可されています。デフォルトでは GNU は次のように見なすからです。void *とchar *同じであり、確定している以上、もちろんいくつかの算術操作を行うことができます。ここではsizeof(*p)==sizeof(char)。

void にはほとんど「注釈」とプログラムを制限する役割しかありません。なぜなら、void 変数を定義する人は誰もいないからです。試しに定義してみましょう:

void a;

この行の文はコンパイル時にエラーになり、次のように表示されます。"illegal use of type 'void'"。たとえvoid aそのコンパイルがエラーにならなくても、それに実用的な意味はありません。

周知のように、ポインタ p1 と p2 の型が同じであれば、p1 と p2 の間で直接相互に代入できます。p1 と p2 が異なるデータ型を指している場合は、強制型変換演算子を使用して、代入演算子の右辺のポインタ型を左辺のポインタ型に変換する必要があります。

float *p1;
int *p2;
p1 = p2;
//其中p1 = p2语句会编译出错,
//提示“'=' : cannot convert from 'int *' to 'float *'”,必须改为:
p1 = (float *)p2;

而 void *void* は異なります。任意の型のポインタを直接代入でき、強制型変換は不要です。

void *p1;
int *p2;
p1 = p2;

しかし、これはvoid *void* も強制型変換なしで他の型のポインタに代入できることを意味するわけではありません。「無型」は「有型」を包含できますが、「有型」は「無型」を包含できないからです。

void ポインタ型の使用には注意してください:

ANSI(American National Standards Institute)標準によれば、void ポインタに対して算術操作を行うことはできません。つまり、次の操作はすべて不正です:

void * pvoid;
pvoid++; //ANSI:错误
pvoid += 1; //ANSI:错误
//ANSI标准之所以这样认定,是因为它坚持:进行算法操作的指针必须是确定知道其指向数据类型大小的。
//例如:
int *pint;
pint++; //ANSI:正确
pint++ の結果は、sizeof(int) だけ増加します。

しかし、GNU はこのようには見なさず、次のように指定しています。void *の算術操作はchar *と一致します。したがって、次の文は GNU コンパイラではすべて正しいです:

pvoid++; //GNU:正确
pvoid += 1; //GNU:正确

pvoid++ の実行結果は 1 だけ増加します。

実際のプログラム設計では、ANSI 標準に適合し、プログラムの移植性を高めるために、同じ機能を実現するコードを次のように書くことができます:

void * pvoid;
((char *)pvoid)++; //ANSI:错误;GNU:正确
(char *)pvoid += 1; //ANSI:错误;GNU:正确

GNU と ANSI にはまだいくつかの違いがあります。全体的に見ると、GNU は ANSI よりも「オープン」で、より多くの構文をサポートしています。しかし、実際の設計では、できるだけ ANSI 標準に適合するようにすべきです。関数のパラメータが任意の型のポインタであり得る場合、そのパラメータを次のように宣言する必要があります。void *。

注:void ポインタは任意の型のデータを指すことができ、プログラムにいくつかの利点をもたらします。関数のパラメータがポインタ型の場合、それを void ポインタとして定義できるため、関数は任意の型のポインタを受け入れることができます。例:

典型的な例として、メモリ操作関数 memcpy と memset の関数プロトタイプはそれぞれ次のとおりです:

void * memcpy(void *dest, const void *src, size_t len);
void * memset ( void * buffer, int c, size_t num );

これにより、任意の型のポインタを memcpy と memset に渡すことができます。これは、メモリ操作関数の意義を如実に示しています。なぜなら、操作対象は単なるメモリ領域であり、そのメモリ領域がどの型であるかは関係ないからです(C言語によるジェネリックプログラミングの実装を参照)。もし memcpy と memset のパラメータ型がvoid *ではなく、char *ならば、それは本当に奇妙です!そのような memcpy と memset は明らかに「純粋で、低級な趣味から脱却した」関数ではありません!void の出現は単に抽象化の必要性のためです。オブジェクト指向における「抽象基底クラス」の概念を正しく理解していれば、void データ型も容易に理解できます。抽象基底クラスにインスタンスを定義できないのと同様に、void(比喩的に void を「抽象データ型」と呼びましょう)変数も定義できません。

原文アドレス:https://www.cnblogs.com/wuyudong/p/c-void-point.html