/* * 20200612-01-QQQQ.c * 与えられた文字列の文字を二度ずつ出力する関数を作成する */ #include <stdio.h> #include <string.h> /* * double_print * 与えられた文字列(message)の文字を二度ずつ出力する */ void double_print ( char *message ) { if ( !strcmp ( message, "" ) ) { /* 空文字列だった */ /* なにもする必要はない */ } else { putchar ( *message ); /* 取り敢えず、一つ分は出す */ putchar ( *message ); /* 次に、二つ分を出す */ double_print ( message + 1 ); /* 再帰呼出しをする */ } } /* * main */ int main ( void ) { double_print ( "abc" ); printf ( "\n" ); double_print ( "1234567" ); printf ( "\n" ); return 0; }
/* * 20200612-02-QQQQ.c * 与えられた文字列の文字を逆順に出力する関数を作る */ #include <stdio.h> #include <string.h> /* * reverse_print * 与えられた文字列(message)の文字を逆順に出力する */ void reverse_print ( char *message ) { if ( !strcmp ( message, "" ) ) { /* 空文字列だった */ /* なにもする必要はない */ } else { /* ここで再帰呼出しを行うのだが... */ reverse_print ( message + 1 ); putchar ( *message ); } } /* * main */ int main ( void ) { reverse_print ( "abc" ); printf ( "\n" ); reverse_print ( "1234567" ); printf ( "\n" ); return 0; } /* "abc" => "abc" "abc" 'a' + "bc" putchar 再帰 => myprint の実装 g( "abc" ) => "abc" 'a' + "bc" putchar ( 'a' ) + g ( "bc" ); putchar ( *"abc" ) + g ( "abc" + 1 ); "abc" => "cba" "cba" "cb" + 'a' 再帰 putchar f( "abc" ) => "cba" "cb" + 'a' f ( "bc" ) + putchar ( 'a' ) f ( "abc" + 1 ) + putchar ( *"abc" ) */
#include <stdio.h> int main(void) { printf ( "Hello, World\n" ); return 0; }
#include <stdio.h> int main(void) { putchar ( 'a' ); /* 画面に「文字」「a」を表示する */ return 0; }
#include <stdio.h> int main(void) { printf ( "文字を出力する場合は、putchar 関数を使う\n" ); printf ( "ここに :「" ); putchar ( 'a' ); /* 画面に「文字」「a」を表示する */ printf ( "」を表示する\n" ); return 0; }
#include <stdio.h> int main(void) { putchar ( 'a' ); /* 画面に「文字」「a」を表示する */ putchar ( '\n' ); /* 画面に「文字」「改行」を表示する */ /* 結果的に、画面に「a」と「改行」が表示されるので */ /* printf ( "a\n" ); と同じ結果になる */ return 0; }
#include <stdio.h> int main(void) { printf ( "キーボードから、一文字入力し、さらに [Enter]キーを押してください 「" ); putchar ( getchar() ); /* キーボードから一文字入力し、その文字をそのまま出力する */ /* getchar() は、[Enter] キー(改行キー)を押すまで、待っている */ printf ( "」\n" ); return 0; }
#include <stdio.h> int main(void) { printf ( "キーボードから、文字列を入力し、さらに [Enter]キーを押してください \n" ); putchar ( getchar() ); putchar ( getchar() ); putchar ( getchar() ); printf ( "\n" ); return 0; }
#include <stdio.h> int main(void) { printf ( "キーボードから、文字を入力し、さらに [Enter]キーを押してください \n" ); putchar ( getchar() + 1 ); /* 入力された『文字』の次の『文字』が表示される */ printf ( "\n" ); return 0; }
#include <stdio.h> int main(void) { printf ( "キーボードから、文字を入力し、さらに [Enter]キーを押してください \n" ); putchar ( getchar() - 1 ); /* 入力された『文字』の前の『文字』が表示される */ printf ( "\n" ); return 0; }
#include <stdio.h> int main(void) { putchar ( *"abc" ); /* 画面に「文字」「a」を表示する */ /* 『文字列』「"abc"」に「*」つけてると、 『文字列』の先頭の『文字』である 「'a'」が得られる */ return 0; }
#include <stdio.h> int main(void) { putchar ( *"abc" ); /* 先頭の文字「a」 */ putchar ( * ( "abc" + 1 ) ); /* 二番目の文字「b」 */ putchar ( * ( "abc" + 2 ) ); /* 二番目の文字「c」 */ /* "abc" + 2 => ( "abc" + 1 ) + 1 */ /* => "bc" + 1 */ /* => "c" */ printf ( "\n" ); return 0; }
#include <stdio.h> int main(void) { putchar ( "abc"[0] ); /* 先頭(0番目)の文字「a」 */ putchar ( "abc"[1] ); /* 二番目の文字「b」 */ putchar ( "abc"[2] ); /* 三番目の文字「c」 */ /* "abc"[2] <-> *( "abc" + 2 ) <-> * ( "c" ) <-> 'c' */ printf ( "\n" ); return 0; }
#include <stdio.h> #include <string.h> /* * myprintf ( char *string ); * 引数で指定された『文字列』を表示する */ void myprintf ( char *string ) { /* string が空文字列の時とそうでない時に分ける */ if ( !strcmp ( string, "" ) ) { /* 空文字列の時は何もしない */ } else { putchar ( *string ); /* 先頭の『文字』を出力 */ myprintf ( string + 1 ); /* 残りの『文字列』を出力 (再帰) */ } } int main(void) { myprintf ( "Hello, World\n" ); return 0; }
#include <stdio.h> #include <string.h> /* * myprintf ( char *string ); * 引数で指定された『文字列』を表示する */ void myprintf ( char *string ) { /* string が空文字列の時とそうでない時に分ける */ if ( *string == '\0' ) { /* 『文字列』の先頭の『文字』が「null 文字('\0')」に等しい */ /* => 『文字列』が「空文字列("")」である事と同じ */ /* C 言語で「同じ値」を意味する等式記号は「==」を使う */ /* => 「!strcmp ( string, "" )」と同じ */ /* 空文字列の時は何もしない */ } else { putchar ( *string ); /* 先頭の『文字』を出力 */ myprintf ( string + 1 ); /* 残りの『文字列』を出力 (再帰) */ } } int main(void) { myprintf ( "Hello, World\n" ); return 0; }
前回(2020/06/05)の内容 複雑な数学パズルを、再帰を用いて解決する 順接/条件分岐/再帰 => 万能性 原理的に解く事ができるものは、解ける 解くプログラムを作成する事ができる 再帰的に問題を解くパズル ハノイの塔 高さ N のハノイの塔の問題を解くには 高さ N-1 のハノイ塔を移動し サイズ N の円盤を移動 高さ N-1 のハノイ塔を移動 => 問題をより簡単な問題に分割 再帰 => 数学的に「正しく問題を解く」事が証明できる プログラムが動いたから OK だけでなく => ある特殊な条件ではうまく行く事が保証される 作成されたプログラムが、「任意の場合」でもうまくゆく事を 証明する事ができる => 数学の能力が要求される 砂漠の旅人 「数学」と「情報」の関係 プログラムを作成して問題を解く 「アルゴリズム」:問題を解くための手順 数学的な「ものの考え方」 問題を解く鍵(アルゴリズム)になる 数学の概念でアルゴリズムにつながる考え方 計算 色々モデル(数学概念:線形空間/収束/分類/帰納法/背理法/etc..) C 言語における『文字』 cf. 『文字列』: C 言語の表現上の「文字列」を『文字列』であらわす 「"」で挟さまれた「文字」の並びで表現 例 : 「"abc"」は、三つの「文字」「a」,「b」,「c」からなる「文字列」を表す => 『文字列』は、長さをもっており、この例では長さ 3 になる 『文字』: C 言語の表現上の「文字」 「'」で挟まれた、一つの「文字」で表現 例 : 「'a'」は、一つの「文字」「a」を表す 『文字』は、長さはない ( 常に 1 と考えてもよいが、意味がない ) 注意 : 「"a"」は、長さ 1 の『文字列』で、「'a'」は『文字』なので、異なるもの C 言語では、『文字』と『文字列』は、まったく違うもの 文字の入力と出力 『文字』の出力 cf. 『文字列』の出力 printf 関数を使う 例: printf ( "abc\n" ); => 画面に「abc」と改行が表示される エスケープシーケンス : 「\n」は、 「改行」一文字を表す putchar ( 'a' ); => 画面に対して、「文字」「a」を出力する # 同じ「出力(画面への表示)」機能 # 出力対象(『文字』と『文字列』)が異なる # => 異なる関数(putchar,printf)を利用する必要がある 文字の表現 文字は「'」で挟む (cf. 「文字列」は「"」で挟む) 当分は、半角のみ、日本語の「文字」は扱わない 半角 : 英数、大文字小文字、簡単な記号 => ASCII Code 表の対象 # 実は、C 言語では、「全角の文字」(日本語の漢字)も扱える # 色々な制約 : この講義では扱ない # !! 文字コードによって結果異なる # !! Windows は SJIS / Ubuntu は UTF-8 # !! => Windows と Ubuntu で結果が違う ## 「全角文字を含む文字列」は「表示目的」で利用する 文字の出力 putchar( 文字 ); を使う 「putchar ( 'a' );」 で文字('a')が出力される 改行文字は '\n' で表す : putchar ( '\n' ) で改行する エスケープシーケンスは、「文字」の表現にも利用される 「'\n'」は、「一文字の改行」を表す『文字』を表している 文字の「入力」 「出力」=> 画面になにを表示する機能 「入力」=> キーボードからキーを押して、そのキーに対応する『文字』を プログラム内で扱えるようにする機能 「getchar()」とすると、キーボードからの入力を待つ キーボードからキーを押して、さらに[Enter]キーを押すと そのキーに対応する『文字』とさらに'\n' が入力される [プログラム] printf ( "キーボードから、一文字入力し、さらに [Enter]キーを押してください 「" ); ^^^^^^^^^^^^^^^^^^^^^^^^^^(1)^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ putchar ( getchar() ); ========= キーボーから入力 : 入力内容がエコーバック ~ printf ( "」\n" ); ++++++++++++++++++ [出力] キーボードから、一文字入力し、さらに [Enter]キーを押してください 「a ^^^^^^^^^^^^^^^^^^^(1)^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^= = : 「a」と「改行」が表示 : キーボードからの入力 ('a'+[Enter]) a」 ~ : 「a」という文字が表示されている : getchar() で入力された『文字』を出力 ++ : 閉じかっこと改行が表示 getchar() によって、「プログラムの実行時」に、 キーボードから入力された『文字』をプログラム内で利用できる [プログラム] printf ( "キーボードから、文字列を入力し、さらに [Enter]キーを押してください \n" ); ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ それぞれ、一文字入力し、出力をする putchar ( getchar() ); ここで、初めて入力待ちになり、 キーボードから「"abc\n"」が入力される 最初の文字「'a'」が getchar でとりだされ、出力 => 'a' のみ出力 putchar ( getchar() ); getchar() が呼ばれて、「入力」が要求されるが、 すでに、"abc\n" が入力済で、かつ、そのうちの 先頭の一文字「'a'」は、一つ前の getchar でとりだされているので、 ここでは、次の「'b'」が取り出され、それが表示される => 'b' のみ出力 putchar ( getchar() ); => 'c' のみ出力 !!! ここで、まだ、「改行」が残ったまま、食い残しが残っている printf ( "\n" ); => 改行 [出力] キーボードから、文字列を入力し、さらに [Enter]キーを押してください ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ abc ==== : 文字列「"abc\n"」: 改行も含め 4 文字 abc 「putchar ( getchar() );」とすると、入力した文字が出力される 文字の計算 cf. 『文字列』には +1 する事ができた => 先頭の一文字が失われ、長さが 1 短い文字列になる 例 : "abc" + 1 => "bc" 『文字』 + 1 => 次の『文字』になる 'a' + 1 => 'b' 'A' + 1 => 'B' '1' + 1 => '2' !!! '1' + '1' は '2' にならない !!! 1 + 1 も '2' ではない ( 2 ) !!! 「1」と「'1'」は違う物 !!! 「数値」と「数字」 !!! 「数字」 => '0' ? '9' !!! 「数値」 => 無限に存在する じゃ、 'z' + 1 '9' + 1 は... ? 『文字』の「次」は 「ASCII コード表順」の次になるので 'z' + 1 => '{' となる。 逆に、『文字』に「-1」する事により(ASCII コード表順の) 前の『文字』になる == C 言語では、『文字』が扱える プログラム中で表現ができる : 'a' で、「文字」「a」を表す事ができる 出力ができる : puthar ( 'a' ) で「a」が画面に表示される 入力ができる : getchar() でキーボードから「文字」が入力できる 計算ができる : +1 で次の『文字』、-1 で前の『文字』になる !!! 『文字』と『文字列』は別物 ( "a" と 'a' ) 『文字』と『文字列』の関係 『文字列』「"abc"」は、三つの「文字」「a」,「b」,「c」からなる => 長さは 3 になる 「文字」「a」は、C 言語では、「'a'」なので、この関係がどうなっているか 『文字列』から、その一部である『文字』を取り出す方法 # 『文字列』を『文字』に分解する方法 # !!! 分解 => 合成もあるが、二周目 「*」演算子 ( ポインター剥ぎ / 間接参照演算子 ) 『文字列』の先頭に「*」を付けると、「先頭の『文字』」が取り出せる 例 : *"abc" => 'a' => 先頭以外の文字は ?? 『文字列』「"abc"」の二文字目: 『文字』「'b'」 !!! 『文字列』の計算 !!! "abc" +1 => "bc" => * ( "abc" + 1 ) 「[]」演算子 *( X + i ) <-> X[i] X : 何か ( 文字列 ) i : 整数値 例: *"abc" <-> *( "abc" + 0 ) <-> "abc"[0] <-> 'a' *( "abc" + 1 ) <-> "abc"[1] <-> 'b' *( "abc" + 2 ) <-> "abc"[2] <-> 'c' 「空文字列」へ「*」をつけるとどうなるか ? => 結果は、「null 文字」「'\0'」になる *"" => '\0' !!! 空文字列は「""」で、「null 文字」は「'\0'」の事 !!! => 全然違う イメージとして、 "abc" は、{ 'a', 'b', 'c', '\0' }となるもの !!! 『文字列』の最後には必ず「null 文字」がいる !!! => 「null 文字」が『文字列』の終わりを表す
課題プログラム内の「/*名前:ここ*/」の部分を書き換え「/*この部分を完成させなさい*/」の部分にプログラムを追加して、プログラムを完成させます。
Download : 20200612-01.c
/* * 20200612-01-QQQQ.c * 与えられた文字列の文字を二度ずつ出力する関数を作成する */ #include <stdio.h> #include <strings.h> /* * double_print * 与えられた文字列(message)の文字を二度ずつ出力する */ void double_print ( char *message ) { if ( !strcmp ( message, "" ) ) { /* 空文字列だった */ /* なにもする必要はない */ } else { putchar ( *message ); /* 取り敢えず、一つ分は出す */ /* ** この部分を完成させなさい */ double_print ( message + 1 ); /* 再帰呼出しをする */ } } /* * main */ int main ( void ) { double_print ( "abc" ); printf ( "\n" ); double_print ( "1234567" ); printf ( "\n" ); return 0; }
$ ./20200612-01-QQQQ.exe aabbcc 11223344556677 $
Download : 20200612-02.c
/* * 20200612-02-QQQQ.c * 与えられた文字列の文字を逆順に出力する関数を作る */ #include <stdio.h> #include <strings.h> /* * reverse_print * 与えられた文字列(message)の文字を逆順に出力する */ void reverse_print ( char *message ) { if ( !strcmp ( message, "" ) ) { /* 空文字列だった */ /* なにもする必要はない */ } else { /* ここで再帰呼出しを行うのだが... */ /* ** この部分を完成させなさい */ } } /* * main */ int main ( void ) { reverse_print ( "abc" ); printf ( "\n" ); reverse_print ( "1234567" ); printf ( "\n" ); return 0; }
$ ./20200612-02-QQQQ.exe cba 7654321 $