/* * 20210430-01-QQQQ.c * */ #include <stdio.h> #include <string.h> /* * こだまでしょうか * (c) 金子みすゞ * http://www.ad-c.or.jp/campaign/self_area/03/index.html */ void kakko ( char *message ) { printf ( "「" ); printf ( message ); printf ( "」" ); } void iu ( char *message ) { kakko ( message ); printf ( "っていうと" ); if ( !strcmp( message, "もう遊ばない" ) ) { kakko ( "遊ばない" ); /* 特別扱い / 条件を確認する */ } else { kakko ( message ); /* いつもと同じ */ } printf ( "っていう。\n" ); } /* * main */ int main ( void ) { iu ( "遊ぼう" ); iu ( "馬鹿" ); iu ( "もう遊ばない" ); printf ( "そうして、あとでさみしくなって、\n" ); iu ( "ごめんね" ); printf ( "こだまでしょうか、いいえ、誰でも。\n" ); return 0; } /* 「遊ぼう」っていうと「遊ぼう」っていう。 「馬鹿」っていうと「馬鹿」っていう。 「もう遊ばない」っていうと「遊ばない」っていう。 そうして、あとでさみしくなって、 「ごめんね」っていうと「ごめんね」っていう。 こだまでしょうか、いいえ、誰でも。 */
/* * 20210507-03-QQQQ.c * 文字だけで「Hello, 自分のイニシャル」を出力するプログラム * サンプルは、イニシャルが「S.K 」の場合 */ #include <stdio.h> /* * main */ int main ( void ) { /* 「Hello, 」の出力 */ putchar ( 'H' ); /* 「H」 一文字の出力 */ putchar ( 'e' ); putchar ( 'l' ); putchar ( 'l' ); putchar ( 'o' ); putchar ( ',' ); putchar ( ' ' ); /* イニシャルの出力 */ putchar ( 'S' ); putchar ( '.' ); putchar ( 'K' ); putchar ( '\n' ); /* 「改行」の出力 */ return 0; } /* 目的は(最終的に..)画面に、次の文字列を出力する事(イニシャルが「S.K」の時) 「Hello, S.K<改行>」 => printf を利用してよいのであれば、簡単 printf ( "Hello, S,K\n" ); 課題としては、 printf をつかわず、putchar だけでやれ putchar を繰り返し ( 順接 ) で、実現する 一文字ずつ出力する putchar ( 'H' ); putchar ( 'e' ); .. putchar ( 'd' ); putchar ( '\n' ); */
#include <stdio.h> /* helloName という関数 ( name という引数[仮引数変数]がある ) => この命令列が行う行為は、「name」という変数に どんなもの(値)が入っているかによって、異なる */ void helloName ( char *name ) { printf ( "Hello, " ); printf ( name ); printf ( "さん\n" ); } /* 引数無し関数 void helloKurino ( void ) { printf ( "Hello, " ); printf ( "栗野" ); printf ( "さん\n" ); } => プログラムを作成した時点で、振る舞いが確定している */ int main(void) { helloName ( "鈴木" ); helloName ( "田中" ); return 0; }
#include <stdio.h> int main(void) { printf ( "出力するのは「" ); putchar ( 'H' ); /* 「H」という文字を画面に出力する */ printf ( "」という文字一文字\n" ); return 0; }
#include <stdio.h> /* helloName という関数 ( name という引数[仮引数変数]がある ) => この命令列が行う行為は、「name」という変数に どんなもの(値)が入っているかによって、異なる */ void helloName ( char *name ) { printf ( "Hello, " ); printf ( name ); /* name に指定されている名前が「渡辺」の場合は、敬称を「先生」にする */ if ( !strcmp( name, "渡辺" ) ) { /* name と「渡辺」が等しい */ /* 渡辺先生の場合 */ printf ( "先生\n" ); } else { printf ( "さん\n" ); /* <= 敬称 : 対象によって異なる */ /* 田中さん、鈴木さん */ } } /* 引数無し関数 void helloKurino ( void ) { printf ( "Hello, " ); printf ( "栗野" ); printf ( "さん\n" ); } => プログラムを作成した時点で、振る舞いが確定している */ int main(void) { helloName ( "鈴木" ); helloName ( "田中" ); helloName ( "渡辺" ); return 0; }
#include <stdio.h> /* helloName という関数 ( name という引数[仮引数変数]がある ) => この命令列が行う行為は、「name」という変数に どんなもの(値)が入っているかによって、異なる */ void helloName ( char *name ) { printf ( "Hello, " ); printf ( name ); /* name に指定されている名前が「渡辺」の場合は、敬称を「先生」にする */ if ( !strcmp( name, "渡辺" ) ) { /* name と「渡辺」が等しい */ /* 渡辺先生の場合 */ printf ( "先生\n" ); } else { if ( !strcmp( name, "小紫" ) ) { printf ( "先生\n" ); } else { printf ( "さん\n" ); /* <= 敬称 : 対象によって異なる */ /* 田中さん、鈴木さん */ } } } /* 引数無し関数 void helloKurino ( void ) { printf ( "Hello, " ); printf ( "栗野" ); printf ( "さん\n" ); } => プログラムを作成した時点で、振る舞いが確定している */ int main(void) { helloName ( "鈴木" ); helloName ( "田中" ); helloName ( "渡辺" ); helloName ( "小紫" ); return 0; }
#include <stdio.h> #include <string.h> /* strcmp を利用するときに追加 */ /* helloName という関数 ( name という引数[仮引数変数]がある ) => この命令列が行う行為は、「name」という変数に どんなもの(値)が入っているかによって、異なる */ void helloName ( char *name ) { printf ( "Hello, " ); printf ( name ); /* name に指定されている名前が「渡辺」の場合は、敬称を「先生」にする */ if ( !strcmp( name, "渡辺" ) ) { /* name と「渡辺」が等しい */ /* 渡辺先生の場合 */ printf ( "先生\n" ); } else if ( !strcmp( name, "小紫" ) ) { printf ( "先生\n" ); } else if ( !strcmp( name, "水野" ) ) { printf ( "先生\n" ); } else if ( !strcmp( name, "古津" ) ) { printf ( "先生\n" ); } else { printf ( "さん\n" ); /* <= 敬称 : 対象によって異なる */ /* 田中さん、鈴木さん */ } } /* 引数無し関数 void helloKurino ( void ) { printf ( "Hello, " ); printf ( "栗野" ); printf ( "さん\n" ); } => プログラムを作成した時点で、振る舞いが確定している */ int main(void) { helloName ( "鈴木" ); helloName ( "田中" ); helloName ( "渡辺" ); helloName ( "小紫" ); return 0; }
#include <stdio.h> #include <string.h> /* strcmp を利用するときに追加 */ /* helloName という関数 ( name という引数[仮引数変数]がある ) => この命令列が行う行為は、「name」という変数に どんなもの(値)が入っているかによって、異なる */ void helloName ( char *name ) { printf ( "Hello, " ); printf ( name ); /* name に指定されている名前が「渡辺」の場合は、敬称を「先生」にする */ if ( !strcmp( name, "渡辺" ) ) { /* name と「渡辺」が等しい */ /* 渡辺先生の場合 */ printf ( "先生\n" ); } else if ( !strncmp( name, "栗", 2 ) ) { /* 先頭の 2 文字が「栗」 */ /* 全角一文字は、長さが 1 でない.. */ printf ( "先生\n" ); } else { printf ( "さん\n" ); /* <= 敬称 : 対象によって異なる */ /* 田中さん、鈴木さん */ } } /* 引数無し関数 void helloKurino ( void ) { printf ( "Hello, " ); printf ( "栗野" ); printf ( "さん\n" ); } => プログラムを作成した時点で、振る舞いが確定している */ int main(void) { helloName ( "鈴木" ); helloName ( "田中" ); helloName ( "渡辺" ); helloName ( "栗野" ); helloName ( "栗原" ); helloName ( "粟野" ); return 0; }
#include <stdio.h> int main(void) { printf ( "Hello, World\n" + 1 ); /* 文字列に +1 すると、先頭の文字が一文字欠けて、 1 文字分、短くなった文字列に変わる */ return 0; }
#include <stdio.h> int main(void) { printf ( "Hello, World\n" ); /* 文字列そのもの */ printf ( "Hello, World\n" + 1 ); /* 先頭の一文字が削れる */ printf ( ( "Hello, World\n" + 1 ) + 1 ); /* 先頭の二文字が削れる*/ printf ( "Hello, World\n" + 3 ); printf ( "Hello, World\n" + 4 ); printf ( "Hello, World\n" + 5 ); printf ( "Hello, World\n" + 6 ); printf ( "Hello, World\n" + 7 ); return 0; }
#include <stdio.h> int main(void) { printf ( "Hello\n" ); /* 「Hello<改行>」 */ printf ( "Hello\n" + 1 ); /* 「ello<改行>」 */ printf ( "Hello\n" + 2 ); printf ( "Hello\n" + 3 ); printf ( "Hello\n" + 4 ); printf ( "Hello\n" + 5 ); /* 「<改行>」 */ printf ( "Hello\n" + 6 ); /* 「」 <=> "" (空文字列) */ /* 文字列に、整数値を加えてゆくと、 どんどん、文字列の長さが減ってゆき、 最終的に、長さが0 (空文字列「""」)になる */ return 0; }
#include <stdio.h> void func(void) { printf ( "関数 func を呼び出しました。\n" ); func(); /* 自分自身を呼び出している ( 再帰呼び出し ) */ } int main(void) { printf ( "関数 func を呼び出す前\n" ); /* => 「関数 func を呼び出す前\n」が表示される */ func(); /* 関数 func の呼び出し */ /* printf ( "関数 func を呼び出しました。\n" ); => 「関数 func を呼び出しました。\n」が表示される func(); printf ( "関数 func を呼び出しました。\n" ); => 「関数 func を呼び出しました。\n」が表示される func(); /* 自分自身を呼び出している ( 再帰呼び出し ) printf ( "関数 func を呼び出しました。\n" ); => 「関数 func を呼び出しました。\n」が表示される func(); /* 自分自身を呼び出している ( 再帰呼び出し ) */ printf ( "関数 func を呼び出す後\n" ); return 0; }
#include <stdio.h> void func(char *message) { if ( !strcmp( message, "" ) ) { /* もう、なにもしない */ } else { printf ( message ); func( message + 1 ); } } int main(void) { printf ( "関数 func を呼び出す前\n" ); func("1234567\n"); /* => message <- "1234567\n" printf ( message ); func( message + 1 ); => printf ( "1234567\n" ); => 「1234567<改行>」 func( "1234567\n" + 1 ); func( "234567\n" ); => message <- "234567\n" printf ( message ); func( message + 1 ); => printf ( "234567\n" ); => 「234567<改行>」 func( "234567\n" + 1 ); ... => func( "\n" ); => message <- "\n" printf ( message ); func( message + 1 ); => printf ( "\n" ); => 「<改行>」 func( "\n" + 1 ); => func( "" ); => message <- "" printf ( message ); func( message + 1 ); => printf ( "" ); func( "" + 1 ); <= エラーがおきた */ printf ( "関数 func を呼び出す後\n" ); return 0; }
前回(2021/04/30)の内容 (復習) 関数 命令列に名前をつける事(関数定義)ができ、 その名前を使って、その命令を呼び出せる(関数呼び出し) => 機能的には、なくてもこまらない <= 色々な利得がある 抽象化 => 人間が分かりやすく => ミスがヘリ => ソフトウェアの生産性が上がる ソフトウェアの生産性を高める事を目的とする学問が => ソフトウェア工学 引数付き関数 命令の一部を変数化し、後から、変数の値を決める事によって 関数の振る舞いを変えることができる # 「後」:プログラムの実行時(プログラム作成時の後) 文字と文字列 「文字列」は、「"」に挟まれた、「文字」の並び 例 : "Hello, World\n" は、「Hello, World<改行>」という文字列 => 13 文字からなる 「H」,「e」,...「d」,「\n」<改行> 「文字」は、ASCII Code 表にある文字(半角英数字記号) !! この講義では、「全角文字」は、文字として扱わない !! ASCII Code 表に載っている「文字」だけを文字とする !! # 1 byte で表現できる文字コードのみを扱う !! "田中" の時に、「田」や、「中」は文字として考えない => 基本、文字は、「キーボードのキーに印刷されていて、キーを押すと入力されるもの..」 印字できないものがある エスケープシーケンスを利用して、文字列で、文字を表現する エスケープシーケンス 「\」から始まる「文字の並び」で、それで一つの「文字」を表す 例 : \n (改行), \" (ダブルクォーテーション), \\ (円マーク/バックスラッシュ) 等 \b ベルを鳴らす.. \0 NUL 文字 [2021/05/07] 文字 「'」で挟まれたものが文字 「H」という文字は、'H' で表す !!! "H" <= 長さが 1 の「文字列」 !!! 'H' <= 一つの「文字」 文字列は文字の並んだもの => エスケープシーケンスのルール '\n' <= 改行を表す文字 文字を出力する 「putchar ( 文字 )」 とすると、「文字」が画面に出力される cf. printf ( 文字列 ) とすると、「文字列」が画面に出力される <いくつかわかること> putchar という新しい命令を覚えた => printf しかしらなかった 文字列を出力する方法 ( printf ) と、 文字を出力する方法 ( putchar ) は異なる <= 「文字」と「文字列」が違うので、 「『画面に出力する』という同じ行為」なのに、 それを実現するための命令が異なる !! やりたい事が違うならな、違うやり方を取る !! <= やりたい事が同じでも、対象が異なれば、違うやり方をとる必要がある => 何かをしたいとき 対象によってやり方が異なる 対象に結び付いた、方法を学ぶ必要 API 操作をしたい対象に紐づいて、 目的を実現するには、どのような手段をとればよいかを 決めたルール !!! 新しい命令を覚えたい !!! 新しい対象と、(それを操作するライブラリを入手して..) API を覚えて、そのルールに従い命令を並べればよい !! printf / putchar !! 標準ライブラリの中に入っている !! その使い方 (API) の一部をこの講義で学ぶ事 VcXsrv のインストール 目的 Ubuntu から、グラフィックスをしたい # 画像描画をしたい 画像の描画先を用意する必要がある VcXsrv が提供するする機能 やること 一度だけやること Windows : VcXsrv をインストールする インストーラ vcxsrv-64.1.20.9.0.installer.exe をダウロードし、 それを実行して、インストールする Ubuntu : X11 をインストールする sudo apt install x11-apps 毎回やること Windows で PC を起動したとき XLaunch を起動 ( => 下にアイコンができる ) Ubuntu の場合 export DISPLAY=:0.0 を実行する # Ubuntu で、毎回、設定するのがいやな場合は、 # 次の命令を一度だけやればよい # echo export DISPLAY=:0.0 >> ~/.profile 確認の方法 Ubuntu 上で xeyes を実行してみる xeyes => 止めたいときには、[x]ボタンでもよいし、 Ubuntu のところで Ctrl-C ( [Ctrl] + [c] ) 引数付き関数 引数によって、挙動を変える関数 変数の部分が変化するだけ => 「連続的」に変化する 入力(引数)の変化が小さければ、振る舞いの変化も小さい 入力(引数)の変化が大きければ、振る舞いの変化も大きい [条件分岐(if 構文)] 引数によって、その振る舞いを大きく変えたい場合に利用する機能 if 構文と strcmp 関数を利用して対応できる strcmp 関数 : 二つの文字列を比較する if ( !strcmp ( A, B ) ) { 条件判断をしている X 条件が成立しているとき(特別扱い) } else { Y 条件が成立していないとき } A と B が同じ文字列なら X の命令を、そうでなければ Y の命令を行う <ポイント> X, Y の二つの命令があるが、そのどちらか一方しか実行されない !! 順接で、X;Y; と書くと、X, Y が実行された !! => 書いた量と同じだけ実行される !! <= if 構文の場合は、一方実行されない 関数の振る舞いが、 if 構文を利用する事により、不連続にふるまう 例: 1 (x>0) f(x) = { -1 (x<=0) 「else if」を使うと更に複数の命令が選べる if ( C1 ) { P1 一つ目の条件(C1)が成立 } else if ( C2 ) { P2 二つ目の条件(C2) が成立 } .. } else { どの条件も成立しなかかった場合 Pn デフォルト(標準/一般的/例外でない)処理 } C1 の時 P1、そうでなく C2 の時は P2 .. いずれでもないと Pn おまじない ( strcmp を利用する場合に使う ) #include <string.h> strncmp ( A, B, N ); A と B の先頭の N 文字だけを比較する !strncmp ( "abc", "abz", 3 ); : 等しくない !strncmp ( "abc", "abz", 2 ); : 等しい !strncmp ( "abc", "abz", strlen( "abc" ) ); : 等しくない !strncmp ( "ab", "abz", strlen( "ab" ) ); : 等しい == プログラム 命令の列 「命令」<= 何かをするもの printf ( 文字列 ) => 画面に文字列を出す putchar ( 文字 ) => 画面に文字出す 後は、その組み合わせ 組見合わせに、名前をつける => 関数定義 名前で呼び出す (関数呼び出し) => 命令が実行される 組み合わせ方 順接 : 命令を並べるだけ、並べた命令をその順に実行 条件分岐 : 条件によって、命令の一方を実行 再帰呼び出し 「繰り返し」という、新しい命令の組み合わせ方 関数の中で、他の関数を呼び出す 関数の中で、自分自身を呼び出す事もできる <= 再帰呼び出し => 命令の一部が何度も何度も繰り返し実行される p-010.c のように、無条件に再帰呼び出しをすると、 => 命令が無限に繰り返してしまう <= 望みの回数だけ実行したい => 再帰呼び出しをするかしないかを判断する => if 構文でできる if 構文と再帰呼び出しを組み合わせる事により、 繰り返しの回数を制御し、 繰り返し回数を有限にする事できる。
課題プログラム内の「/*名前:ここ*/」の部分を書き換え「/*この部分を完成させなさい*/」の部分にプログラムを追加して、プログラムを完成させます。
Download : 20210507-01.c
/* * 20210507-01-QQQQ.c * 一つ目の引数の文字列の長さの個数だけ、 * 二つ目の引数の文字列を出力する関数の作成 */ #include <stdio.h> #include <string.h> /* strcmp を利用するために必要 */ /* * n_times_print_string ( char *times, char *string ) * times : 繰返し回数を表す文字列 * string : 出力する文字列 */ void ntimes_print_string ( char *ntimes, char *string ) { if ( !strcmp ( ntimes, "" ) ) { /* ntimes の長さが 0 */ /* 何もする必要はない (関数は終了) */ } else { /* そうでなければ */ /* 二つ目の文字列を出力 */ printf ( string ); /* 再帰を利用して、残りの回数だけプリント */ /* 一つ目の引数は、長さを減らす必要がある */ /* 二つ目の引数は、そのまま渡してよい */ /* ** この部分を完成させなさい */ } } /* * main */ int main ( void ) { /* "Hello, World\n" を 5 回数 */ ntimes_print_string ( "12345", "Hello, World\n" ); /* "こんにちは、皆さん\n" を 8 回数 */ /* ** この部分を完成させなさい */ return 0; }
$ ./20210507-01-QQQQ.exe Hello, World Hello, World Hello, World Hello, World Hello, World こんにちは、皆さん こんにちは、皆さん こんにちは、皆さん こんにちは、皆さん こんにちは、皆さん こんにちは、皆さん こんにちは、皆さん こんにちは、皆さん $
Download : 20210507-02.c
/* * 20210507-02-QQQQ.c * 引数の文字列の長さに対するフィボナッチ数だけの文字「*」を出力する関数 * * 0 ( n = 0 の時 ) * fib(n) = { 1 ( n = 1 の時 ) * fib(n-1)+fib(n-2) ( その他 [n > 1 の時] ) */ #include <stdio.h> #include <string.h> /* strcmp を利用するために必要 */ /* * void c_fibnacci ( char *n ) * n : その長さで、整数値を表す文字列 */ void c_fibnacci ( char *n ) { if ( !strcmp ( n, "" ) ) { /* n の長さが 0 */ /* 何もしなくて良い (関数は終了) */ } else if ( !strcmp ( n + 1, "" ) ) { /* n の長さが 1 */ /* ** この部分を完成させなさい */ putchar ( '*' ); } else { /* その他 ( n の長さが 1 より長い ) */ /* 再帰呼出しを二度行う */ /* 一度目は、1 だけ長さを減らす */ /* ** この部分を完成させなさい */ /* ニ度目は、2 だけ長さを減らす */ c_fibnacci ( n + 2 ); } } /* * main */ int main ( void ) { /* fib(4) = 3 回だけ「*」を出す */ c_fibnacci ( "****" ); putchar ( '\n' ); /* 改行 */ /* fib(7) = 13 回だけ「*」を出す */ /* ** この部分を完成させなさい */ putchar ( '\n' ); /* 改行 */ return 0; }
$ ./20210507-02-QQQQ.exe *** ************* $
Download : 20210507-03.c
/* * 20210507-03-QQQQ.c * 文字だけで「Hello, 自分のイニシャル」を出力するプログラム * サンプルは、イニシャルが「S.K 」の場合 */ #include <stdio.h> /* * main */ int main ( void ) { /* 「Hello, 」の出力 */ putchar ( 'H' ); /* 「H」 一文字の出力 */ putchar ( 'e' ); putchar ( 'l' ); /* ** この部分を完成させなさい */ putchar ( ',' ); putchar ( ' ' ); /* イニシャルの出力 */ /* ** この部分を完成させなさい */ putchar ( '\n' ); /* 「改行」の出力 */ return 0; }
$ ./20210507-03-QQQQ.exe Hello, S.K $