C/C++のポインタの機能--参照渡しのような処理(ZDNet)

wakatonoの戯れメモより.

マトモそうなサイトの中では,おそらく史上最凶のポインタの解説記事. どう最凶かは,次のサンプルプログラムを見れば一目瞭然.

   #include <stdio.h>

   int main( void ) {
     int *n;
     *n = 5;   /* ポインタ変数nに値5を代入 */
     printf( "%d\n", *n );  /* ポインタ変数nが持つ値(5)の出力 */
     return 0;
   }

初期化していないポインタの指し示すアドレスに 5 を書き込んでいる. もちろんこんなものマトモに動くはずもなく,x86 Linux では Segmentation fault で止まる.

gdb でのトレース

こんなのをくどくど説明するのもアホらしいのだけど,gdb でトレースすると以下のようになる.

(gdb) b main
Breakpoint 1 at 0x8048385: file hoge.c, line 6.
(gdb) c
The program is not being run.
(gdb) run
Starting program: /home/imai/a.out 

Breakpoint 1, main () at hoge.c:6
6	      *n = 5;   /* ポインタ変数nに値5を代入 */
(gdb) print n
$1 = (int *) 0x4730b0b0
(gdb) n

Program received signal SIGSEGV, Segmentation fault.
0x08048388 in main () at hoge.c:6
6	      *n = 5;   /* ポインタ変数nに値5を代入 */
(gdb) c
Continuing.

Program terminated with signal SIGSEGV, Segmentation fault.
The program no longer exists.

つまり,

int *n;

の時点では,n の値は不定なので,これを絵に描くと

  +--------+
n |???   O----------> unknown memory
  +--------+

となる.

*n = 5;

unknown memory に 5 を書き込む

という操作を行うのだが,このアドレスは当該プロセスには割り当てられていない. このため例外が発生し,めでたく Segmentation fault で異常終了するわけである.

うまく動いてしまう場合

で,このプログラム,環境や運によってはうまく動いてしまうことも有り得る.

(gdb) b main
Breakpoint 1 at 0x8048385: file hoge.c, line 6.
(gdb) run
Starting program: /home/imai/a.out

Breakpoint 1, main () at hoge.c:6
6	      *n = 5;   /* ポインタ変数nに値5を代入 */
(gdb) print &n
$1 = (int **) 0xbfd54780
(gdb) set n=0xbfd50000
(gdb) c
Continuing.
5

Program exited normally.
(gdb) q

ここではデバッガ上で

(gdb) set n=0xbfd50000

で n の初期値を手動で設定して,「運がいい場合」をシミュレートしている. このアドレスはスタック領域近辺であり,プロセスにはメモリが割り当てられている.

n の初期値がこんな値の場合はプログラムは無事実行され,正常終了する.

どんな場合でも動作するようにするには

元記事のコメントや戯れメモのほうにも上がっているのだが,要するに

きちんとメモリを割り当て,そのアドレスを n に設定

すればよいだけの話である. そうすれば運や環境に左右されず,汎用的に正常動作するプログラムとなる.

というわけでこの記事は

この記事が公開されてしまったいきさつを最大限に良い方に解釈すると

筆者も編集部もサンプルプログラムの動作確認をしたのだが,正常動作してしまいミスを見逃してしまった

というシナリオが考えられる.

が,イカレたサンプルプログラムを元に説明しているので,説明自体もイカレている.

 続いて、*nを使って処理に用いる値を代入し、それを出力する例を示す。このときnには代入された値を記憶した場所(アドレス)が自動的に代入されるため、nそのものの値は実行されるごとに異なる可能性があるものの、*nが表す値は、処理中に変更されない限り5のままである。

「自動的に代入されるアドレス」って何だろう? 「int *n で int 型変数の格納領域が確保される」という意味なのか,*n = 5 の時点で 「5 という整数型オブジェクトの格納アドレスが n に代入される」という意味なのか. 意味不明である.

で,元記事コメントを見ている限りでは,編集部は

初心者に対する説明のために大雑把な表現となり,細かい正確性が失われている

という認識のようであるが,実際のところ

サンプルプログラム自体がイカレており,説明も意味不明

なわけで.

考えてみれば

編集部内にこのような記事が書ける人がいれば,外部のライタには頼まずに編集部名義で記事を書くはずである. というわけでライタに記事を書いてもらったのだけど,それが正しいかどうかチェックする仕組みが無く「そのライタを信用する」しか無いのではないか. で,そのライタが怪しい場合は…

そういう体制なんじゃないか,と,想像してみたりもする.


トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2008-03-31 (月) 08:57:12 (5893d)