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