自分は、大学のころにC言語を勉強し、そこで初めてポインタとキャストという概念を知りました(まあ、配列という概念も大学入って知ったぐらい、プログラミングに関しては当時、超初心者だったんですが)。
なんだか難しいと言われてるポインタだけど、概念だけでいえばそんなに難しいとは思わなかった。ようは、ショートカットファイルみたいなものだ。キャストはただの型変換らしいので、難しいとは思わなかった。
ただ、最近になってこのポインタのキャストというのが分かっていないということが分かりました。いや、もしかしたらポインタもキャストもちゃんと分かってないのかもしれません。例えば、C言語で書いた以下のコード。
#include <stdio.h> int main(void) { int i = 0x41; printf("%s\n",(char*)&i); return 0; }
0x41とは、16進数で41という意味で、10進数では65を表す。ASCIIコードでは65は”A”なのでこの実行結果は、以下のようになります。
A
まあ、これはいいとして。じゃあ次のようなコードだとどうか。
#include <stdio.h> int main(void) { int i = 0x41; int j = 0x414243; int k[] = {0x41, 0x42, 0x43}; printf("%s\n",(char*)&i); printf("%s\n",(char*)&j); printf("%s\n",(char*)&k); return 0; }
一番上は先ほどと同じなので、”A”と表示されるとして、下二つは”ABC”と表示されるかな? なんて思っていた。結果は以下。
A CBA A
どちらも違った・・・。C言語は分かったつもりでいたけど、まだまだ分かってないことだらけなような気がする。そもそも、先日#ifdefの意味を知ったぐらいだし・・・。
コメント
こんちには、興味深い話だったのでちょっとまとめてみました。
intが4バイト、リトルエンディアンだとすると、
iの内部は
… | 0x41 | 0x0 | 0x0 | 0x0 | …
jの内部は
… | 0x43 | 0x42 | 0x41 | 0x0 | …
kの内部は
…| 0x41 | 0x0 | 0x0 | 0x0 | 0x42 | 0x0 | 0x0 | 0x0 | 0x43 | 0x0 | 0x0 | 0x0 |…
となっています。
ポインタをキャストしてポインタの値自体は変わりません。例えば
printf(“char *: %p, int *: %p\n”, (char*)&i, &i);
printf(“char *: %p, int *: %p\n”, (char *)&j, &j);
printf(“char *: %p, int *: %p\n”, (char*)k, k);
を実行してもchar *とint *で同じアドレスが表示されるはずです。何が変わるかというと例えばポインタ演算をしたときにどれだけアドレスを進めるかという部分。
char *p1 = (char *)k;
int *p2 = k;
printf(“%x\n”, *(p1 + 1));
printf(“%x\n”, *(p2 + 1));
でp1はcharのポインタなので+1したら1バイト進み、p2はintのポインタなので+1したら4バイト進みます。したがって *(p1 + 1)の値は0x0、*(p2 + 1)の値は0x42となります。また*演算子の適用後の型も、前者ではchar、後者ではintとなります。例えば
int k[] = {0x41, 0x414243, 0x43};
とするとkの内部は
…| 0x41 | 0x0 | 0x0 | 0x0 | 0x43 | 0x42 | 0x41 | 0x0 | 0x43 | 0x0 | 0x0 | 0x0 |…
となり、
char *p1 = (char *)k;
int *p2 = k;
printf(“%x\n”, *(p1 + 4));
printf(“%x\n”, *(p2 + 1));
を実行すると(p1 + 4)と(p2 + 1)は同じアドレスを指しているけど、*演算子の適用結果は前者がchar型の0x43、後者がint型の0x414243となります。
丁寧な解説、本当にありがとうございます。まさかこんなアクセス数の少ないブログに答えてくれるとは思いませんでしたので、正直驚いてます。
ところで、名前は聞いたことあるのですが、リトルエンディアンというものをよく分かってませんでした。
『リトルエンディアンとは【little endian】 – 意味/解説/説明/定義 : IT用語辞典』
http://e-words.jp/w/E383AAE38388E383ABE382A8E383B3E38387E382A3E382A2E383B3.html
上記の解説を見て納得しました。てっきり、前から順番に入れていくものだと思っていたので・・・。