汎用ポインタ型 - void*

あらゆるポインタ型を代入できるポインタ型を汎用ポインタ型「void*」があります。

void*

汎用ポインタ型のサンプル

汎用ポインタ型のサンプルです。

汎用ポインタへの代入とその逆の代入

malloc関数calloc関数は「void*」型の値を返します。実際に「void*」型の変数を宣言して他のポインタ型に代入してみましょう。

また他のポインタ型を「void*」型に代入するのもやってみましょう。

#include <stdint.h>
#include <stdlib.h>

int main(void) {
  
  void* memory_block1 = malloc(sizeof(int32_t) * 10);
  void* memory_block2 = calloc(sizeof(int32_t), 10);
  
  int32_t* nums1 = memory_block1;
  int32_t* nums2 = memory_block2;

  void* memory_block1_again = nums1;
  void* memory_block2_again = nums2;
}

C言語では、void*型を他のポインタ型に代入する場合も、他のポインタ型にvoid*型を代入する場合も明示的なキャストは、必要がありません。

汎用ポインタ型変数へint32_t型の値を保存する

これは、裏技的な方法ですが、汎用ポインタ型の変数へ、int32_t型の値を保存することができます。この裏技は「配列の要素をvoid*型で実装したんだけど、あー、これに、整数も保存したいねぇ、どうやるのよ?」という場合に使えます。

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>

int main(void) {
  
  // void*型を要素に持つ配列
  void** objects = calloc(sizeof(void*), 10);
  
  // int32_t型の値をvoid*型の要素に保存
  int32_t num = 5;
  
  // int32_t型ををintptr_tにキャスト、さらにvoid*に変換して代入
  objects[1] = (void*)(intptr_t)num;
  
  // void*型をintptr_tへキャスト、さらにint32_t型へキャストして、取得
  int32_t num2 = (int32_t)(intptr_t)objects[1];
  
  printf("%d\n", num2);
}

「void*型」と互換性があり、符号あり整数型とも互換性のあるintptr_t型のキャストを挟み込むのがポイントです。

この裏技的テクニックを使う場合は、32bit環境と64bit環境の両方で動作させるために、int32_tを使うことに注意してください。int64_tを使うと、32bit環境では、正しく動きません。符号なし32bit整数型のuint32_tは、問題なく使うことができます。

浮動小数点を保存したい場合はfloatなら32bit幅なのでOKです。doubleは64bit幅なので、アウトです。

void型とvoid*型のvoidは同じ意味?

いいえ、void型のvoidは「存在しない」ということを表現する特殊な型です。

一方、「void*型」のvoidの部分は「汎用(どんな型でも)」を意味しています。

void*から他のポイント型への明示的キャストについてどう思いますか?

void*から他のポイント型への明示的キャストについてどう思いますか?

以下のような書き方ですね。

  void* memory_block1 = malloc(sizeof(int32_t) * 10);
  int32_t* nums1 = (int32_t*)memory_block1;

これは、C言語仕様では、特に求められていないことです。一方、C++仕様では、求められていることです。

もしC++について意識する必要ないのであれば、C言語にとっては冗長な書き方になっています。

冗長ですが、もし書いてあっても特別悪い影響はないと思います。

書いていても、特別良い影響があるわけでもないと思います。

言語論争なようなものに巻き込まれがちな分野でもありますが、実用的には、そんなに気にすることはないと思います。

関連情報