前回(2020/06/12)の内容 文字型 (小さな整数 : ASCII Code) 「文字」の入出力と操作 / 文字列と文字の関係 一周目のまとめ C 言語における『文字』 『文字』の C 言語上での表現(リテラル) 二つの「'」で挟まれた文字 ( [とりあえず] ASCII コード表の文字 ) 'a' => C 言語では、「a」という「文字」を表す表現 『文字』は、「小さな整数値」に対応 ( ASCII コード表の数値 ) コーディングされている 全角文字の「漢」という文字はどうやって扱うか ? => この講義では考えない 『文字』の順序 : (ASCII コード表によって) 対応する整数値の順序 'A' と 'B' では、 'A' が 'B' より小さい 'A' の一つ次が 'B' となる事が 'A' に対応している ASCII コード (小さな整数値) が 65 'B' に対応している ASCII コード (小さな整数値) が 66 に基づいて、説明される 『文字』の計算 : (ASCII コード表によって) 対応する整数値の計算 'A' + 1 => 65 + 1 => 66 => 'B' 『文字』の C 言語上での入出力 getchar() : 『文字』の入力 putchar() : 『文字』の出力 『文字』と『文字列』の関係 『文字列』は、『文字』の並び 例 : "abc" => 「a」と「b」と「c」という三つ文字からなる 'a', 'b', 'c', '\0' (EOS : End Of String : null 文字) EOS は『文字列』の「終わり」を表す『文字』として扱う EOS は普通は扱わない文字なので、これに対し、特別な意味を持たせている cf. 「\」も、C 言語では「特別な意味(エスケープシーケンスの開始)」 「\」は、普段も利用するので、「\」を'\\'で表現 EOS は、'\0' 複数 ( 0 個以上 ) の『文字』と、最後に '\0' (EOS : End Of String) からなる "abc" <-> { 'a', 'b', 'c', '\0' } 『文字』が『文字列』の一部であるなら、一部を取り出したい 『文字列』の前に「*」を付けると、『文字列』の先頭の『文字』が取り出せる 『文字列』[整数値] <-> *(『文字列』+整数値) 一周目の内容 表現 プログラムの基本的な書き方(Hello World) 幾つかのライブラリ関数の利用 : printf, putchar, strcmp 関数の作り方(特に、引数付き関数) 命令に名前をつける 引数を利用して、同じ関数で、様々な事を行う 引数の内容によって振る舞いを変える => if 構文 同じ内容を繰り返すために : 再帰呼び出し 幾つかのデータ型とその扱い(計算) 文字列 文字列の表現 (「"」で挟む) / 出力 (printf) / 計算 (+1 すると短くなる ) 文字 文字の表現 (「'」で挟む) / 出力 (putchar) / 計算 (+1 すると次の文字 ) 操作 コンパイル、リンクの仕方 サクラエディタを利用ソースコードプログラム (*.c) を作る cc -c ソースコードファイル(*.c) => コンパイル => オブジェクトファイル(*.o) が作られる cc -o 実行ファイル(*.exe) オブジェクトファイル(*.o) => リンク => 実行ファイル(*.exe)が作られる 実行ファイルの実行方法は、 ./実行ファイル => プログラムの実行結果が表示される 分割コンパイル 一つのプログラム(実行ファイル)を作成するために、 複数のソースコードファイルを作成し、 個々にコンパイル(して、リンク)する [事例] p-005.c 一つのファイルで、main と hello の二つの関数を定義 => 単独で、一つのプログラム ( p-005.exe ) が作れる p-006.c / p-006-01.c => p-005.c の内容を二つに分けた => p-006.exe を作るには、両方 (p-006.c / p-006-01.c)が必要 それぞれが異なるソースコードなので、 それぞれ別々にコンパイルが必要 => 分割コンパイル それぞれのオブジェクトファイル 例: cc -c p-006.c => p-006.o cc -c p-006-01.c => p-006-01.o さらに実行ファイルを作るときには、 すべてのオブジェクトファイルを指定する必要がある 例: cc -o p-006.exe p-006.o p-006-01.o 分割コンパイルの利点 C 言語のプログラムでは main 関数が一つ必要 もし、main 関数の中で他の関数を利用している場合は、 その関数の定義が必要 関数の定義を使いまわしたい場合にどうするか ? ProgA : mainA + func => ProgA.c ProgB : mainB + func => ProgB.c func の定義が複数の場所に現れるので厄介 分割コンパイル ソースコードの分割 mainA.c => mainA mainB.c => mainB func.c => func リンクで ProgA.exe : mainA.o func.o ProgB.exe : mainB.o func.o => func の定義 (func.c) が共有できる make / makefile makefile の記述に従ってコマンドを実行するコマンドが make 分割コンパイル => 作業が増える 作業内容を makefile に記述し、make で作業を行う 1. 分割コンパイルによる作業の増加を軽減できる 2. コンパイル/リンク作業を記録して再利用できる 作成 : プログラムを構築する考え方 分割統治をおこなう ( 問題を複数の小さな副問題に分割する ) => 問題をどのように分割するか ? ( => 数学 ) 命令を組合せる三つの表現(この三つで万能になる) 順接 : 命令を並べると、その順に実行される 条件分岐 : if 構文を使い、条件によって二つの命令の一方だけを実行する 再帰呼出し : 関数内で自分を呼び出す事により、間接的に命令を繰り返す <数学:一般の問題> 問題 Q => 複数の副問題 (Q1,Q2,..,Qn)に分割する それぞれ、解決する 解決 A <= A1, A2, .., An <情報:プログラムの作成> 問題 Q => 複数の副問題 (Q1,Q2,..,Qn)に分割する それぞれ、解決するプログラム(ソースコード) 解決 P <= P1, P2, .., Pn 再利用 : ライブラリとサンプルの利用 再利用によるソフトウェア作成 スクラッチ(何もない所)から、作るのは効率が悪い 「在るモノ」は利用しよう (「車輪の再発明」は良くない) 創造性の原理 : 「在るモノ」は作るな 差分プログラミング : サンプルプログラムの利用 既に「*正しく* 動く」事が解っているプログラムを変更する 「動きが変に成った」なら、「最後に変更した所が変(元に戻してみよ)」 少しずつ、「作っては試す」を繰り返す (一度に完成しようとしない) 分割コンパイル : プログラムを複数のファイルに分割して実現する ヘッダーファイル (*.h) の利用 コンパイル作業の軽減化 : Makefile と make を利用 [二周目] 「Hello, World」再び サクラエディタ(テキストエディタ)で、ソースコードファイル(*.c)を作成 最も単純なプログラム : Hello, World 「完全」で、「解り易く」、「十分に役に立つ」プログラム 「完全」: これ単独で、「プログラム」になっている 「誤り」を含んでない 「解り易く」: Hello, World の振る舞いが(なんとなく..)予想できる プログラム作成の「スタートポイント」 新しいプログラムを作成する場合 スクラッチ(最初)から作るより、 Hello, World を叩き台(出発点)として利用する 差分プログラミング いまある(完全なプログラム)に対し、 変更(差分を加える)ことにより、新しいプログラム作成する まず、「動く」事の大切さ 人間は「誤りを犯す」生物 : 変更部分に誤り プログラムの『骨格』の理解 Hello World 先頭に、#include がある main 関数が必ず含まれる main 関数の頭の部分が 「int main(void)」 main 関数の本体が「{」から「}」まで main 関数の最後の命令は「return 0;」である 他の言語の学習でも役立つ その言語での「Hello, World」がわかれば その言語は、(調べるだけで..) 利用できる 「Hello, World」の考え方(旧) 「{」〜「}」の中にある「printf ( "Hello, World\n" );」だけに注目 この部分を書き換えれば良い 残りの部分は「御呪い」とすれば良い 「Hello, World」の謎 御呪い:「#include」/「int」/「return 0;」は何をしている ?? 少し、「謎」を解いてみる 遺伝子ノックアウト その「遺伝子」の役割が解らなければ、それを壊して、何が起きるか見る 「働かなくなった機能」があれば、「それが、壊した遺伝子の機能」に関係する プログラム・ノックアウト 動くプログラム(Hello, World)から、一部分を取り除いた(ノックアウト)したら..? ノックアウト 1 printf() .. メッセージがでなくなった printf がメッセージ出力の役割を担っている ノックアウト 2 コンパイルは OK そもそもエラーになる部分がないので、当然 リンクでエラー => 「main 関数定義がない」 undefined reference to `main' main という定義されていない関数への参照がある # main を使いたいのに定義されていない C のプログラムには、 必ず、「main 関数の定義」が含まれている必要がある ノックアウト 3 「#include」.. コンパイル時に「警告」 warning: implicit declaration of function ‘printf’ 関数 printf の宣言が「暗黙宣言」である # printf は、すでに別な所で定義されている # それを再利用している # 自分で定義した関数を同じ # => それがどんな関数かという情報無い <= printf の宣言が無い => extern 宣言を入れれば、warnig が消せる #include => /usr/include/stdio.h をここに取り込む(include)という命令 ノックアウト 4 : 「#include」と printf .. 1 と同じ #include は printf を利用する場合に、 printf の宣言 ( /usr/include/stdio.h にある )が必要なので ノックアウト 5 : 「void」にして「return 0;」を削る .. 問題ない main の前 int と「return 0;」が関係する => 来週説明 大雑把なまとめ main は必要 : 最も短いプログラム 「#include」 : extern 宣言と関係するらしい.. 「return 0;」/「int」: 「void」に交換すると良いらしい.. 「printf」: まだ、謎があるようだ(変な extern 宣言) 謎の解明 幾つかの謎は解けたが、まだ解明されない謎や、新しい謎が...