【C言語】汎用ポインタの利便性
サチローこと幸一朗です。
汎用ポインタについて、使い道がわかりませんと、質問を受けたため、自分なりに解説。
【大前提】
通常、変数は型の整合性を取らなければならない。
int i = 128; char c; c = i; printf("%d\n", c);
これの実行結果は「-128」となる(はず)。
charは1バイトの変数なので、「11111111」が最大値となります。
が、最上位のビットは符号ビットとなり、正の値では「01111111」が最大値。
つまり127までしか保持できない。
そこに対して4バイト変数を代入したため、オーバーフローが発生したという例。
※ちなみに、コンパイラによっては警告すら出さない。
代入元の型と、代入先の型は一致しているべきである。
【ポインタの扱い】
ポインタ変数は、変数が確保している領域の先頭アドレスを保持する。
int i = 128; int *ip = &i; printf("i[%p]\n", &i); printf("ip[%p]\n", ip);
表示結果は一致する。
変数が領域を確保することは、ポインタ変数も同様。
では、下記の結果はどうなるか?
typedef struct { char foo; char bar; } hoge; printf("%d\n", sizeof(char)); printf("%d\n", sizeof(int)); printf("%d\n", sizeof(hoge)); printf("%d\n", sizeof(char *)); printf("%d\n", sizeof(int *)); printf("%d\n", sizeof(hoge *));
実行環境によって異なるが、
1
4
2
8
8
8
となる。
ポインタ変数では変数の先頭アドレスを格納するため、そこに格納されている値に依存しない。
【汎用ポインタ】
ポインタ変数同士であれば、アドレスを格納する点では共通しているが、問題は中身を参照する場合。
typedef struct { char foo; char bar; } hoge; hoge st; int *p = &st; printf("%d\n", p->foo);
この場合、「foo」という子は知りませんと、コンパイルエラーが発生する。
ここでvoid型のポインタの登場。
typedef struct { char foo; char bar; } hoge; hoge st; void *p = &st; hoge *pst; pst = (hoge *)p; printf("%d\n", pst->foo);
一旦、void型のポインタに退避し、後からキャストすることで中身を参照することができる。
【汎用ポインタの利便性】
例えばこんな構造
typedef struct { int key; int value; } token; const token token_list[] = { { 1, 10 }, { 2, 11 }, { 3, 12 }, };
token型の配列を宣言。
keyを渡して値を得るhashのような使い方をしたい。
がしかし、この形式で管理できる値はint型に限定される。
void型のポインタにすることにより、これが回避される。
typedef struct { int key; void *value; } token; typedef struct { int foo; char bar; } hoge int i = 10; char c = 'c'; char str[128 + 1] = "fuga"; hoge hoge_st; const token token_list[] = { { 1, &i }, { 2, &c }, { 3, str }, { 4, &hoge_st }, };
値を取り出したい場合、値を取り出した先で、同じ型のポインタ変数で受ければ良い。
/* tokenの最後尾を知る手段として、末端のkeyにそれを示す値が格納されているものとする。 それが無理ならsizeをfetch関数に渡す。 */ #define GetValue(type, key, list) (type *)fetch(key, list) void *fetch(int key, token *list) { void *ret = NULL; while (list->key != KEY_TAIL) { if (list->key == key) { ret = list->value; break; } } return ret; } int *ip; hoge *hoge_p; ip = GetValue(int, 1, token_list); hoge_p = GetValue(hoge , 4, token_list);
といった具合に、型に依存せずにデータを管理できる。
型に依存しないという点では便利ですが、乱用は避けてください。
中身に何が入っているのか訳が分からなくなり、メンテナンス時に苦労します。