第 6 章 C 言語入門
6.21 落ち穂拾い
6.21.3 実行時エラー
Cで作成したプログラムなどを実行する際に, Segmentation fault
Bus error
Floating exception
などというエラーが発生することがある.
6.21.3.1 Segmentation fault
Segmentation fault というエラーは,以下のように割り当てられていないメモリ領域に対するアクセ
スがあった場合に,「アクセス違反」として発生する.
/* Segmentation fault を起こす
* 意味:非割り当てメモリへのアクセス.
*/
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char **argv) {
struct data { char x ;
struct data *next ; } *data_p, *p ;
data_p = (struct data *)malloc(sizeof(struct data)) ; data_p->x = ’1’ ;
p = data_p ;
printf("%c\n",p->x);
p = p->next ;
printf("%c\n",p->x);
/* ここで割り当てられていないメモリにアクセスしている */
return 0 ; }
Id: C10.tex,v 1.7 2001-03-24 16:39:01+09 naito Exp
6.21.3.2 Bus error
Bus errorというエラーは,以下のように,ワード境界に合わせられていないアドレスから,ワード単位
で読み取りを行おうとした場合などに発生する.
/* Bus error を起こす
* 意味:適切に境界を合わせていないアドレスからデータを読み取ろうとした.
* 原因:
* ハーフワード, ワード, ダブルワードの境界に合わせられていないアドレスから,
* それぞれ2バイト, 4バイト, 8バイトを読み取った.
*/
#include <stdlib.h>
int main(int argc, char **argv) {
char *s = "hello world" ; int *i = (int *)&s[1] ; int j ;
j = *i ; return 0 ; }
6.21.3.3 Floating exception
Floating exception エラーは,0での除算を行おうとすると発生する.
/* 0 で除算を行う. */
#include <stdio.h>
int main(int argc, char **argv) {
printf("%d\n",1/0) ; return 0 ;
}
これの代りに
printf("%f\n",1.0/0.0) ;
とすると,Infという答えが返ってくる.
6.21.3.4 実行時エラーのトラップ
UNIX では上のような実行時エラーはカーネルによって検出され,カーネルからプロセスに対してシグ
ナル(signal)を送ることによって,プロセスはエラーの発生を知ることが出来る. Cでは, 標準ライブラ
リ関数signalを利用することによって,受け取ったシグナルの種類ごとにそのハンドラ(handler)を記
述することが可能になっている. 上のそれぞれの実行時エラーに対して,プロセスが受け取るシグナルは,
• segmentation faultに対してはSIGSEGV,
• bus errorに対してはSIGBUS,
• floating exceptionに対してはSIGFPE と定められている101 . したがって,
/* 0 で除算を行う.
実行時エラーをトラップする */
#include <stdio.h>
#include <signal.h>
101 これらのシンボルはsignal.h内で定義される整数定数である.
Id: C10.tex,v 1.7 2001-03-24 16:39:01+09 naito Exp
extern int signal_handler(void) ; int main(int argc, char **argv) {
signal(SIGFPE, (void (*)(int))signal_handler) ; printf("%d\n",1/0) ;
return 0 ; }
int signal_handler(void) {
fprintf(stderr,"Floating exception が発生したので, 実行を停止します\n") ; exit(-1) ;
return ; }
として,ハンドラを記述すれば,実行時エラーに対して,適切な処理を行うことも可能である.
6.21.4 ライブラリ呼出しとシステムコール
これまでに各種の標準関数を利用してきたが, それらのほとんどはライブラリ関数の呼出しという手順 で行われていた. これに良く似た概念でシステムコールと呼ばれるものがUNIX上では存在する102. 例 えば,ファイルをオープンする関数としてfopenがあるが,この関数内では実際にはシステムコールopen が用いられている. また, Cのプログラム内からファイルを削除するためには, unlinkシステムコールが 用いられる.
このように, Cのプログラム内から呼び出すことが出来る関数として,ライブラリコールとシステムコー ルの2種類があることがわかる. ここでは,この2つの違いを簡単にまとめておこう.
• ライブラリコールはANSI規格で定められ,すべての処理系でその呼出し方法は同一であるが,システ ムコールは, OSによって異なる呼出し方法が異なる.
• ライブラリコールは,ライブラリ内にあるサブルーチンの呼出しであるが, システムコールは,サービ スを受けるためのカーネル呼出しである.
• ライブラリコールは,プロセスのアドレス空間で実行されるが, システムコールは,カーネルの空間で 実行される.
• 時間測定では,ライブラリコールは,「ユーザ」時間になるが,システムコールは,「システム」時間に なる.
• ライブラリコールは呼出しに時間が掛らないが,システムコールは,呼出しのオーバヘッドが大きい.
このように,一見似ているが,ライブラリコールとシステムコールはその役割が異なり,処理系に依存する システムコールの部分を,ライブラリ関数によって吸収するという意味がある.
6.21.5 ANSI で定められた翻訳の最低基準
最後に, ANSIで定められた, 処理系に求められている最低基準を列挙しておこう. これらに挙げる数値 は翻訳限界と呼ばれ, 「各限界の出現をそれぞれ少なくとも一つ含むプログラムのうち少なくとも一つを 翻訳および実行できなければならない」と定められている.
• 複合文,繰り返し制御構造および選択制御構造に対する入れ子のレベル数(15)
• 条件付き取り込みにおける入れ子のレベル数(15)
• 一つの宣言中の一つの算術型, 構造体型,共用体型または不完全型を修飾するポインタ,配列および関 数宣言子(の任意の組み合わせ)の個数(12)
• 一つの完全宣言子における括弧に囲まれた宣言子の入れ子のレベル数(21)
102 MS-DOSでは,これに相当するのはBIOSコールと呼ばれるものがある.
Id: C10.tex,v 1.7 2001-03-24 16:39:01+09 naito Exp
• 一つの完全式における括弧に囲まれた式の入れ子のレベル数(32)
• 内部識別子またはマクロ名において意味のある先頭の文字数(31)
• 外部識別子において意味のある先頭の文字数(6)
• 一つの翻訳単位における外部識別子数(511)
• 一つのブロックにおけるブロック有効範囲を持つ識別子数(127)
• 一つの翻訳単位中で同時に定義されうるマクロ識別子数(1024)
• 一つの関数定義における仮引数の個数(31)
• 一つの関数呼出しにおける実引数の個数(31)
• 一つのマクロ定義における仮引数の個数(31)
• 一つのマクロ呼出しにおける仮引数の個数(31)
• 一つの論理ソース行における文字数(509)
• (連結後の)単純文字列リテラルまたはワイド文字列リテラル中における文字数(509)
• (ホスト環境の場合)一つのオブジェクトのバイト数(32767)
• #includeで取り込まれるファイルの入れ子のレベル数(8)
• 一つのswitch文(入れ子になったswitch 文を除く)中におけるcase 名札の個数(257)
• 一つの構造体または共用体のメンバ数(127)
• 一つの列挙体における列挙定数の個数(127)
• 一つのメンバ宣言並びにおける構造体または共用体定義の入れ子のレベル数(15)
これらの翻訳限界を越えたプログラムは,必ずしも他の処理系で翻訳できるとは限らないことに注意しよう.
また, 標準ヘッダファイル limits.hには, 各算術型で格納できる限界の数が書かれている. ここでは, そのマクロ名と, ANSI規格に定められた最低限の数値を書いておく.
• ビットフィールドでない最小のオブジェクト(バイト)におけるビット数 CHAR_BIT 8
• signed charのオブジェクトにおける最小値 SCHAR_MIN -127
• signed charのオブジェクトにおける最大値 SCHAR_MAX +127
• unsigned charのオブジェクトにおける最大値 UCHAR_MIN 255
• charのオブジェクトにおける最小値と最大値 CHAR_MIN CHAR_MAX
char のオブジェクトの値を符号付き整数として扱う場合, CHAR MIN の値は, SCHAR MIN と同じであ り, CHAR MAXの値は,SCHAR MAXと同じでなければならない.
その他の場合,CHAR MIN の値は0でなければならず,CHAR MAXの値はUCHAR MAX と同じでなければ ならない.
• サポートするロケールに体する多バイト文字の最大バイト数 MB_LEN_MAX 1
• short intのオブジェクトにおける最小値 SHRT_MIN -32767
• short intのオブジェクトにおける最大値 SHRT_MAX +32767
• unsigned short intのオブジェクトにおける最大値 USHRT_MAX 65535
• intのオブジェクトにおける最小値 INT_MIN -32767
• intのオブジェクトにおける最大値
Id: C10.tex,v 1.7 2001-03-24 16:39:01+09 naito Exp
INT_MAX +32767
• unsigned intのオブジェクトにおける最大値 UINT_MAX 65535
• long intのオブジェクトにおける最小値 LONG_MIN -2147483647
• long intのオブジェクトにおける最大値 LONG_MAX +2147483647
• unsigned long intのオブジェクトにおける最大値 LONG_MAX 4294967295
この他にもANSI規格にはfloat.h内で定める,浮動小数点型の特性も定められている.
References
[1] B. W. Kernighan and D. M. Ritchie. プログラミング言語C. 共立出版, 1981.
[2] B. W. Kernighan and D. M. Ritchie. プログラミング言語C(第2版). 共立出版, 1989.
[3] 日本規格協会. JISハンドブック(情報処理−プログラム言語編). 日本規格協会.
[4] ANSI. ANSI C Rationale. Silicon Press, 1990.
[5] ANSI. ANSI C Rationale. ftp://ftp.uu.net/doc/standards/ansi/X3.159-1989, 1989.
[6] P. van der Liden. エキスパートCプログラミング. アスキー出版局, 1996.
[7] N. Wirth. アルゴリズム+データ構造=プログラム. 日本コンピュータ協会, 1979.
[8] B. W. Kernighan and P. J. Plauger. プログラミング作法. アスキー出版局, 2000.
[9] B. W. Kernighan and D. M. Ritchie. The C Programing Language (2nd Ed.). Addison-Wesley, 1988.
[10] B. W. Kernighan and P. J. Plauger. プログラム書法(第2版). 共立出版, 1982.
[11] B. W. Kernighan and P. J. Plauger. ソフトウェア作法. 共立出版, 1982.
[12] B. W. Kernighan and P. J. Plauger. Software Tools in Pascal. Addison-Wesley, 1981.
[13] A. R. Feuer and N. Gehani. Ada, C, Pascal. 工学社, 1981.
[14] S. Oualline. C実践プログラミング(第3版). オライリー・ジャパン, 1998.
[15] N. Wirth. アルゴリズムとデータ構造. 近代科学社, 1990.
[16] A. R. Feuer. The C Puzzle Book (Revised Edition). Addison-Wesley, 1999.
[17] S. McConnel. Code Complete. アスキー出版局, 1994.
[18] E. Post. Real programmers don’t use Pascal.
http://www.mit.edu/people/rjbarbel/Humor/Computers/real.programmers, 1982.
Id: C.tex,v 1.4 2001-03-22 22:29:22+09 naito Exp