ここでは,文字列を操作する簡単なプログラムをデバッガ上で動作させて,C言語における文字列の仕組みを学びます.
使用するプログラムは stringサブフォルダに配置してあるので,string.sln ファイルをダブルクリックしてプロジェクトを開きます.
// 文字列操作のサンプル #include <string.h> #include <stdio.h> int main() { char* pointer = "Hello, "; char array[] = "world!"; char buf[14]; printf("String operation tests\n"); // <- ブレークポイントを設定 printf("%s%s\n", pointer , array); strcpy(buf, pointer); // 文字列のコピー strcat(buf, array); // 文字列の連結 printf("%s\n", buf); // ※ gets(buf); // 文字列の入力 printf("%s\n", buf); return 0; }
プロジェクトをロードしたら,プログラムをビルドし,ブレークポイントを指示された位置に設定し,デバッガを起動する.
[デバッグ]メニューから[ウインドウ] -> [メモリ]->[メモリ1] をクリックし,メモリウインドウを表示する.メモリウインドウの[アドレス]ボックスに buf と入力し,変数 buf のメモリ領域を表示させる.
1)ステップ実行しながら buf の値がどのように変化するかを調べ,strcpy 関数,strcat 関数の動作を確認せよ.
2)また,文字列がナル文字('\0') により終端されていることを確認せよ.
3) ※ までステップ実行した時点で,ウォッチウインドウを使用して buf[13] の値をゼロ以外に変更する.このときに,変数ウインドウやメモリウインドウに表示されている buf の値がどのように変化するかを確認せよ.
4) また,buf[5] の値を 0 に設定し,buf の値がどのように変化するか確認せよ.C言語では,文字列という概念はあるが,文字列型という変数の型は存在しません.文字列を扱う場合,文字列が格納されたメモリの先頭アドレスのみがやりとりされ,そのアドレスからナル文字が現れるまでを文字列として扱 います.
上記のように,文字配列中の途中にナル文字を挿入した場合,そこが文字列の終端と判断され,ナル文字以降にいかなる文字が格納されていても,そこから先は文字列とは認識されない のです.strcpy, strcmp などの関数は,第1引数で渡されたアドレスの指すメモリ領域への書き込みを行いますが,このときに,書き込まれる先のメモリ領域が有効なものであるかどうかのチェックはまったく行 いません.文字列を扱うプログラムを作成する場合には,実行の結果得られる文字列が,確保されているメモリ内に収まるかどうかに常に留意しておく必要があります.
プログラムの先頭には,以下のように非常によく似た宣言があります.
char* pointer = "Hello, ";
char array[] = "world!";どちらも,printf の行では,printf("%s%s\n", pointer , array); のように型の区別無く利用されてますが,それらにどのような違いがあるのかを,以下の式をウォッチウインドウで調べることにより考察します.
- sizeof(array)
- sizeof(pointer)
- pointer
- &pointer
- array
char array[] = "world!"; と宣言した場合,スタック領域に文字列分の領域が確保され,指定した文字列で初期化されています.
char* pointer = "Hello, "; と宣言した場合には,実際の文字列は別の場所に割り当てられ,その先頭アドレスがポインタ変数に設定されます.
メモリウインドウで,pointer の指しているアドレスの領域の様子を調べてみましょう."hello, " 以外にも,さまざまな文字列が格納されていることがわかります.
C言語中で "hogehoge" のように記述された文字列リテラル(文字列定数)は,このようにまとまったメモリ領域に確保され,その文字列の先頭アドレスと解釈されます.
printf("Hello world!\n"); のようなプログラムの場合,"Hello world!\n" の文字列がこの領域に確保され,pointf 関数には,この文字列の先頭アドレスが渡されることとなります.
gets 関数は,文字列のアドレスを引数にとり,標準入力から入力された文字列をアドレスの指す先にコピーする関数です.ユーザーからの文字列入力を受け取るのに使用され ます.この関数の動作をデバッガで確認してみましょう.
gets 関数の行までステップイン実行する.
gets関数に制御が移ると,標準入力からの入力待ちとなるので,コマンドプロンプトから任意の文字列を入力し,エンターキーを押す.
デバッガに制御が戻るので,このときの buf, array などの変数の値を確認する.
コマンドプロンプトから,5文字~20文字までの長さの文字列を入力し,そのときの動作を比較せよ.
gets 関数は,入力された文字列の長さのチェックを行 いません.ユーザーの入力した文字列長がプログラマの想定を超えた場合,他のメモリ領域の内容を破壊してしまうので注意が必要です.
ユーザーがどのような長さの文字列を入力しても対応できるプログラムにするは,文字列を受け取る領域の大きさを指定できる fgets 関数を使用するのがよ いでしょう.fgets 関数の使用方法についてはヘルプを参照してください.
課題1で作成した文字列検索プログラムをデバッガ上でステップ実行し,その動作を確認しなさい.