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 $