Download : sample-001.c
/* * 2019/06/07 sample-001.c */ #include <stdio.h> #include <string.h> /* * myPrintf (No.1) * * myPrintf の考え方 * * <意味を考える> * 「文字列」を全部を出力 * = 文字列の先頭の文字を出力 + 残りの文字列を出力 * = 「文字列」の先頭の文字を出力 + 「残りの文字列」を全部を出力(再帰) * * <再帰関数の形にする> * myPrintf ( 「文字列」 ) * = putchar ( 「文字列」の先頭 ) + myPrintf ( 「文字列」の残り ) * = putchar ( *「文字列」 ) + myPrintf ( 「文字列」+ 1 ) * * <再帰を利用する場合は「終了の場合」を作る> * myPrintf ( 「文字列」 ) * = if ( 「文字列」が空文字列の時 ) { * なにもしない * } else { * putchar ( *「文字列」 ); * myPrintf ( 「文字列」+ 1 ); * } * */ void myPrintf ( char *string ) { if ( !strcmp ( string, "" ) ) { /* 空文字列なら..(終了条件) */ /* 何もしなくてよい */ } else { /* まだ出力する文字があれば */ putchar ( *string ); /* 先頭の文字を出力する */ myPrintf ( string + 1 ); /* 残りの文字列を出力する */ } } /* * main */ int main ( void ) { myPrintf ( "abcde\n" ); myPrintf ( "xyz\n" ); return 0; }
$ ./sample-001.exe abcde xyz $
Download : sample-002.c
/* * 2019/06/07 sample-002.c */ #include <stdio.h> #include <string.h> /* * myPrintf (No.2) * * 終了条件を EOS ( '\0' : End Of String) で判定 */ void myPrintf ( char *string ) { if ( *string == '\0' ) { /* 先頭が空文字('\0')なら空文字列 */ /* 文字の比較は 「==」で行う */ /* 何もしなくてよい */ } else { /* まだ出力する文字があれば */ putchar ( *string ); /* 先頭の文字を出力する */ myPrintf ( string + 1 ); /* 残りの文字列を出力する */ } } /* * main */ int main ( void ) { myPrintf ( "abcde\n" ); myPrintf ( "xyz\n" ); return 0; }
$ ./sample-002.exe abcde xyz $
Download : sample-003-01.c
/* * 2019/06/07 sample-003-01.c */ #include <stdio.h> /* putchar の為に必要 */ /* * println : 文字列を出力するついでに最後に改行を付加する */ void println ( char *string ) { if ( *string == '\0' ) { putchar ( '\n' ); /* 最後に改行を出力する */ } else { putchar ( *string ); println ( string + 1 ); } }
Download : sample-003.c
/* * 2019/06/07 sample-003.c */ #include <stdio.h> /* * println の extern 宣言 */ extern void println ( char *string ); /* 関数の頭部の前に extern 後に ; */ /* * main */ int main ( void ) { println ( "abcde" ); /* 改行(\n)が不要になった */ println ( "xyz" ); return 0; }
$ ./sample-003.exe abcde xyz $
Download : sample-004-01.c
/* * 2019/06/07 sample-004-01.c */ /* もはや printf も putchar も使わないので #include <stdio.h> も不要 */ /* * println の extern 宣言 */ extern void println ( char *string ); /* * triangle again * */ void triangle ( char *string ) { if ( *string == '\0' ) { /* 何もしなくてよい */ } else { println ( string ); /* println を利用する */ triangle ( string + 1 ); } }
Download : sample-004.c
/* * 2019/06/07 sample-004.c */ /* * triangle, println の extern 宣言 */ extern void println ( char *string ); extern void triangle ( char *string ); /* * main */ int main ( void ) { triangle ( "****" ); /* 間接的に println を使うので、改行が不要 */ println ( "" ); /* 空行を出力する */ triangle ( "*******" ); return 0; }
$ ./sample-004.exe **** *** ** * ******* ****** ***** **** *** ** * $
Download : sample-005-01.c
/* * 2019/06/07 sample-005-01.c */ /* * println の extern 宣言 */ extern void println ( char *string ); /* * rtriangle -- triangle の「逆」版 */ void rtriangle ( char *string ) { if ( *string == '\0' ) { /* 何もしなくてよい */ } else { rtriangle ( string + 1 ); /* 最初に残りをやって.. */ println ( string ); /* 最後に先頭を処理する */ } }
Download : sample-005.c
/* * 2019/06/07 sample-005.c */ /* * rtriangle, println の extern 宣言 */ extern void println ( char *string ); extern void triangle ( char *string ); extern void rtriangle ( char *string ); /* * main */ int main ( void ) { triangle ( "****" ); rtriangle ( "*******" ); return 0; }
$ ./sample-005.exe **** *** ** * * ** *** **** ***** ****** ******* $
Download : sample-006-01.c
/* * 2019/06/07 sample-006-01.c */ /* * extern 宣言 */ extern println ( char *string ); /* * string の文字列の長さ回 「Hello World\n」を表示する */ void nhello ( char *string ) { if ( *string == '\0' ) { /* 既に全てすんだ */ /* 何もしなくてよい */ } else { /* まだ、やる事がある */ println ( "Hello, World" ); /* 取り敢えず、一度出力 */ nhello ( string + 1 ); /* 文字列を短くして、残りを行う (再帰) */ } }
Download : sample-006.c
/* * 2019/06/07 sample-006.c */ /* * extern 宣言 */ extern println ( char *string ); extern nhello ( char *string ); /* * n 回数の繰返し */ int main ( void ) { println ( " 3 回 Hello : " ); nhello ( "***" ); /* 長さ 3 の文字列を指定 */ println ( " 5 回 Hello : " ); nhello ( "12345" ); /* 長さ 5 の文字列を指定 */ return 0; }
$ ./sample-006.exe 3 回 Hello : Hello, World Hello, World Hello, World 5 回 Hello : Hello, World Hello, World Hello, World Hello, World Hello, World $
Download : sample-007-01.c
/* * 2019/06/07 sample-007-01.c */ /* * extern 宣言 */ extern println ( char *string ); /* * string の文字列の長さ回 message を出力する * 基本は nhello と同じだが、出力する message が異なる */ void nprintln ( char *string, char *message ) { if ( *string == '\0' ) { /* 既に全てすんだ */ /* 何もしなくてよい */ } else { /* まだ、やる事がある */ println ( message ); /* 取り敢えず、一度出力 */ nprintln ( string + 1, message ); } }
Download : sample-007.c
/* * 2019/06/07 sample-007.c */ /* * extern 宣言 */ extern void nprintln ( char *string, char *message ); /* * n 回数の繰返し */ int main ( void ) { println ( " 3 回「Hello」: " ); nprintln ( "***", "Hello" ); println ( " 5 回「こんにちは」: " ); nprintln ( "12345", "こんにちは" ); return 0; }
$ ./sample-007.exe 3 回「Hello」: Hello Hello Hello 5 回「こんにちは」: こんにちは こんにちは こんにちは こんにちは こんにちは $
Download : sample-008-01.c
/* * 2019/06/07 sample-008-01.c */ #include <stdio.h> /* putchar が必要なので.. */ /* * extern 宣言 */ extern println ( char *string ); /* * itemize ( char *item, char *message ) * 個々の文字の前にマークを付ける */ void itemize ( char *item, char *message ) { if ( *item == '\0' ) { /* 既に全てすんだ */ /* 何もしなくてよい */ } else { /* まだ、やる事がある */ putchar ( *item ); /* 項目 */ putchar ( ' ' ); /* 一文字空けて */ println ( message ); /* 一行出力 */ itemize ( item + 1, message ); } }
Download : sample-008.c
/* * 2019/06/07 sample-008.c */ /* * extern 宣言 */ extern void itemize ( char *item, char *message ); /* * n 回数の繰返し */ int main ( void ) { println ( " 3 回「Hello」: " ); itemize ( "***", "Hello" ); println ( " 5 回「こんにちは」: " ); itemize ( "12345", "こんにちは" ); return 0; }
$ ./sample-008.exe 3 回「Hello」: * Hello * Hello * Hello 5 回「こんにちは」: 1 こんにちは 2 こんにちは 3 こんにちは 4 こんにちは 5 こんにちは $
Download : sample-009-01.c
/* * 2019/06/07 sample-009-01.c */ #include <stdio.h> /* putchar が必要なので.. */ /* * extern 宣言 */ extern println ( char *string ); extern nprintln ( char *string, char *message ); /* * mnprintln ( char *m, char *n, char *message ) * m * n 回数 message を出力する */ void mnprintln ( char *m, char *n, char *message ) { if ( *m == '\0' ) { } else { nprintln ( n, message ); /* n 回数は nprintln にお任せ */ mnprintln ( m + 1, n, message ); } }
Download : sample-009.c
/* * 2019/06/07 sample-009.c */ /* * extern 宣言 */ extern void mnprintln ( char *m, char *n, char *message ); /* * n 回数の繰返し */ int main ( void ) { println ( " 3 x 5 回「Hello」: " ); mnprintln ( "***", "*****", "Hello" ); println ( " 4 x 4 回「こんにちは」: " ); mnprintln ( "1234", "1234", "こんにちは" ); return 0; }
$ ./sample-009.exe 3 x 5 回「Hello」: Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello 4 x 4 回「こんにちは」: こんにちは こんにちは こんにちは こんにちは こんにちは こんにちは こんにちは こんにちは こんにちは こんにちは こんにちは こんにちは こんにちは こんにちは こんにちは こんにちは $
Download : sample-010-01.c
/* * 2019/06/07 sample-010-01.c */ #include <stdio.h> /* putchar が必要なので.. */ /* * nprinttail * n 回数 message を出力するが、行末を変更する。 */ void nprinttail ( char *tail, char *message ) { if ( *tail == '\0' ) { } else { printf ( message ); if ( *tail == '\n' ) { /* 改行の時のみ */ putchar ( '\n' ); /* 改行する */ } else { /* それ以外は */ /* 何もしない */ } nprinttail ( tail + 1, message ); } }
Download : sample-010.c
/* * 2019/06/07 sample-010.c */ /* * extern 宣言 */ extern void println ( char *message ); extern void nprinttail ( char *tail, char *message ); /* * n 回数の繰返し */ int main ( void ) { println ( "毎回改行「Hello」: " ); nprinttail ( "\n\n\n", "Hello" ); println ( "3 回目, 7 回目だけ改行「Hello」: " ); nprinttail ( "12\n456\n", "Hello" ); return 0; }
$ ./sample-010.exe 毎回改行「Hello」: Hello Hello Hello 3 回目, 7 回目だけ改行「Hello」: HelloHelloHello HelloHelloHelloHello $
Download : sample-011.c
/* * 2019/06/07 sample-011.c */ #include <stdio.h> /* * 文字の扱い */ int main ( void ) { printf ( "abc\n" ); /* 「abc(改行)」とでる */ printf ( "abc\n" + 1 ); /* 「bc(改行)」とでる ( 1 を加えると短くなる ) */ putchar ( 'A' ); /* 「A」とでる */ putchar ( '\n' ); /* 「(改行)」とでる */ putchar ( 'A' + 1 ); /* 文字に 1 を加えると ? */ putchar ( '\n' ); /* 「(改行)」とでる */ return 0; }
$ ./sample-011.exe abc bc A B $
Download : sample-012-01.c
/* * 2019/06/07 sample-012-01.c */ #include <stdio.h> /* putchar が必要なので.. */ /* * printcharln ( char ch ) * 文字を出力して改行する */ void printcharln ( char ch ) { putchar ( ch ); /* 指定された文字を出力 */ putchar ( '\n' ); /* その後に改行 */ }
Download : sample-012.c
/* * 2019/06/07 sample-012.c */ /* * extern 宣言 */ extern void printcharln ( char ch ); /* * 文字の扱い(2) */ int main ( void ) { printcharln ( 'A' + 1 ); /* 'B' になった */ printcharln ( 'B' + 1 ); /* 'C' になる */ printcharln ( 'A' + 1 + 1 ); /* これも 'C' になる */ printcharln ( 'A' + 0 ); /* これはもちろん 'A' */ printcharln ( 'A' + 10 ); /* 'A', 'B', .., 'J', 'K' になるはず */ printcharln ( 'A' + 25 ); /* 'Z' !! */ printcharln ( 'Z' + 1 ); /* ??? */ return 0; }
$ ./sample-012.exe B C C A K Z [ $
Download : sample-013-01.c
/* * 2019/06/07 sample-013-01.c */ #include <stdio.h> /* putchar が必要なので.. */ /* * nprintcharln ( char ch ) * 文字を連続 n 個だけ、出力する */ void nprintcharln ( char *n, char ch ) { if ( *n == '\0' ) { putchar ( '\n' ); /* 改行 */ } else { putchar ( ch ); /* 指定された文字を出力 */ nprintcharln ( n + 1, ch + 1 ); } }
Download : sample-013.c
/* * 2019/06/07 sample-013.c */ /* * extern 宣言 */ extern void nprintcharln ( char *n, char ch ); extern void printcharln ( char ch ); extern void println ( char *string ); /* * 文字の扱い(3) */ int main ( void ) { println ( "'A' から 10 個 :" ); nprintcharln ( "1234567890", 'A' ); println ( "'U' から 10 個 :" ); nprintcharln ( "1234567890", 'U' ); println ( "'k' から 10 個 :" ); nprintcharln ( "1234567890", 'k' ); println ( "'0' から 10 個 :" ); nprintcharln ( "1234567890", '0' ); println ( "'9' + 1 は.. :" ); printcharln ( '9' + 1 ); /* 残念ながら "10" ではない */ return 0; }
$ ./sample-013.exe 'A' から 10 個 : ABCDEFGHIJ 'U' から 10 個 : UVWXYZ[\]^ 'k' から 10 個 : klmnopqrst '0' から 10 個 : 0123456789 '9' + 1 は.. : : $
Download : sample-014-01.c
/* * 2019/06/07 sample-014-01.c */ #include <stdio.h> /* putchar が必要なので.. */ /* * printonedigit ( int n ) * 一桁の整数を出力する */ void printonedigit ( int n ) { putchar ( '0' + n ); /* n が 0 〜 9 の場合だけ、正く動く */ } /* * printonedigitln ( int n ) * 一桁の整数を出力して改行する */ void printonedigitln ( int n ) { printonedigit ( n ); /* 一桁の数値を出力し.. */ putchar ( '\n' ); /* 改行する */ }
Download : sample-014.c
/* * 2019/06/07 sample-014.c */ /* * extern 宣言 */ extern void println ( char *string ); extern void printonedigit ( int n ); extern void printonedigitln ( int n ); /* * 整数 */ int main ( void ) { println ( "整数値 0 の出力" ); printonedigitln ( 0 ); /* 「'0'」 でも 「"0"」 でもなく 「0」 */ println ( "整数値 9 の出力" ); printonedigitln ( 9 ); /* 一桁は OK */ println ( "整数値 11 の出力" ); printonedigitln ( 11 ); /* 上手く行かない */ println ( "整数値 1 + 1 の出力" ); printonedigitln ( 1 + 1 ); /* やっと計算がでてきた */ println ( "整数値 5 - 2 の出力" ); printonedigitln ( 5 - 2 ); /* 引算 */ println ( "整数値 3 * 2 の出力" ); printonedigitln ( 3 * 2 ); /* かけ算 */ println ( "整数値 8 / 3 の出力" ); printonedigitln ( 8 / 3 ); /* 小数点以下は余りは切り捨て */ println ( "整数値 8 % 3 の出力" ); printonedigitln ( 8 % 3 ); /* 余りは「%」をで計算 */ println ( "整数値 8 - ( 8 / 3 ) * 3 の出力" ); printonedigitln ( 8 - ( 8 / 3 ) * 3 ); /* 余りを求めるもう一つの方法 */ return 0; }
$ ./sample-014.exe 整数値 0 の出力 0 整数値 9 の出力 9 整数値 11 の出力 ; 整数値 1 + 1 の出力 2 整数値 5 - 2 の出力 3 整数値 3 * 2 の出力 6 整数値 8 / 3 の出力 2 整数値 8 % 3 の出力 2 整数値 8 - ( 8 / 3 ) * 3 の出力 2 $
Download : sample-015-01.c
/* * 2019/06/07 sample-015-01.c */ #include <stdio.h> /* putchar が必要なので.. */ /* * extern 宣言 */ extern void printonedigit ( int n ); /* * printpositiveintsub ( int n ) * 正の整数を出力する */ void printpositiveintsub ( int n ) { if ( n == 0 ) { /* 0 の場合 */ /* もう出力なくてよい */ } else { printpositiveintsub ( n / 10 ); /* 上位の桁を出力し */ printonedigit ( n % 10 ); /* 1 の位を出力する */ } } /* * printpositiveint ( int n ) * 非負の整数を出力する */ void printpositiveint ( int n ) { if ( n == 0 ) { /* 0 の場合は特別扱い */ putchar ( '0' ); /* 0 を出力 */ } else { /* その他の場合は、n 桁の処理 */ printpositiveintsub ( n ); /* 正の場合の処理 */ } } /* * printpositiveintln ( int n ) * 正の整数を出力して改行する */ void printpositiveintln ( int n ) { printpositiveint ( n ); /* 正の整数値を出力し.. */ putchar ( '\n' ); /* 改行する */ }
Download : sample-015.c
/* * 2019/06/07 sample-015.c */ /* * extern 宣言 */ extern void println ( char *string ); extern void printpositiveint ( int n ); extern void printpositiveintln ( int n ); /* * 整数 */ int main ( void ) { println ( "整数値 0 の出力" ); printpositiveintln ( 0 ); println ( "整数値 11 の出力" ); printpositiveintln ( 11 ); println ( "整数値 12345 の出力" ); printpositiveintln ( 12345 ); println ( "整数値 12 * 34 の出力" ); printpositiveintln ( 12 * 34 ); return 0; }
$ ./sample-015.exe 整数値 0 の出力 0 整数値 11 の出力 11 整数値 12345 の出力 12345 整数値 12 * 34 の出力 408 $
#include <stdio.h> int main(void) { putchar ( 'A' ); /* 標準出力(画面)に 「A」を出力[画面に表示される] */ putchar ( 'A' + 1 ); /* +1 すると、次の文字「B」が出力される */ putchar ( 'Z' ); putchar ( 'Z' - 1 ); /* 文字列では +1 できても、-1 できない */ putchar ( 'Z' + 1 ); /* 'Z' の次の文字とは .. ?? */ /* 結果としては.. 「[」になっている.. why ?? */ putchar ( 'a' ); putchar ( 'a' + 1 ); putchar ( '0' ); /* '0' は、「0」という文字(数字)、数値ではない */ putchar ( '0' + 1 ); putchar ( '9' + 1 ); /* '9' の次の「文字」とは... ? */ /* '10' ではない '10' は C 言語的に誤り */ /* "10" は、「文字列」であって「文字」ではない */ /* => 「:」 */ putchar ( 'A' - 1 ); /* ASCII code 表から、A の前なので、「@」になる */ putchar ( '\n' ); /* エスケープ文字「\」はさらに、もう一文字追加して 「普段とは異なる(エスケープ)」意味(文字)を表す 「\n」は「改行を表す */ /* すべての答えは「ASCII code 表」 */ /* 「文字」の順序は「ASCII Code表」の順に決まっていて +1 / -1 をすることにより、順序に従って、文字が決まる */ return 0; }
#include <stdio.h> int main(void) { putchar ( '0' + '0' ); /* これは何がおきるか */ /* '0' にはならない .. */ /* 「`」になった.. */ /* ASCII Code 表から理解できる */ putchar ( 48 ); /* 48 という数値は、ASCII Code から '0' に対応 */ putchar ( '\n' ); return 0; }
#include <stdio.h> int main(void) { printf ( "??" ); /* ?????? SJIS ???????????A?????????? */ putchar ( '\n' ); return 0; }
#include <stdio.h> int main(void) { printf ( "あ" ); /* UTF-8 で保存したので、きちんと表示される */ putchar ( '\n' ); return 0; }
#include <stdio.h> /* 名前を指定すると、その人にあいさつをする */ void print_hello_to ( char *name ) { /* print_hello_to という関数を定義 */ printf ( "こんにちは " ); /* 標準ライブラリ内にあるので、定義せずに利用可能 */ /* リンク時に、ライブラリ内の定義が取り出される */ /* 利用されている関数が、定義済みかどうかは */ /* リンク時にならないとわからない */ /* => 未定義の関数の利用は、リンク時にエラー */ /* <= (コンパイル時でなく)リンク時に定義があれば OK */ printf ( name ); printf ( "さん\n" ); } int main(void) { print_hello_to ( "安部" ); /* ここで、それを利用している */ print_hello_to ( "トランプ" ); return 0; }
#include <stdio.h> /* p-006.c の print_hello_to だけ取り出した */ /* 名前を指定すると、その人にあいさつをする */ void print_hello_to ( char *name ) { /* print_hello_to という関数を定義 */ printf ( "こんにちは " ); printf ( name ); printf ( "さん\n" ); }
#include <stdio.h> /* p-006.c の main だけ取り出した */ int main(void) { print_hello_to ( "安部" ); /* ここで、それを利用している */ print_hello_to ( "トランプ" ); return 0; }
#include <stdio.h> /* p-006.c の main だけ取り出した */ int main(void) { print_hello_to ( "鈴木" ); print_hello_to ( "佐藤" ); return 0; }
/* * 2013/05/10 hanoi.c */ #include <stdio.h> /* * ハノイプログラムには、s_hanoi.h が必要 */ #include "s_hanoi.h" /* * ハノイの塔 プログラム */ /* s_hanoi_init(); ハノイのプログラムの初期化(ハノイの準備を行う) s_hanoi_move ( "1", "2" ); どの棒からどの棒に移すかを指定 棒の指定は左から "1", "2", "3" で指定する */ int main ( void ) { s_hanoi_init(); /* 初期化 */ s_hanoi_move ( "1", "2" ); /* 棒 1 から 棒 2 に円盤を移動 */ getchar(); /* 終了前に結果を確認するために待つ */ return 0; } /* ハノイのプログラムを作るには、(基本) hanoi フォルダに、そのプログラムファイルを保存し make BASE=名前 test とする => Makefile の中に、ハノイプログラムを作るうえでの 特殊な操作や情報が記載されているので、 make が勝手に、その情報を利用して、実行ファイルを作成してくれる !! 結局 cc コマンドを利用しているので、従来の方法をかわらないが、 !! いろいろ細かい(本質的でない : しらべればわかる事)情報が必要なので面倒 */
[前回の内容] 前回(2019/05/31)の内容 「繰り返し」の復習 : 再帰呼出しする関数の定義は「繰返し」を表現 再帰呼び出し 関数を定義する時(その関数はまだ未定義..)に、 その定義の中で、自分自身を呼び出す。 # 今のプログラミング言語では、「できる事が常識」 # 古い言語 (Fortaran, Cobol, BASIC, etc.. )はできない => 自分が呼び出されると、自分が「再度」呼び出される => 自分の中身が、繰り返される => 「中身」の部分を繰り返すために、「再帰」を利用する => 順接/条件分岐/繰り返し(再帰呼び出し)の三つの制御構造によって、万能になる 講義(ソフトウェア概論)の目的 => プログラムが書けるようになる プログラム <=> 命令の並び => 「基本命令」と「その組み合わせ方法」の両方ができればよい 基本命令 => いろいろな API (ライブラリの利用) [単語の学習] 組み合わせ方法 => 制御構造三つあれば、完全 [文法の学習] => 一通りの内容が終わっている # 他の言語でも同様(基本命令:ググる/制御構造:三つの表現を理解する) [今後] 基本命令 : API => 今後も色々紹介したい 制御構造 : 同じ事をする、もっと便利な表現がある => 自分がプログラムを書く場合は、(便利だけど..)不要だが、他の人のプログラムを読むために必要 => プログラムを学ぶ上で、最も有効な方法は、 他の(優秀な)人の書いたプログラムを読んで、 (変更を加えたうえで)実行する プログラム : 命令の組み合わせ 命令 : 何[対象 : Object] をどう[動作] する 今まで : 対象を「文字列」に限定していた => 対象のバリエーションに着目して話をしてゆく [文字] 「文字列」とは異なる 例:「文字列」が長さをもっているが、「文字」は、つねに一つのもの 「文字列」は +1 すると、長さが短くなる、「文字」は次の「文字」になる 「文字列」から「文字を取り出す」事できる 「文字」の扱い : C 言語では「文字列」以外に「文字」が扱える 文字のリテラル(文字自身の表現) : 「'」+「一文字」+「'」 例 : 「'A'」という表現は、「A」という「文字」を表す 文字の入出力 : getchar/putchar 関数で、文字が入出ができる [今週の話] 「文字」について続き 「(半角英数記号)文字」は、「ASCII 表に従って、小さな整数と 1 対 1 対応になっている」 # 「小さな整数」は、「ASCII 表では、0 ? 127 の値」=> 1 byte に入る # => 128 種類の「文字」しか扱えない # => キーボード上の 文字の数に対応している # <= 全角文字(日本語)の個数には対応していない # => もし、「日本語」を表現するとすると 1 文字を 1 byte では表現できない # => 一文字を 2 byte で表現する事になる # !! 最近 C 言語では、wchar 1 byte に入らない文字を扱えるようになっているが、 # !! => この講義では扱わない 実は、C 言語では、「文字」と小さな整数値(1 byte 内)は「ASCII 表」を通して、 (ほぼ)同じものとして扱われる putchar ( '0' ) と putchar ( 48 ) は同じもの ! コンピュータ「計算機」は、「計算」しかできない ! 「計算」(狭義では..)は「数」の処理を指す ! => 計算機は「数」しか扱えない ( 今でも、本質的には、そう.. ) ! なぜ、今のコンピュータは、数以外のものがあつかえるか ! => 「コーディング」(数といろいろな情報の対応表[Code 表]があり、 ! 「情報」を、この「Code 表」で、対応付ける事により、 ! 間接的に「情報」の処理ができているように見せている ! 情報を処理する時に、「どの表を使うか」は、予め決めおく必要がある ! => これが違っていると混乱することになる ! windows では、全角を表現するために SJIS (Shift JIS)を利用するが、 ! ubuntu では、UTF-8 を使う (ので、文字のコード表が異なる)ので注意 == [make と makefile] C 言語の話ではない C 言語でプログラムを作成するときに利用する便利ツール 「C 言語でプログラムを作成するときに利用するツール」として cc を知っている cc の使い方を覚えた ( cc -c でコンパイル、cc -o でリンク..) => C 言語で作成したプログラムを実行する事が可能となる C 言語で呼び出す関数は、どこかで定義されている必要がある printf は、実は(別の所で定義され..)標準ライブラリに収められているので、 cc が、リンク時に、その標準ライブラリの中から、取り出して利用している !! 標準ライブラリ内の関数は、定義せずに使えるので便利 !! cf. printf/putchar/getchar !! => C 言語の標準的な知識の一部は、標準ライブラリの APIを学ぶ事 もし、定義されていない関数を呼び出すと、リンク時にエラーになる C のプログラムを作るときに、 関数を利用する場合は、その関数が定義されている必要がある しかし、その定義は、同じプログラムファイルの中になくてよい cf. 標準ライブラリの中の printf は、定義しなくても使える そこで、 関数を定義する C ファイルと利用する C ファイルを別々して、 後で(リンク時)にまとめる事ができる => 分割コンパイル 例: p-006.c は一つのファイルの中に print_hello_to の定義と利用が一つにまとまっている => 一つのファイル一度コンパイルして、リンクしれば実行ファイルが作れる p-007.c と p-007-01.c は、p-006.c の関数定義と参照を別のファイルにした # 分割は、基本、関数単位(関数の一部だけを別のファイルにするわけではない..)に行う => それぞれの C ファイルをコンパイルして、リンク時にすべてのオブジェクトファイルを指定すればよい p-006.c => p-006.o => p-006.exe | +- p-007.c => p-007.o -+-> p-007.exe +- p-007-01.c => p-007-01.o -+ 分割コンパイルのメリット 関数定義を再利用できる => 一度定義した関数は、完成していれば、一度コンパイルするだけで、 それを他のプログラムでも利用できる !! 「ソフトウェア工学」 !! => プログラムの生産性を高める !! => プログラムの再利用 !! # 分割コンパイルは、そのための典型的な手法 デメリット: 実行ファイルを作る手間が増える # 「手間」は、「同じ」なら、我慢できるが、「個々に違うといや」 => 実行ファイルを作る個別の内容に関しては、 1. それを記録して、間違いないようにする Makefile 2. 1. を利用して、自動化する make Makefile が用意されていれば、 make とするだけで、自動的に必要なコマンドを実行してくれる [まとめ] 分割コンパイル : プログラムを複数のファイルに分けて記述し、 実行ファイルを作成する(リンク)時にまとめるという考え方 => プログラムファイルの再利用がやりやすい <= 作業内容が複雑になる => Makefile と make によって、その複雑さを回避し、さらに簡単になる !! 作業内容をドキュメント化し、さらに自動化をしている Hanoi 等の API が、いろいろな手順を要求する可能性があり、 その場合には、makefile と make が重要な役割を担う !! 資料の中に makefile があったら、とりあえず、 make する == ハノイ塔 大きさの違う円盤が何枚か(ここでは 3 枚)が、1の棒に小さい円盤が 大きい円盤の上にあるようにおかれている | | | *|* | | **|** | | ***|*** | | ======================== 1 2 3 目的 1 の三枚の円盤をすべて 2 に移動したい 規則 a. 円盤は一度に1枚しか移動できない b. 小さい円盤の上には、大きな円盤を乗せる事ができない どのようにすれば、すべての円盤を移動させる事ができるか ?
課題プログラム内の「/*名前:ここ*/」の部分を書き換え「/*この部分を完成させなさい*/」の部分にプログラムを追加して、プログラムを完成させます。
Download : 20190607-01.c
/* * 20190607-01-QQQQ.c * コマンドライン引数で指定した長さのハノイの塔を解く */ #include <stdio.h> #include <stdlib.h> /* exit を利用するために必要 */ /* * ハノイプログラムには、s_hanoi.h が必要 */ #include "s_hanoi.h" /* * hanoi ( char *f, char *t, char *w, char *size ) * 高さ size のハノイの塔を、 * f で表されている棒から * t で表されている棒へ * w で表されている棒を * 作業領域として利用して移動する * w は空っぽか、size より大きな円盤しかない */ void hanoi ( char *f, char *t, char *w, char *size ) { if ( !strcmp ( size, "" ) ) { /* 空っぽだったら*/ /* する事はない */ } else { /* まだ仕事があるなら */ /* やりたい事は、 「size の大きさの塔を f から t に動かしたい」事 そのためには、最終的に、size の大きさの円盤を動かす必要がある。 そのためには、 size の大きさの上の塔を t を利用して、一旦、f から w に移し size の大きさ円盤を f から t に移し size の大きさの上の塔を f を利用して、w から、t に動かせば良い */ /* f の所にある size の円盤の上の塔を w に移動して退かす (再帰呼出し) */ hanoi ( f, w, t, size + 1 ); /* f の所にある size の円盤を t に移動 */ /* ** この部分を完成させなさい */ /* size の円盤の上に w に退かした塔を載せる (再帰呼出し) */ /* ** この部分を完成させなさい */ } } /* * ハノイの塔 プログラム */ int main ( int argc, char *argv[] ) { /* コマンドライン引数を用いるので、 int main ( int argc, char *argv[] ) と、main 関数の引数を宣言する。 */ if ( argc < 1 ) { /* 引数が指定されていない */ printf ( "ハノイの塔の高さを指定する文字列を指定してください\n" ); /* エラーメッセージの出力 */ exit ( -1 ); /* エラーコード : -1 でプログラム終了 */ /* main 関数内であれば return -1; でも可 */ } /* else {} */ /* 「else {}」は省略可 */ /* * 最初は "1" に全ての円盤が置いてある * これを "2" に全ての円盤を移動する */ /* * ハノイで、できること * s_hanoi_init() : ハノイプログラムの開始 : 最初に一度だけ呼び出す * s_hanoi_size ( char *discs ) : ハノイの塔の高さを設定する * s_hanoi_move ( char *from, char *to ) : from にある円盤を to に移す * s_hanoi_clear () : 最初の状態に戻す * s_hanoi_stop() : ハノイ塔プログラムの終了 : return 0 の前に呼び出す */ s_hanoi_init(); /* ハノイの塔のプログラムの初期変化 */ /* コマンドライン引数の一つ目 (argv[1]) で、問題の高さを初期化 */ /* s_hanoi_size を呼び出す */ /* ** この部分を完成させなさい */ printf ( "これから解答を開始します。[Enter] キーを押してください\n" ); putchar ( '>' ); putchar ( getchar() ); /* 開始前に、一旦停止 */ /* 解答開始 */ hanoi ( "1", "2", "3", argv[1] ); /* 解答終了 */ printf ( "プログラムを終了するには [Enter] キーを押してください\n" ); putchar ( '>' ); /* 終了前に確認 */ putchar ( getchar() ); s_hanoi_stop(); return 0; }
$ ./20190607-01-QQQQ.exe $
Download : 20190607-02.c
/* * 20190607-02-QQQQ.c * 出力する繰返し回数を整数で指定する ntimeprint を作りなさい */ #include <stdio.h> /* * ntimeprint ( int times, char *message ) */ void ntimeprint ( int times, char *message ) { if ( times <= 0 ) { /* 非正なら、回数として不適切か、終わり */ /* 何もしないくてよい */ } else { /* 繰返し回数が正ならば.. */ printf ( message ); /* 少なくても一度は出力し.. */ /* ** この部分を完成させなさい */ } } /* * main */ int main ( void ) { printf ( "「Hello」を 3 回\n" ); ntimeprint ( 3, "Hello\n" ); printf ( "「ハ」を 30 回\n" ); ntimeprint ( 30, "ハ" ); printf ( "\n" ); return 0; }
$ ./20190607-02-QQQQ.exe 「Hello」を 3 回 Hello Hello Hello 「ハ」を 30 回 ハハハハハハハハハハハハハハハハハハハハハハハハハハハハハハ $
Download : 20190607-03.c
/* * 20190607-03-QQQQ.c * 負の整数も処理できる printint を作成しなさい * ただし、正の整数を出力する printpositiveintsub, printpositiveint を利用してよい */ #include <stdio.h> /* * printint */ void printint ( int n ) { if ( n < 0 ) { /* 負の時は.. */ /* ** この部分を完成させなさい */ } else { /* 正の時には.. */ printpositiveint ( n ); } } /* * printintln */ void printintln ( int n ) { printint ( n ); putchar ( '\n' ); } /* * main */ int main ( void ) { printf ( "123 の出力\n" ); printintln ( 123 ); printf ( "-123 の出力\n" ); printintln ( -123 ); printf ( "123 + 456 の出力\n" ); printintln ( 123 + 456 ); printf ( "123 - 456 の出力\n" ); printintln ( 123 - 456 ); return 0; }
$ ./20190607-03-QQQQ.exe $