14: 発展事項 C プログラミング入門 基幹2 (月4) Linux にログインし、以下の講義ページ を開いておくこと http://www-it.sci.waseda.ac.jp/ teachers/w483692/CPR1/ 2014-07-14
これまでの講義で説明していない事項について簡単に紹介する 今日の内容 これまでの講義で説明していない事項について簡単に紹介する 静的変数 (static) const 変数 プリプロセッサ・ディレクティブ マクロ (ファイルの読み込み) (数学関数のエラーチェック) 文法 (関数ポインタ) 標準入出力ファイル (可変長引数) 異常終了 プログラミング技術 短絡評価 モジュールプログラミング 文字定数の型 外部リンケージ キャスト アサーション 変数の宣言位置 グローバル変数 2014-07-14 C プログラミング入門 基幹2 (月4)
文法 2014-07-14 C プログラミング入門 基幹2 (月4)
標準入出力 標準入力、標準出力はファイルの一種 標準エラー出力というのもある それぞれ stdin, stdout という FILE ポインタがグローバル変数で定義されている たとえば printf("Hello!"); は fprintf(stdout, "Hello!); と同じ 標準エラー出力というのもある fprintf(stderr, "Memory error\n"); 標準出力とは区別されていて、エラーを知らせる時に使う。標準出力がリダイレクトされてもエラーは画面に出る <stddef.h> で定義されているが、<stdio.h> などで読み込まれている 2014-07-14 C プログラミング入門 基幹2 (月4)
main() から return で終了した場合を正常終了という 正常終了・異常終了 main() から return で終了した場合を正常終了という 標準ライブラリ関数 exit() を使っても同じ 終了時に自動的に呼ばれる関数を atexit() で登録することができる OS による検出などで強制的に終了することを異常終了という プログラムから意図的に行うには abort() を用いる シグナルをトラップして処理できる(詳細略) 2014-07-14 C プログラミング入門 基幹2 (月4)
短絡評価 論理演算子の最初の式から全体の値が決まる場合、2つ目の式を評価しない たとえば、以下のような場合に意味を持つ (式1) && (式2) : (式1) が偽の場合、(式2) を評価しない (式1) || (式2) : (式1) が真の場合、(式2) を評価しない たとえば、以下のような場合に意味を持つ if(p != NULL && p[0] == 3) … もし p が null ポインタの場合、 p[0] は評価できないが、 短絡評価されるので問題ない 式全体として偽となる 式全体として真となる もちろん、 if(p != NULL) { if(p[0] == 3) ... } と書いてもよい 2014-07-14 C プログラミング入門 基幹2 (月4)
文字定数 (文字リテラル) は char 型ではなく、 int 型である 文字定数の型 文字定数 (文字リテラル) は char 型ではなく、 int 型である 例: 'A' は ASCII コードでは int の 65 標準ライブラリ関数で文字を扱う関数では、文字を指定するのに int 型となっている 例: 指定した文字 ch を文字列 str から見つける char *strchr(const char *str, int ch); C++ では、文字リテラルは char 型である 2014-07-14 C プログラミング入門 基幹2 (月4)
キャスト 数値を任意の型に強制的に変換する キャストの例 キャストはバグの温床なので多用しない 変換されるものに対して挙動が異なる 構文: (型)値 キャストの例 (int)4.5 4.5 を 4 に切り捨てる (int *)p ポインタの型を変える const 型を非 const 型に変更することもできる キャストはバグの温床なので多用しない () はいろいろなところで使われるので検索で見つけづらい 演算子の一種 const を使う意義がなくなるので、やるべきではない 2014-07-14 C プログラミング入門 基幹2 (月4)
C89 まではローカル変数は必ずブロックの先頭で宣言する必要がある ローカル変数の宣言位置 C89 まではローカル変数は必ずブロックの先頭で宣言する必要がある C99 以降はどこでも可能になった GCC は標準で許容 C++ の文法を導入したもの 使用する直前で宣言するほうがコードが読みやすい { int i, j, k; // int sum = 0; ... 長いプログラム int sum = 0; for(... sum += s[i]; } ここで定義するよりは… ここでは sum は全く使われない ここで定義すれば、ここから始めて使われることがわかる 2014-07-14 C プログラミング入門 基幹2 (月4)
グローバル変数 関数の外で宣言される変数 ソース全体の定数などに利用 宣言位置以降どこでも可視 初期化がない場合、 0 に初期化される #include <stdio.h> const int PI = 3.1415; int count; int func1(void) { ... } int main(void) const を付けて、定数として使う 実行開始時に 0 に初期化される デフォルトで外部リンケージを持つため、ほかのプログラムとの共有をしない場合は static を付ける。詳細略 2014-07-14 C プログラミング入門 基幹2 (月4)
関数の変数宣言に static を付けるとグローバル変数と同様にプログラムの開始時から終了まで生存する変数となる 書かなくても 0 に初期化される 関数の変数宣言に static を付けるとグローバル変数と同様にプログラムの開始時から終了まで生存する変数となる スコープはその関数のみ 初期化を指定しない場合 0 に初期化される #include <stdio.h> int count(void) { static int n = 0; ++n; return n; } int main(void) printf("%d\n", count()); ... 呼び出すたびに 1 増える グローバル変数に対する static とは効果が異なる 2014-07-14 C プログラミング入門 基幹2 (月4)
変数とポインタと動的メモリ確保の整理 再掲 2014-06-23 C プログラミング入門 基幹2 (月4) 分類 生存期間 スコープ メモリ領域 初期化 自動変数 (ローカル変数) 定義位置からブロック終端まで スタック 初期化が指定されている場合のみ、ブロックに入るたびに初期化される 大域変数 (グローバル変数) プログラムの実行開始から終了まで 定義位置からプログラム終了まで 静的領域 プログラム開始時に1度だけ。初期化が指定されない場合、0 で初期化される 静的変数 (static 変数) 同上 動的メモリ 確保から解放まで ヒープ malloc() はされない calloc() は 0 を書きこむ 2014-06-23 C プログラミング入門 基幹2 (月4)
# で始まるものは、プログラムのコンパイル前にプリプロセッサというソフトで処理される。これをディレクティブという プリプロセッサとディレクティブ # で始まるものは、プログラムのコンパイル前にプリプロセッサというソフトで処理される。これをディレクティブという #include によるヘッダファイルの読み込みや #define のマクロなどはこれの一種 #if, #ifdef, #ifndef, #endif, #define, #undef, #line などがある 詳細は、リファレンスサイトで調べてください。 2014-07-14 C プログラミング入門 基幹2 (月4)
多くの場合マクロを使わず、グローバル変数を書くべき 主に定数を書く際に用いられる 通常のマクロと、関数型のマクロがある どちらも #define ディレクティブで定義する 例 #define MVAL 100 ソースコード中のすべての MVAL を、文字列 100 で置き換える 多くの場合マクロを使わず、グローバル変数を書くべき static const int MVAL = 100; 間に = を書かない 最後にセミコロンは付けない 関数の外で書く 値の型が必要 2014-07-14 C プログラミング入門 基幹2 (月4)
標準ライブラリでは、定数の多くがマクロで定義されている 例 RAND_MAX 乱数の最大値 EXIT_SUCCESS, EXIT_FAILURE return の後に 0 や 1 を書く代わりに使う ERANGE 数学関数のエラーコード EOF ファイルの終端 2014-07-14 C プログラミング入門 基幹2 (月4)
関数型マクロは、関数の様に引数を与えることができる 特に理由がない限り、普通の関数を使うべき 標準ライブラリ関数のいくつかはマクロである 例 #define xy2idx(x,y,w) ((x)+(y)*(w)) いつも決まった式を書く場合に便利 単純なソースコード上の文字列の置き換えになるので、解釈がおかしくならないように、引数それぞれと、式全体に括弧を付ける 2014-07-14 C プログラミング入門 基幹2 (月4)
関数型マクロ 標準ライブラリの関数型マクロの例 getc(fp) fgetc(fp) と等価 assert(exp) exp が偽であれば異常終了する 2014-07-14 C プログラミング入門 基幹2 (月4)
プログラミング技術 2014-07-14 C プログラミング入門 基幹2 (月4)
関数の引数がポインタであり、それを通して値を変更しない場合は常に const を付ける(説明済み) コンパイラによる最適化(高速化)が期待できる 2014-07-14 C プログラミング入門 基幹2 (月4)
アサーション(表明) (assertion) コメントで書くより効果的 ソースコード中で、常に成り立つ条件をコードで表す assert マクロを使う 条件を満たさない場合、異常終了する 常に条件が満たされるようにコードを書く #include <stdio.h> #include <assert.h> // n 要素の int 配列 a の総和 int sum(const int *a, int n) { int s = 0, i; assert(n > 0); assert(a != NULL); for(i = 0; i < n; ++i) s += a[i]; } return s; 条件を満たさない限り以降の処理が正しく行われないので、絶対に進ませない プログラムのバグがない ⇒ 全てのアサーションが真 2014-07-14 C プログラミング入門 基幹2 (月4)
条件判断には計算時間がかかるため、リリース製品のような場合には無効化する アサーションの無効化 条件判断には計算時間がかかるため、リリース製品のような場合には無効化する NDEBUG マクロを定義すると、条件式が空行に置き代わる gcc では、コンパイル時に -DNDEBUG オプションを付ける 2014-07-14 C プログラミング入門 基幹2 (月4)
バグのないプログラム⇒全てのアサーションが真 アサーションの考え方 バグのないプログラム⇒全てのアサーションが真 プログラマは、アサーションに違反しないように関数を呼び出すことを強制される(契約) アサーションは、多ければ多いほど良い 一度書いたアサーションは基本的に消さない 詳細は、以下のキーワードで調べてください 事前条件・事後条件・不変条件 ホーア論理 (Hoare logic) 契約プログラミング (Programming (Design) by Contract) 2014-07-14 C プログラミング入門 基幹2 (月4)
コンパイルとリンク コンパイル (compile) リンク (link) ソースコードファイル オブジェクトファイル myprog1.c savePGM() drawLine() func1() main() myprog1.o 実行可能形式 (executable) myprog1 標準ライブラリ (libc.so) printf() malloc() ... それぞれを行うソフトウェアを、コンパイラ (compiler), リンカ (linker) という gcc は、これらを総合して実行するコンパイラドライバである 使用するコンパイラに付属するライブラリ 2014-07-14 C プログラミング入門 基幹2 (月4)
いくつかのプログラムで共通の処理を、ソースコードから分離する モジュールプログラミング いくつかのプログラムで共通の処理を、ソースコードから分離する myprog1.c savePGM() drawLine() func1() main() myprog2.c savePGM() loadPGM() func2() main() myprog3.c savePGM() loadPGM() drawLine() main() 同じ関数が複数のプログラムに存在する pgm_module.c savePGM() loadPGM() drawLine() 共通の処理を抜き出す 2014-07-14 C プログラミング入門 基幹2 (月4)
モジュールプログラミング コンパイル リンク pgm_module.c savePGM() loadPGM() drawLine() pmg_module.o モジュール myprog1.c func1() main() myprog1 myprog1.o 標準ライブラリ 2014-07-14 C プログラミング入門 基幹2 (月4)
それぞれのソースをコンパイルして、オブジェクトファイルを生成 モジュール分けしたプログラムのビルド それぞれのソースをコンパイルして、オブジェクトファイルを生成 gcc -c -o pgm_module.o pgm_module.c –Wall –Wextra gcc -c -o myprog1.o myprog.c –Wall –Wextra リンク gcc -o myprog1 myprog.o pgm_module.o –Wall –Wextra 2014-07-14 C プログラミング入門 基幹2 (月4)
外部リンケージ コンパイル リンク pgm_module.c savePGM() loadPGM() drawLine() リンクの対象となる公開された関数やグローバル変数は外部リンケージを持つという 関数やグローバル変数は普通は外部リンケージを持つ pmg_module.o 公開される関数 savePGM loadPGM drawLine myprog1.c static int func1(void); main() myprog1 myprog1.o 公開される関数 main 複数のオブジェクトファイルが同じ名前の関数や変数を公開している場合、リンクの結果は未定義である。したがって、最小限のものだけを公開する必要がある 他のモジュールから使われることのない関数, グローバル変数には static を付ける 標準ライブラリ 2014-07-14 C プログラミング入門 基幹2 (月4)
モジュール分けするために以下のことをする モジュールの作成 モジュール分けするために以下のことをする モジュールで公開しない関数に static を付ける モジュールで公開しないグローバル変数に static を付ける モジュールのヘッダファイルとして、公開する関数のプロトタイプ、公開するグローバル変数の extern 宣言を書く ヘッダファイルにインクルードガードを書く モジュールで自分のヘッダをインクルードする 詳細は、書籍などを参照 2014-07-14 C プログラミング入門 基幹2 (月4)