汎用ポインタ型 - 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言語にとっては冗長な書き方になっています。
冗長ですが、もし書いてあっても特別悪い影響はないと思います。
書いていても、特別良い影響があるわけでもないと思います。
言語論争なようなものに巻き込まれがちな分野でもありますが、実用的には、そんなに気にすることはないと思います。