voidポインタの復習
CoddyのCジャーニー「Object Oriented Programming」セクションの一部 — レッスン 49/61。
void* は、「何らかのデータへのポインタですが、それが何であるかは教えません」というC言語流の表現です。これは、int、float、struct、あるいはその他のあらゆるデータ型の型のアドレスを保持できます。これにより、C言語で汎用的なコードを記述するための基礎となります。
注意点は、void* を直接デリファレンス(間接参照)できないことです。コンパイラは、何バイト読み取るべきか、あるいはそれらをどのように解釈すべきかを知りません。データを使用するには、正しい型にキャストし直す必要があります:
void print_int(void* data) {
int* p = (int*)data;
printf("%d\n", *p);
}
int main() {
int x = 42;
print_int(&x);
return 0;
}この柔軟性には責任が伴います。誤った型にキャストしても、コンパイラはそれを止めませんが、プログラムはゴミデータを出力したりクラッシュしたりします。そのため、汎用関数では void* と型識別子(enumやフラグなど)をペアにすることが多く、それによって関数がデータを正しく解釈する方法を判断できるようにします。
今後のレッスンでは、void* を使用して、あらゆる型のデータを格納できるコンテナを構築し、真に再利用可能なデータ構造を実現します。
チャレンジ
簡単データへの void* ポインタと int 型のフラグの2つのパラメータを受け取る print_value という関数を作成してください。この関数は、型フラグに基づいてデータを解釈し、出力する必要があります。
- 型フラグが
0の場合、ポインタをint*にキャストし、整数値を出力します。 - 型フラグが
1の場合、ポインタをfloat*にキャストし、小数点以下2桁で浮動小数点数を出力します。
プログラムは2つの入力を受け取ります:
- 型フラグ(整数の場合は
0、浮動小数点数の場合は1) - 数値
型フラグに基づいて、適切な変数型に値を格納し、その変数へのポインタと型フラグを引数にして print_value を呼び出してください。
入力が 0 と 42 の場合の出力例:
42入力が 1 と 3.14 の場合の出力例:
3.14入力が 0 と -17 の場合の出力例:
-17入力が 1 と 99.5 の場合の出力例:
99.50チートシート
void* は、あらゆるデータ型のアドレスを保持できる汎用ポインタです。C言語で汎用的なコードを記述するための柔軟性を提供します。
void* を直接デリファレンスすることはできません。まず正しい型にキャストする必要があります。
void print_int(void* data) {
int* p = (int*)data;
printf("%d\n", *p);
}
int main() {
int x = 42;
print_int(&x);
return 0;
}汎用関数は、データを正しく解釈する方法を知るために、void* を型インジケータ(enumやフラグなど)とペアにすることがよくあります。誤った型にキャストすると、未定義の動作やクラッシュの原因となる可能性があります。
自分で試してみよう
#include <stdio.h>
// TODO: ここに print_value 関数を記述してください
int main() {
int type_flag;
scanf("%d", &type_flag);
if (type_flag == 0) {
int int_value;
scanf("%d", &int_value);
// TODO: int_value へのポインタと type_flag を引数に指定して print_value を呼び出してください
} else if (type_flag == 1) {
float float_value;
scanf("%f", &float_value);
// TODO: float_value へのポインタと type_flag を引数に指定して print_value を呼び出してください
}
return 0;
}このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。