プログラミング演習II 2004年11月 16日(第5回) 理学部数学科・木村巌
前回までの復習 配列とポインタの関係 引数と配列
今日学ぶこと 文字列とポインタ 文字列の操作 動的なメモリの確保 関数ポインタ 教科書10.3から(p. 317~)
文字列をポインタで扱う 7章で見たように、C言語には「文字列型」というものは存在せず、char型の配列(で、最後の要素が’\0’になっているもの)で代用しているのだった char str[] = “Hello”; これは、次と同じ char str[] = {‘H’, ‘e’, ‘l’, ‘l’, ‘o’, ‘\0’};
文字列をポインタで扱う(続) char型のポインタで扱うことも出来る: char *str = “Hello”; ポインタで扱う場合は、どこかに、”Hello”を保存するのに必要なだけのメモリが確保され、その先頭のアドレスがstrというchar型のポインタ変数strに保存される 教科書の図10-7(318頁参照) Sample8.cを打ち込んで、コンパイル・実行してみよう
配列とポインタとの違い 配列には、一度初期化した後で、再び代入することは出来ない char str[] = “Hello”; str = “Goodbye”; /* これは出来ない */ 実際に試してみて、どのようなエラーメッセージが表示されるか確認しよう! しかし、ポインタを使うばあいは、再代入することが出来る(図10-8、320頁)
文字列を入力する場合 配列を宣言して、文字列を格納する領域を確保しておく: char str[100]; /* 文字列を格納する配列 */ scanf (“%s”, str); /* 配列に文字列を格納 */ ポインタを使う場合は、文字列を格納する領域を、動的に確保する(malloc()を使う、後述)
文字列への再代入 標準ライブラリ関数に、文字列の再代入を行う関数がある 実際は、文字列をメモリ上のどこかにコピーしてくれる関数strcpy() 後述
文字列の配列 複数の文字列を、文字列の配列1つにまとめることが出来る Sample9.cを打ち込んで、コンパイル・実行してみよう char str[3][20] = {“Hello”, “Goodbye”, “Thankyou”}; 19文字までの文字列が3つからなる、文字列の配列 図10-9を参照
文字列ポインタの配列 文字列はポインタとしても扱える 文字列のポインタの配列として、複数の文字列をまとめて扱うことも出来る char *str[3] = {“Hello”, “Goodbye”, “Thankyou”}; ポインタの配列として実現されている.図10-10, 325頁を参照
文字列の操作 標準ライブラリ関数には、文字列を操作するための関数が多数用意されている string.h というヘッダーファイルをインクルードする #include <string.h>
文字列操作用の関数(例) size_t strlen (const char *str); 文字列strの長さ(最後のNULLを除く) char *strcpy (char *str1, const char *str2); 文字列str2をstr1にコピーし、str1を返す char *strcat (char *str1, const char *str2); 文字列str2をstr1の末尾に追加してstr1を返す int strcmp (const char *str1, const char *str2); 文字列str1, str2を辞書式順序で比較し、str1がstr2より小さい、等しい、大きいに応じて、負、0、正の値を返す
文字列の長さを調べる 入力された文字列の長さを返すプログラムSample11.c 入力して、コンパイル・実行してみよう 文字列の長さを知るには、strlen()を使う(string length) strlen()を自分で書いてみよ
文字列をコピーする 文字列を配列に代入した場合、再代入が出来ないのだった 配列の各要素に、一文字ずつ代入することはできる 簡単な関数で実現できる(書いてみよ)が、これも標準ライブラリ関数strcpy()として用意されている Sample12.cを打ち込んで、コンパイル・実行してみよう コピー先に、コピー元が収まるだけのメモリがあることを保証するのは、プログラマの責任
文字列を連結する char *strcat (char *str1, const char *str2); 一つの文字列strの末尾に、もう一つの文字列str2を連結するという処理を行うのが、strcat()関数(string conCATenate) str1が指しているメモリに、連結した後の文字列が収まるだけのメモリがあることを保証するのは、プログラマの責任 Sample12.cを入力し、コンパイル・実行してみよう
配列の大きさに注意 上でも述べたように、strcpy()のコピー先、またstrcat()の連結先に、十分なメモリがあることを保証するのは、プログラマの責任 そうでない場合、配列の最後の要素を超えて書き込まれる 何が起こるか分かりません また、strcpy()で、コピー元とコピー先が重なっている場合の挙動も未定義
文字列を比較する strcmp()関数(string compare)は、引数を比較し、一致していた場合は0を返す Sample14.cを入力し、コンパイル・実行してみよう 必ずしも同じ長さの配列でなくても良い
文字列の長さを実行時に決める これまでのプログラムでは、文字列の長さは(配列の長さとして)、コンパイル時に(つまり、プログラムの実行が始まる前に)決まっていた プログラムの実行中に、文字列の長さを決めることが出来ると便利である 文字列に限らず、任意の大きさのメモリを、実行中に確保・開放できる:malloc()とfree()
void *malloc(size_t n)関数 #include <stdlib.h> を忘れないこと プログラムの実行時に、必要なサイズ(n bytes)のメモリを確保して、そのメモリのアドレスを返す 任意の型にキャストできるように、返値の型はvoid * (void型へのポインタ) メモリの確保に失敗した場合は、NULLポインタが返る
void *free(void *p)関数 pが指すメモリ領域を開放し、後のmalloc()などに使えるようにする ほとんどのOS(たとえばMicrosoft Windows, FreeBSD, Linuxなど)では、プログラムが正常に終了すれば、そのプログラムが使ったメモリは自動的に開放される 長時間実行され続けるプログラムでは、malloc()して不要になったメモリを正しく開放するのは重要。そうでないと、使用可能なメモリが減少していく(memory leakという)
動的なメモリ確保の例 Sample15.cを入力し、コンパイル・実行してみよう 型Tの要素n個を保持するメモリは、sizeof (T) * n bytes たとえば、T = char なら、n文字からなる文字列を保持するのに必要なのは sizeof (char) * (n+1) bytes. 最後の’\0’用に1足している
関数ポインタ ポインタ変数とは、さまざまなオブジェクト(int, char, double型の値や、それらの配列など)のアドレスを格納する変数だった アドレスとは、メモリ上の場所(通し番号)だった 関数も、メモリ上に一定の場所を占める 関数のアドレスもある 関数ポインタ
関数ポインタの宣言 関数ポインタの宣言 たとえば pMは、int型を二つ取り、int型を返す関数へのポインタ 戻り値の型 (*関数ポインタ) (引数リスト); たとえば int (*pM) (int x, int y); pMは、int型を二つ取り、int型を返す関数へのポインタ
関数ポインタにアドレスを代入 関数ポインタに、関数のアドレスを代入する たとえば 関数ポインタに代入された関数の呼び出し 関数ポインタ = 関数名; &はいらない(関数名は関数のアドレスそのもの) たとえば pM = max; pMは関数max()を指す 関数ポインタに代入された関数の呼び出し (*pM)(5, 10) /* max(5, 10)と同じ */
関数ポインタの利用と応用 Sample16.cを入力して、コンパイル・実行してみよう 関数ポインタの応用 たとえば、関数ポインタを配列に格納し、必要に応じて異なる関数を呼び出すことができる Sample17.cを入力して、コンパイル・実行してみよう
今日学んだこと 文字列とポインタ 文字列の操作 動的なメモリの確保 関数ポインタ 教科書10.3から10.5まで(p. 317~348)
レポート課題 (1)13番目のスライドにあるように、strlen()と同じ動作をする関数、mystrlen()を書いてみよ。Sample11.cを、mystrlen()を使うものに書き換えよ。 (2・やや難)文字列str1とstr2とを連結した文字列を新たに作り、それを返す関数 char *strappend (const char *str1, const char *str2); を書け。
レポート課題(続) (2)のヒント: char * strappend (const char *str1, const char *str2) { char *str = (char *) malloc (/*str1とstr2が入るだけのメモリを確保する */); if (!str) perror ("malloc failed in mystrappend()"); exit (EXIT_FAILURE); } strcpy (/* strにstr1をコピーする */); strcat (/* strにstr2を連結する */); return str;
レポート課題(続) 締め切り:2004年11月22日一杯(日本時間で) 提出:メールで木村(iwao@sci.toyama-u.ac.jp)まで. 感想などあると木村が喜びます