/*
* 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 $