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