整数定数とポインタへのキャストの挙動の意味

ようやくわかったかもしれない。

int main (void) {
    int *a = (int *)100;
    printf("%d \n",a);
    return 0;
}
$ main
100

これが100と表示される意味。

整数定数がどうとか考えるからややこしかったのかも。

まず実体のある変数のポインタを表示させてみる。

int main (void) {
    int tmp;
    printf("%d \n",&tmp);
    return 0;
}
$ main
1245040

色々な記事を読んでると、ポインタ変数は整数型にキャストが可能で、その値はアドレスそのものになるそうだ。

つまりこの「1245040」と表示されてるのはtmp変数のアドレスということになる。

アドレスを保持するための変数がポインタ変数なので

int main (void) {
    int tmp;
    int *a;
    a = &tmp
    printf("%d \n",&tmp);
    printf("%d \n",a);
    return 0;
}
$ main
1245040
1245040

このように代入が可能なのだ。当然同じ型じゃないとエラーになる。&tmpとはつまり(int *)型なんだよね。aも(int *)型なので明示的にキャストを指定しなくても代入が可能となる。

&tmpはアドレスで、単なる整数値ということがわかった。あとは型さえ同じであればポインタ変数への代入が可能なわけでとどのつまり

int main (void) {
    // 100という整数値を(int *)でキャストしてポインタ変数aに代入。
    // これはつまり100というアドレスをポインタ変数aに代入してるのとまったく同じ意味
    int *a = (int *)100;
    printf("%d \n",a);
    return 0;
}

となる。aに100を代入して100と表示されてたのは至極当然な挙動なのだ。だって単なるアドレスだもん。

そしてここからが面白くも怖いお話。

つまり任意のアドレスを指定できるってことなので

int main (void) {
    int *a = (int *)1245040;
    int *b = (int *)1245040; // aと同じアドレスを指定
    
    // bを間接参照して書き換え
    *b = 999;
    
    // aを間接参照して表示すると・・・
    printf("%d \n",*a);
    return 0;
}
$ main
999

なんということでしょう!恐ロシア。

これは恐ろしい。

何が恐ろしいかというのは任意のアドレスに直接データを流し込めるところが怖い。

だってプログラムがどこのアドレスをどういう風に使っているかなんて僕にはわからないから。

実際に最初アドレスを100にして間接参照で代入処理したらプログラムがクラッシュしたw

どうやら変数として使ってはいけない部分のアドレスだったらしい・・・。こえぇ・・・。

「1245040」にしたのは&tmpのアドレスを表示させたときに出たアドレスなので多分変数として扱える範囲のアドレスなのかな?という程度でやっただけなのでこのアドレスも当然環境によってはクラッシュするだろうし、何が起こるかわからない。

なので直にアドレス指定して処理を実行するなんて危険なことはしないように。してはならない。

ちゃんと実体のある変数に対して&でアドレスを得るか、malloc等で確保したアドレスを得るか、とにかく標準で提供されている真っ当なやり方でアドレスを取得することを固くオススメする。



・・・と、いう認識であってるのだろうか。K&R本も半分くらいまで読み進めてるところなんだけどこういった話は出てくるのだろうか。

やはりポインタ完全制覇も購入した方がいいのかな・・・このあたりのことも詳しく載ってるなら欲しいですね。