プログラミング演習I

プログラミング演習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”};
ポインタの配列として実現されている.図1010, 325頁を参照
文字列の操作


標準ライブラリ関数には、文字列を操作する
ための関数が多数用意されている
string.h というヘッダーファイルをインクルード
する
#include <string.h>
文字列操作用の関数(例)

size_t strlen (const char *str);


char *strcpy (char *str1, const char *str2);


文字列str2をstr1にコピーし、str1を返す
char *strcat (char *str1, const char *str2);


文字列strの長さ(最後のNULLを除く)
文字列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型の値や、それらの配列など)の
アドレスを格納する変数だった
アドレスとは、メモリ上の場所(通し番号)だっ
た
関数も、メモリ上に一定の場所を占める
関数のアドレスもある
関数ポインタ
関数ポインタの宣言

関数ポインタの宣言


(*関数ポインタ) (引数リスト);
たとえば


戻り値の型
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日一杯(日本時間
で)
提出:メールで木村([email protected])まで.
感想などあると木村が喜びます