Download : sample-001.c
/* * 2018/06/29 sample-001.c */ /* * 返値を持つ関数 */ #include <stdio.h> /* * to_upper : 引数で与えられた文字が小文字なら大文字にする */ char to_upper ( char ch ) { if ( 'a' <= ch ) { /* もし、ch が 'a' 以上で.. */ if ( ch <= 'z' ) { /* 更に、ch が 'z' 以下なら.. */ return ch - 'a' + 'A'; /* 小文字なので大文字に変換して返す */ /* return 命令が実行されると、そこで関数は終了 */ } /* else {} */ /* else 以下はないので省略 */ } /* else {} */ return ch; /* 小文字でない場合は、そのまま返す */ } /* * main 関数 */ int main ( void ) { printf ( "一文字入力して、[Enter] キーを押してください : " ); putchar ( to_upper ( getchar() ) ); putchar ( '\n' ); return 0; }
k
$ ./sample-001.exe < sample-001.in 一文字入力して、[Enter] キーを押してください : k K $
Download : sample-002.c
/* * 2018/06/29 sample-002.c */ /* * main の返値 */ #include <stdio.h> /* * is_upper : ch が大文字なら 1 そうでなければ、0 を返す */ int is_upper ( int ch ) { if ( 'A' <= ch ) { if ( ch <= 'Z' ) { return 1; /* 大文字だった.. */ } } return 0; /* 大文字ではなかった.. */ } /* * main 関数 */ int main ( void ) { printf ( "一文字入力して、[Enter] キーを押してください : " ); if ( is_upper ( getchar() ) ) { printf ( "大文字なので 0 を返します。\n" ); return 0; } else { printf ( "大文字でなかったので 1 を返します。\n" ); return 1; } }
Q
$ ./sample-002.exe < sample-002.in 一文字入力して、[Enter] キーを押してください : Q 大文字なので 0 を返します。 $
Download : sample-003.c
/* * 2018/06/29 sample-003.c * * コンパイルの方法: * cc -c -I ~/c/include sample-003.c */ /* * s_print.h の使い方 */ #include <stdio.h> #include "s_print.h" /* s_print_... を利用する場合には必要 */ /* * main 関数 */ int main ( void ) { s_print_char ( 'A' ); /* 文字の出力 : putchar ( 'A' ) と同じ */ s_print_newline(); /* 改行の出力 : putchar ( '\n' ) と同じ */ s_print_string ( "Hello, World\n" ); /* 文字列の出力 : printf と同じ */ s_print_int ( 123 ); /* 整数値の出力 */ s_print_newline(); return 0; }
$ ./sample-003.exe A Hello, World 123 $
Download : sample-004.c
/* * 2018/06/29 sample-004.c * * コンパイルの方法: * cc -c -I ~/c/include sample-004.c */ /* * s_input.h の使い方 */ #include <stdio.h> #include "s_print.h" #include "s_input.h" /* s_input_... を利用する場合には必要 */ /* * main 関数 */ int main ( void ) { s_print_string ( "一文字を入力して、[Enter] を押してください : " ); s_print_char ( s_input_char () + 1 ); s_print_newline(); s_print_string ( "整数値を一つ入力して、[Enter] を押してください : " ); s_print_int ( s_input_int () + 1 ); s_print_newline(); return 0; }
B 123
$ ./sample-004.exe < sample-004.in 一文字を入力して、[Enter] を押してください : B C 整数値を一つ入力して、[Enter] を押してください : 123 124 $
Download : sample-005.c
/* * 2018/06/29 sample-005.c * * コンパイルの方法: * cc -c -I ~/c/include sample-005.c */ /* * 二つの入力を利用する場合 */ #include <stdio.h> #include "s_print.h" #include "s_input.h" /* * addfunc */ int addfunc ( int a ) { s_print_string ( "二つ目の整数値を入力して、[Enter] を押してください : " ); return a + s_input_int(); } /* * main 関数 */ int main ( void ) { s_print_string ( "一つ目の整数値を入力して、[Enter] を押してください : " ); s_print_int ( addfunc ( s_input_int() ) ); s_print_newline(); return 0; }
123 456
$ ./sample-005.exe < sample-005.in 一つ目の整数値を入力して、[Enter] を押してください : 123 二つ目の整数値を入力して、[Enter] を押してください : 456 579 $
Download : sample-006.c
/* * 2018/06/29 sample-006.c * * コンパイルの方法: * cc -c -I ~/c/include sample-006.c */ /* * 二つの入力を利用する場合 (失敗例) */ #include <stdio.h> #include "s_print.h" #include "s_input.h" /* * subfunc */ int subfunc ( int a, int b ) { return a - b; } /* * main 関数 */ int main ( void ) { s_print_string ( "二つの整数値を入力して、[Enter] を押してください : " ); s_print_int ( subfunc ( s_input_int(), s_input_int() ) ); s_print_newline(); return 0; }
123 456
$ ./sample-006.exe < sample-006.in 二つの整数値を入力して、[Enter] を押してください : 123 456 333 $
/* * CDATE FILENAME * * キーボードから一行(改行まで..)文字列を読込み、それを逆順に出す * 入力ともなう関数(副作用を持つ関数)と再帰の関わり キーボードから abc[Enter] 'a' => 保留 'b' => 保留 'c' => 保留 [Enter] => これで入力が終わり 'c' を出力 'b' を出力 'a' を出力 改行 => abc[Enter] 'a' => 保留 (変数に保存しておいて) => 関数を呼び出すときに、先に入力して引数として指定 => 「先読み」をする必要がある bc[Enter] の処理 (再帰呼び出し) 'a' を出力 (変数の値を利用する) 改行 と入力したら cba[改行] と出力する [ポイント] 入力に getchar() を使う 一度に 1 文字しか入力できない (入力の長さがあらかじめ与えられていないので、 繰り返し[再帰]を利用する必要がある) */ #include <stdio.h> /* * reverse ( char contry ) * char cmd : どのメッセージにするかを表す文字 * */ void reverse_line ( char ch ) { if ( ch == '\n' ) { /* 改行ならば.. */ /* なにもしなくてよい */ } else { /* そうでなければ.. */ reverse_line(getchar()); /* 残りの作業 */ putchar ( ch ); /* 先頭の文字に対する処理 */ } } /* * main */ int main( void ) { reverse_line ( getchar() ); /* 最初の文字を読み込んで .. */ putchar ( '\n' ); /* 改行を出力 */ return 0; }
/* * 20180629-01-QQQQ.c * 文字を */ #include <stdio.h> /* * to_lower : 引数で与えられた文字が大文字なら小文字にする * 引数で指定された文字から(それが大文字だったら)小文字を返す関数 * return で小文字を返す必要がある * 文字を返すので関数の返り値の型も char 型にする必要がある */ char to_lower ( char ch ) { /* 大文字だったら、小文字に それ以外は、そのまま 1. return 値を返す必要がある 2. 大文字かどうかの判定が必要 => 条件分岐 ( if 構文 ) を利用する 「'A' <= ch <= 'Z'」 C 言語では、「'A' <= ch」と「ch <= 'Z'」の 一方ずつしかチェックできない 3. 大文字をどうやって小文字にするか (計算する) => ASCII Code の性質を利用する # 条件分岐で 26 文字、全部チェックしてもよい.. # が、面倒臭いので... */ if ( 'A' <= ch ) { /* もし、ch が 'A' 以上で.. */ /* まず、条件の一つ目をチェック */ /* それにパスしたら .. */ if ( ch <= 'Z' ) { /* 更に、ch が 'Z' 以下なら.. */ /* 二つの目の条件をチェック */ /* 両方パスすれば、求める条件 'A' <= ch <= 'Z' が成立 */ /* ch には大文字が入っているので、小文字に変換する */ /* C 言語では、文字を ASCII Code 表に従って、 小さな整数値で表現している 'A' => 65 => 'a' => 97 'B' => 66 => 'b' => 98 .. .. 'Z' => 90 => 'z' => 122 'a' = 97 = 65 + 32 = 'A' + 32 'b' = 98 = 66 + 32 = 'B' + 32 .. 'z' = 122 = 90 + 32 = 'Z' + 32 ch + 32 ch */ return ; /* ch に大文字が入っているときに小文字を表す 値を計算する式を書く */ /* ** この部分を完成させなさい */ } /* else {} */ /* else 以下はないので省略 */ } /* else {} */ return ch; /* 大文字でない場合は、そのまま返す */ } /* * main 関数 */ int main ( void ) { printf ( "一文字入力して、[Enter] キーを押してください : " ); putchar ( to_lower ( getchar() ) ); putchar ( '\n' ); /* プログラムを実行すと、入力待ちになるので、 一文字をいれて改行キーを押すと、変換された結果が表示される */ /* 何かの大文字をいれて、小文字に変換される事を確認 大文字以外の文字もいれてみて、その場合は、そのまま、 入力された結果が表示される事も確認する 'D' => 'd' '1' => '1' 'A' より小さい場合、'Z' より大きい場合、その間の場合 3 通り '@' 'a' # 条件分岐があるたびに、テストデータは二倍になる */ return 0; }
/* * 20170616-02-QQQQ.c * 二つの整数値を入力して、その商を出力する * 入力を二度する必要がある * => 二度,s_input_int を呼ぶが、その順序が問題 * * 安易に考えると... * s_print_int ( s_input_int() % s_input_int() ): * だが(これは正しいが、) * 画面にプロンプト(入力の内容を表示して、入力を誘導する)がだせない * プロンプトをだそうとすると、s_intput_int の実行順を考える必要がでてくる * 1. 割られる数のメッセージを出力 * 2. 割られる数の入力 * 3. 割る数のメッセージ主力 * 4. 割る数の入力 * 5. あまりの計算する */ #include <stdio.h> #include "s_print.h" #include "s_input.h" /* * remainder */ int remainder ( int a ) { /* 入力した結果をすぐに利用せず、 関数の引数に保存して、 後から必要になった時点で、変数経由でその値を利用している 一度得た値を変数に保存し、 適切なタイミングで利用する事ができる => 変数の重要な役割 cf. 関数の引数の値は、関数の中の変数が現れるときに利用される */ s_print_string ( "割る整数を入力して、[Enter] を押してください : " ); /* 割る数のメッセージを出力 */ /* ** この部分を完成させなさい ** ** 割る数の入力 ** 余りの計算をして、値として返す */ return a % /* 割る数を入力(s_input_int()) */; } /* * main */ int main ( void ) { s_print_string ( "割られる整数を入力して、[Enter] を押してください : " ); /* 割れる数のメッセージ */ s_print_int ( remainder ( s_input_int() ) ); /* 割る数の入力を済ませてから、残り (remainder) を呼び出す */ s_print_newline(); return 0; }
/* s_print.h / s_input.h の利用方法 */ #include <stdio.h> /* それぞれ、s_print_xxx, s_input_xxx を利用する場合に必要 利用しない場合も、インクルードしてよい */ #include "s_print.h" #include "s_input.h" int main(int argc, char *argv[]) { /* main 関数の引数は、利用する場合だけ、宣言すればよい */ /* 使わない場合でも、宣言してもよい */ /* 使う場合は、宣言が必ず必要 */ /* さらに、char *env[] という引数もある */ s_print_string ( "整数値を入力してください、その数 1 を加えます : " ); s_print_int ( s_input_int() + 1 ); /* s_input_int によって、整数値をキーボードから一つ 読み込み、その結果に 1 を加えたものを、 s_print_int で表示している */ s_print_newline(); return 0; }
/* s_print.h / s_input.h の利用方法 */ #include <stdio.h> /* それぞれ、s_print_xxx, s_input_xxx を利用する場合に必要 利用しない場合も、インクルードしてよい */ #include "s_print.h" #include "s_input.h" int main(int argc, char *argv[]) { /* main 関数の引数は、利用する場合だけ、宣言すればよい */ /* 使わない場合でも、宣言してもよい */ /* 使う場合は、宣言が必ず必要 */ /* さらに、char *env[] という引数もある */ s_print_string ( "文字列を入力してください、その文字列の先頭を削除します : " ); s_print_string ( s_input_string() + 1 ); /* s_input_string によって、文字列をキーボードから一つ 読み込み、その結果の先頭の文字を削除したものを、 s_print_string で表示している */ s_print_newline(); return 0; }
/* 整数の引数を与えると、それを二乗した値を計算して、 返す関数 square を考える */ #include <stdio.h> #include "s_input.h" #include "s_print.h" /* [手順] 1.まずは、名前を決めて、これまと同じ形で記述 void square(void) { } 2. 引数があれば、(void) のところの中に引数を書く void square ( int number ) { } 3. return 命令を追加して、その後ろに、返す値を計算する 式を書く void square ( int number ) { return number * number; } 4. 返す値の型を関数名の前の void に差し替える int square ( int number ) { return number * number; } */ int square (int number) { /* 引数である number に入った値の二乗を計算して、返す */ return number * number; /* return 命令の後ろの式が計算されて返る */ /* 返り値として、整数型の値が返るので... */ } /* square は、返り値をもつようになったので、 (いわゆる数学での..)「関数」になった => 式の中で利用可能 */ int main(int argc, char *argv[]) { s_print_string ( "整数値を入力して、その二乗を出力します : " ); s_print_int ( square ( s_input_int() ) ); /* s_input_int で整数値を入力し、 その値を square で二乗して それを s_print_int で出力 */ s_print_newline(); return 0; }
/* 整数の引数を与えると、それを二乗した値を計算して、 返す関数 square を考える */ #include <stdio.h> #include "s_input.h" #include "s_print.h" /* [手順] 1.まずは、名前を決めて、これまと同じ形で記述 void square(void) { } 2. 引数があれば、(void) のところの中に引数を書く void square ( int number ) { } 3. return 命令を追加して、その後ろに、返す値を計算する 式を書く void square ( int number ) { return number * number; } 4. 返す値の型を関数名の前の void に差し替える int square ( int number ) { return number * number; } */ int square (int number) { /* 引数である number に入った値の二乗を計算して、返す */ return number * number; /* return 命令の後ろの式が計算されて返る */ /* 返り値として、整数型の値が返るので... */ } /* square は、返り値をもつようになったので、 (いわゆる数学での..)「関数」になった => 式の中で利用可能 */ /* 二点間の距離の二乗の計算をする関数 int distance2 ( int x1, int y1, int x2, int y2 ) (x1, y1) : 一点目の座標 (x2, y2) : 二点目の座標 二点の距離の二乗の計算をする (x2-x1)^2 + (y2-y1)^2 */ int distance2(int x1, int y1, int x2, int y2){ return square ( x2 - x1 ) + square ( y2 - y1 ); /* 関数 square は返り値を持つので、式の中で利用できる */ } int main(int argc, char *argv[]) { /* 2 点 (2, -1), (3, 2) の二点間の距離の二乗を計算する */ s_print_string ( "2 点 (2, -1), (3, 2) の二点間の距離の二乗 : " ); s_print_int ( distance2 ( 2, -1, 3, 2 ) ); s_print_newline(); return 0; }
#include <stdio.h> int main(int argc, char *argv[] ) { return argc; /* 引数の個数 + 1 の値を返す */ }
#include <stdio.h> #include "s_print.h" #include "s_input.h" int f(int x) { return x * x; /* f(x) = x^2 */ } int g(int x) { return x + 1; /* g(x) = x + 1 */ } int main(int argc, char *argv[]) { s_print_string ( "f(2)+g(3)=" ); s_print_int ( f(2)+g(3) ); /* まず、f(2) が計算され次に g(3) */ s_print_newline(); s_print_string ( "g(3)+f(2)=" ); s_print_int ( g(3)+f(2) ); /* まず、g(3) が計算され次に f(2) */ s_print_newline(); return 0; }
#include <stdio.h> #include "s_print.h" #include "s_input.h" int f(int x) { s_print_string ( "f()\n" ); /* 返り値を計算する事には無関係 */ /* 画面表示するという「作用」がある */ /* 値の計算に無関係な作用だから「副作用」*/ return x * x; /* f(x) = x^2 */ } int g(int x) { s_print_string ( "g()\n" ); /* 副作用 */ return x + 1; /* g(x) = x + 1 */ } int h(int x, int y) { s_print_string ( "h()\n" ); /* 副作用 */ return x + y; /* h(x,y) = x + y */ } int main(int argc, char *argv[]) { s_print_string ( "f(2)+g(3)=" ); s_print_int ( f(2)+g(3) ); /* まず、f(2) が計算され次に g(3) */ s_print_newline(); s_print_string ( "g(3)+f(2)=" ); s_print_int ( g(3)+f(2) ); /* まず、g(3) が計算され次に f(2) */ s_print_newline(); s_print_string ( "f(g(3))=" ); s_print_int ( f(g(3)) ); /* まず、g(3)=4 が計算され次に f(4)=16 */ s_print_newline(); s_print_string ( "h(f(2),g(3))=" ); s_print_int ( h(f(2),g(3)) ); /* g, f, h */ s_print_newline(); return 0; }
前回 コマンドライン引数 プログラムを実行するときに、プログラムに対する引数(パラメータ)を 指定する事ができる => main 関数の引数と反映される main 関数から、コマンド引数の内容を参照できる main には、二つの引数 ( argc, argv ) argc : 整数型 引数の個数 + 1 を表している argv : argv[k] (k は整数値) k 番目の引数 (文字列) が参照できる ./p-001.exe abc 123 xxxxx argc => 3 (引数の個数) + 1 = 4 argv[1] => "abc" argv[2] => "123" argv[3] => "xxxxx" argv[0] => "./p-001.exe" argv[4] = argv[argc] => NULL ( <= 特別値 ) int main(int argc, char *argv[]) char *argv[] => (char *)argv[] => (char) *(argv[]) => argv[] が文字列を表している Hanoi のプログラム (C 言語の話でなく => アルゴリズムの話) 問題を解くために、再帰を上手に利用している cf. この講義では、再帰を繰り返しのために *のみ* 導入 => 再帰は、繰り返しより、表現力が高い Hanoi では、その表現力の高さを例示した... loop(arg) { if ( arg が最小 ) { なにもしない } else { 繰り返したい内容 loop ( arg をひとつ小さくする ) } } => loop によって、「繰り返したい内容」が繰り返される(arg サイズだけ) loop ( n ) => loop ( n - 1 ) => loop ( n - 2 ) => .. loop ( 0 ) :おしまい 線になる hanoi( size ) { hanoi ( size - 1 ); move() hanoi ( size - 1 ); } => hanoi では再帰が二度呼ばれる hanoi ( n ) => hanoi ( n - 1 ) => hanoi ( n - 2 ) hanoi ( n - 2 ) hanoi ( n - 1 ) => hanoi ( n - 2 ) hanoi ( n - 2 ) => 二分木 これまで 先々週までの内容で、基本、C 言語でのプログラミングに必要 内容は、一通り終わっている [本日の内容] 一周目に関して、いくつか落としている内容 => 落穂ひろいをする => できれば、今日から 2周目に... s_input.h/s_outpu.h の使い方 (API) プログラム作成時 ヘッダファイルはインクルードする #include "s_input.h" #include "s_print.h" => しばらくは、stdio.h と同じ扱い コンパイル時 cc の後ろに -I ~/c/include を追加する => s_input.h/s_print.h のある場所を指定 使えるようになる関数 s_print.h <= 出力関数 s_print_char char 型 : 文字の出力 putchar と同じ s_print_string 文字列の出力 printf と同じ s_print_int int 型 : 整数の出力 以前講義中でつくった整数出力関数 s_print_newline 改行の出力 putchar ( '\n' ) / printf ( "\n" ) と同じ s_input.h <= 入力関数 s_input_char char 型 : 文字の入力 getchar と同じ s_input_string 文字列の入力 s_input_int int 型 : 整数の入力 基本的なデータ型 ( 文字列[char *], 文字[char], 整数[int] ) の 入出力関数を紹介 関数の「返り値」 関数は、return 命令によって、途中で終了できる。 この時に、return 命令の後ろに「式」を書くと、 「式」の値(計算の結果)が、「関数の返り値」になる 「返り値」を持つ関数は、関数の前の void を、その値の型に する必要がある。 「返り値」もつ関数は、式の中で利用できる これまでと同様、「返り値」を持つ関数を、プログラムの一部の 呼び出しのために利用する(void の関数と同じ)に利用可能 => 「返り値」は、無視される 特に main 関数の「返り値」について 今まで : main には、必ず最後に return 0 をかいていた main の前は int と書いてあった => main 関数も返り値(整数値:int)を返す関数 普通の関数 他の関数から呼び出される(cf. main は、基本、そうではない) => 返り値も、他の関数に返されて、利用される main は、他の関数から呼ばれてない 引数は ... ? => コマンドラインから作られて、渡される(shell が渡している) 返り値は .. ? => コマンドに戻っている (shell に戻っている) shell で「$?」で参照する事ができる これは、「習慣的には」コマンド「エラー情報」を表すとなっている 特に 0 が返った場合は、「正常終了」、それ以外の場合はエラーコード(異常がおきて、 その詳細は、その値で区別する ) => これまで main で return 0 としてきたのは、「正常終了」という意味だった 特に、make では、コマンドの返すエラーコードが 0 でないと、 途中で終了する という規則になっているので、自分が作成したプログラムを make で利用する場合は、「return 0」(or もし、エラーあるなら、 「return エラーコード」) の記述が必須になる。 関数の副作用と実行順序 返り値を持つ関数は、式の中で利用できる f(x) = x^2, g(x) = x + 1 例 : f(2) + g(3) => 2^2 + 3+1 => 4 + 4 => 8 例 : f(2) + g(3) => 2^2 + g(3) => 4 + g(3) => 4 + (3+1) => 4 + 4 => 8 式の評価(値の計算)には順序がある 式の値には(今、皆さんが学んでいる範囲では..)評価順序は無関係 C 言語の関数では(数学の関数にない)副作用がある 副作用 : 関数の値とは無関係な作用 !! 副作用を考える、評価順序をきちんととらえる必要がある 式の中の関数は、左から右に評価 関数合成は、内から外 f(g(x)) => g();f() 引数が複数あるときには、左から右に評価される。 h(x,y) = x * y h(f(2),g(3)) => g(3), f(2), h(4,4) 現時点で、副作用としっているのは 出力と入力の二種類だけ
Download : 20180629-01.c
/* * 20180629-01-QQQQ.c * 文字を */ #include <stdio.h> /* * to_lower : 引数で与えられた文字が大文字なら小文字にする */ char to_lower ( char ch ) { if ( 'A' <= ch ) { /* もし、ch が 'A' 以上で.. */ if ( ch <= 'Z' ) { /* 更に、ch が 'Z' 以下なら.. */ /* ** この部分を完成させなさい */ } /* else {} */ /* else 以下はないので省略 */ } /* else {} */ return ch; /* 大文字でない場合は、そのまま返す */ } /* * main 関数 */ int main ( void ) { printf ( "一文字入力して、[Enter] キーを押してください : " ); putchar ( to_lower ( getchar() ) ); putchar ( '\n' ); return 0; }
D
$ ./20180629-01-QQQQ.exe 一文字入力して、[Enter] キーを押してください : d $
Download : 20180629-02.c
/* * 20170616-02-QQQQ.c * 二つの整数値を入力して、その商を出力する */ #include <stdio.h> #include "s_print.h" #include "s_input.h" /* * remainder */ int remainder ( int a ) { s_print_string ( "割る整数を入力して、[Enter] を押してください : " ); /* ** この部分を完成させなさい */ } /* * main */ int main ( void ) { s_print_string ( "割られる整数を入力して、[Enter] を押してください : " ); s_print_int ( remainder ( s_input_int() ) ); s_print_newline(); return 0; }
100 13
$ ./20180629-02-QQQQ.exe 割られる整数を入力して、[Enter] を押してください : 100 割る整数を入力して、[Enter] を押してください : 13 9 $