11章 ANSI/ISO規格C



11.1:

「ANSI C 規格」とは何を意味するのか。

A:

1983年に、アメリカ国内標準規格協会(ANSI)はC言語の標準化を目指 し委員会X3J11を発足させた。何度かの広範囲にわたる公開レビュー を含む長い困難な過程の後に、委員会の作業は、アメリカ国内標準規 格X3.159として1989年12月14日に批准され、1990年の春に出版された。 ANSI Cの大部分は世の中の慣習を規格化したもので、そのほかはC++ からの(一番有名なのは関数プロトタイプである)アイデアの拝借と、 多国語文字列への対応(酷評されている三連文字(trigraph)を含む)で ある。ANSI C規格は、Cの実行時のライブラリーについても規定して いる。

もっと最近の話では、規格は国際標準ISO/IEC 9899:1990として採択 された。そしてこのISOの規格がそれより前に存在したX3.159に取っ てかわった。これはアメリカ合衆国でも同じである。章番号の振り方 が違っている(手短に言えばISOの5章から7章がだいたい旧ANSIの2章 から4章に対応する)。ISOの規格であるから、技術正誤表(Technical Corrigenda)や規範補遺(Normative Addenda)を発行して絶えず改訂し なければならない。

1994年に、技術正誤表1が発行されおよそ40ヶ所で規格が修正された。 たいていは些細な修正もしくはわかりにくいところの書き直しである。 もっと最近の話では、規範補遺1でおよそ50ページの新しい題材が追 加された。ほとんどは国際化のための新しいライブラリー関数につい てである。国際化のための新しいライブラリー関数の技術正誤表の作 成が進行中で、二番目の正誤表は1995年後半に発行が予定されている。 さらに、ANSIもISOも自分たちの規格の定期的な見直しを命じている。 この手続きは1995年に始まり、まったく改定された規格を産み出すだ ろう("C9X"と愛称がつけられている。これは1999年までには完成する だろうと考えられているところからつけられている)。

出版物としての規格は、「Rationale(論理的根拠)」を含んでいる。 Rationaleは、規格の決定にまつわる多くの事柄について説明し、こ のFAQで取り上げているいくつかのことがらを含む規格の数多くの微 妙な点について説明している(RationaleそのものはANSI Standard X3.159-1989ANSI Standard X3.159-1989の一部ではない。ただし資料 として含まれている。ISO規格にも含まれていない)。


11.2:

どこから規格を手に入れることができるか

A:

アメリカ合衆国国内では、以下のところから手に入れることができる。

American National Standards Institute
11 W. 42nd St., 13th floor
New York, NY 10036 USA
(+1) 212 642 4900

あるいは
Global Engineering Documents
2805 McGaw Avenue
Irvine, CA 92714 USA
(+1) 714 261 1455
(800) 854 7179 (U.S. \& Canada)

その他の国では、国の規格を取り仕切る団体かジュネーブのISOに問 い合わせてみること。 (訳注: 日本では日本規格協会が発売している。JISはTEL 03-3583-8002、ISOはTEL 03-3583-8003 より入手可能である。)

ISO Sales
Case Postale 56
CH-1211 Geneve 20
Switzerland

(URL http://www.iso.chやニュースグループcomp.std.internatのFAQ であるStandards.Faqも参照のこと).

現時点では価格は、ANSIから購入するときは$130で、Globalから購入 する場合は$410.00である。Rationaleを含むオリジナルのX3.159も、 ANSIからは$205.00で、Globalからは$162.50で入手することができる。 ANSIは、その運営資金を規格の印刷物の販売から得ている。よって規 格の電子的なコピーは入手不可である。

合衆国国内では元のANSI X3.159(Rationaleを含む)を「FIPS PUB 160」 という名前で以下から入手可能かもしれない。

National Technical Information Service (NTIS)
U.S. Department of Commerce
Springfield, VA 22161
703 487 4650

誤解を招くような題の「注釈付きANSI C規格(Annotated ANSI C Standard)」という本がHerbert Schildtの注釈付きで Osborne/McGraw-HillからISBN 0-07-881952-0として出版されている。 これはISO 9899の、数ページを除いてほとんど全文を含んでいる。合 衆国国内では$40程度で手に入る。この本と公式の規格書の値段の差 は注釈の価値を反映していると考えられている。この本は誤りや抜け が多くて閉口する。規格そのもののほうも何ページか欠落している。 ネット上で多くの人が註釈部はまるっきり無視することを勧めている。 Clive Featherによる註釈の批評("註釈付きの註釈")は http://www.lysator.liu.se/c/schildt.htmlから手にいれることがで きる。

Rationaleのテキスト(規格全体ではない)はftp.uu.netのディレクト リdoc/standards/ansi/X3.159-1989からanonymous ftp(質問18.16参 照)可能である。http://www.lysator.liu.se/c/rat/title.htmlから も入手することができる。RationaleはSilicon Pressから出版もされ ている。ISBNは0-929306-07-4である。


11.3:

私が使っているANSI Cコンパイラに以下のようなコードを渡すと、デー タの型が違うと文句を付ける。なぜか。

extern int func(float);

int func(x)
float x;
{…

A:

新しい書き方であるプロトタイプ宣言の「extern int func(float);」 と、古い書き方の定義の「int func(x) float x;」を混ぜて使っている から。両方の書き方(質問15.2参照)を混ぜて使ってもたいていの場合 は問題ないが、この場合は問題がある。旧来のCは(ANSI Cもプロトタ イプがないときや、引数が可変個のときはそうだが)関数に渡すとき に一部の引数を"広げる"。floatはdoubleに格上げされ、charやshort intはintに格上げされる(古い書き方の関数定義では、値は呼ばれた 側の関数で格下の変数として宣言されているときには呼ばれたほうの 関数の中では自動的に対応する"狭い"型に逆変換された)。

新しい書き方であるプロトタイプ宣言を関数定義の中で首尾一貫して 使うか、

int func(float x) { … }

新しい書き方の関数プロトタイプ宣言を、古い書き方の定義に合うよ うに変更することで問題は解決する。
extern int func(double);

(この場合パラメータのアドレスを使う気がない限り、古い書き方の 宣言ではdoubleを使ったほうがわかりやすい)

"狭い"(char、short int、float)型は、関数引数や戻り値としては避 けたほうが安全であろう。

質問1.25も参照のこと。

References:
K&R1 Sec. A7.1 p. 186; K&R2 Sec. A7.3.2 p. 202; ANSI Sec. 3.3.2.2, Sec. 3.5.4.3; ISO Sec. 6.3.2.2, Sec. 6.5.4.3; Rationale Sec. 3.3.2.2, Sec. 3.5.4.3; H&S Sec. 9.2 pp. 265-7, Sec. 9.4 pp. 272-3.


11.4:

古い書き方と新しい書き方の関数の構文を混ぜて使ってもいいのか。

A:

注意さえすれば(質問11.3を特に参照のこと)混在することは、まった く合法である。しかし古い書き方の構文は時代遅れと考えられており、 その公式サポートはある日止まってしまうかもしれない。

References:
ANSI Sec. 3.7.1, Sec. 3.9.5; ISO Sec. 6.7.1, Sec. 6.9.5; H&S Sec. 9.2.2 pp. 265-7, Sec. 9.2.5 pp. 269-70.


11.5:

なぜ宣言

extern f(struct x {int s;} *p);

で奇妙な警告メッセージ「構造体xはプロトタイプの有効範囲内で導 入された」が出るのか。

A:

Cのブロックの有効範囲の一般規則の気まぐれにより、プロトタイプ 内でしか宣言されていない構造体は同じソースファイル内で宣言され ている他の構造体と互換性を持つことができないし、構造体名は期待 に反して後で使えない(名前はプロトタイプの終わりで有効範囲から 出てしまう)。

この問題を解決するには、プロトタイプの前に無意味にみえる宣言

struct x;

を付ける。これによりファイル全体にわたる有効範囲で、構造体xの 定義を置く場所が確保される。この領域は、プロトタイプ内部の構造 体の宣言によって埋められる。

References:
ANSI Sec. 3.1.2.1, Sec. 3.1.2.6, Sec. 3.5.2.3; ISO Sec. 6.1.2.1, Sec. 6.1.2.6, Sec. 6.5.2.3.


11.8:

なぜconstの値を、初期化指定子(initializer)や配列の大きさに使え ないか理解できない。

const int n = 5;
int a[n];

A:

const修飾子は実際は"読み込みだけ"を意味する。constと修飾された オブジェクトは普通の実行時のオブジェクトで(普通は)代入不可能で ある。したがってconstと修飾されたオブジェクトは、定数式という 用語の全体の意味を考えると定数式とは呼べない(Cはこの面ではC++ と違う)。コンパイル時の真の定数が必要なら、プリプロセッサーの #defineを使う。

References:
ANSI Sec. 3.4; ISO Sec. 6.4; H&S Secs. 7.11.2,7.11.3 pp. 226-7.


11.9:

「char const *p」と「char * const p」の違いは。

A:

「char const *p」は文字定数へのポインターで(文字を変更すること はできない)、「char * const p」は文字(の変数)へのポインター定 数(ポインターを変更することはできない)である。

一番内側から"ひっくり返して"読む。1.21も参照のこと。

References:
ANSI Sec. 3.5.4.1 examples; ISO Sec. 6.5.4.1; Rationale Sec. 3.5.4.1; H&S Sec. 4.4.4 p. 81.


11.10:

なぜconst char **を引数として取ると関数プロトタイプに書かれた 関数に、char **を渡すことができないのか。

A:

どんな型Tに対しても、const Tへのポインターを想定しているところ にTへのポインターを使うことは可能である。しかしこの(このことを 明記している)規則、つまり代わりに使うことのできるポインター型 に少しの不一致を許している規則は、再帰的に適用されるわけではな く、単に一番上の階層で適用されるだけである。ポインターが一段で ある場合を除いて、型が一致しないポインターを代入または引数とし て渡すときは、明示的にキャスト(この場合はconst char **)しなけ ればならない。

References:
ANSI Sec. 3.1.2.6, Sec. 3.3.16.1, Sec. 3.5.3; ISO Sec. 6.1.2.6, Sec. 6.3.16.1, Sec. 6.5.3; H&S Sec. 7.9.1 pp. 221- 2.


11.12:

main()をvoidとして宣言して「mainの戻り値がない」という目障りな メッセージを消すことができるか。

A:

できない。main()はintを戻り値とし、(適切な型の)0個か2個の引数 を持つと定義しなければならない。exit()を呼んでもまだ警告が出る のであれば、冗長であるがreturn文を挿入するしかない(あるいは使 える環境にいるのであれば「ここには届かないよ:NOTREACHED」命令 を使う)。

References:
ANSI Sec. 2.1.2.2.1, Sec. F.5.1; ISO Sec. 5.1.2.2.1, Sec. G.5.1; H&S Sec. 20.1 p. 416; CT&P Sec. 3.10 pp. 50-51.


11.13:

main関数の3番目の引数envpは。

A:

これは(よく見かけるけれど)標準でない拡張である。標準で用意され ているgetenv()関数が提供すること以上に環境の情報が必要ならば、 グローバル変数environを使うほうがまだましな手段だろう(これも同 じように標準ではないが)。

References:
ANSI Sec. F.5.1; ISO Sec. G.5.1; H&S Sec. 20.1 pp. 416-7.


11.14:

void main()と宣言してうまくいかないわけがないと思う。なぜなら main()から戻る代わりに、exit()を呼んでいるから。だいたい今使っ ているO/Sはプログラムのexit値/戻り値を無視する。

A:

main()から戻ってくるかどうかは関係ないし、そのステータスを見る かどうかも関係ない。問題はmain()の宣言がおかしいと、呼び出し側 (実行時のスタートアップのコード)がmain()を正しく呼び出すことす らできないかもしれないことにある。

君が使っているO/Sは終了時のステータスを無視して、void main()で もうまく動くかもしれない。しかし、このやりかたは移植性が低いし、 正しくもない。


11.15:

僕がいつも使っている『ほんとおの馬鹿向けのC』には、いつもvoid main()と書いてる。

A:

たぶんその本の著者は自分も対象読者の一人に数えているのだろう。 不思議なことに、例題のコードでvoid main()と書いてる本は多い。 そういう本は間違っている。


11.16:

exit(status)の値は、main()からの戻り値statusと本当に等しいのか。

A:

等しいときもあるし違うこともある。規格は等しいといってる。ただ し、規格に従っていないいくつかの古いシステムの上では何かしら問 題がある。また、mainにローカルなデータが、後片付けで必要な場合 はうまく動くことは期待できない。質問16.4を参照。(最後に、 main()を再帰的に起動する場合は上の二つは明らかに同じものではな い。)

References:
K&R2 Sec. 7.6 pp. 163-4; ANSI Sec. 2.1.2.2.3; ISO Sec. 5.1.2.2.3.


11.17:

ANSIの"文字列を作り出す"プリプロセッサーの演算子#を使って、メッ セージの中にシンボル定数を挿入しようとしている。けれど、#はマ クロの値ではなくマクロの名前を文字列にしてしまう。

A:

マクロを展開して文字列を作り出したいときは以下のような二段の手 続きを踏まなければならない。

#define Str(x) #x
#define Xstr(x) Str(x)
#define OP plus
char *opname = Xstr(OP);

これでopnameが「OP」ではなく「plus」に設定される。

同じ様な回避手段が、トークンを連結する演算子##を使って、二つの (名前ではなく)マクロの値を連結するときに必要になる。

References:
ANSI Sec. 3.8.3.2, Sec. 3.8.3.5 example; ISO Sec. 6.8.3.2, Sec. 6.8.3.5.


11.18:

「警告:マクロ展開が文字列リテラル内で発生」とは何を意味してい るのか。

A:

ANSI規格成立より前のコンパイラ/プリプロセッサーは以下のマクロ 定義を、

#define TRACE(var, fmt) printf("TRACE: var = fmt\n", var)

以下の様に使ったら、

TRACE(i, %d);

以下のように展開する。

printf("TRACE: i = %d\n", i);

つまり、マクロの引数は文字列リテラルや文字定数の中でも展開され る。

マクロの展開はK&Rでも標準Cでもこういう風には定義されていない。 マクロの引数を文字列に変換したいときは、新しく導入された.html#とい うプリプロセッサーの演算子を文字列リテラルの連結(これもANSI C で導入された機能)とともに使う。

#define TRACE(var, fmt) \
printf("TRACE: " #var " = " #fmt "\n", var)

上の質問11.17も参照のこと。

References:
H&S Sec. 3.3.8 p. 51.


11.19:

#ifdefで消したコードで、奇妙な構文エラーが発生した。

A:

ANSI Cでは#if、#ifdef、#ifndefによってコンパイルから除外される テキストも「コンパイル前処理のトークンとして有効なものである」 としている。このことは終端のないコメントや引用符(短縮した単語 の中のアポストロフィは文字定数の始まりにみえるので要注意である) があってはならないし、また引用符で囲まれた部分に改行があっては ならないことを意味している。よって自然言語のコメントや擬似コー ドは「公式の」コメントの区切り記号である/*と*/の間に書かなけれ ばならない。(ただし質問20.20を参照のこと。質問10.25も。)

References:
ANSI Sec. 2.1.1.2, Sec. 3.1; ISO Sec. 5.1.1.2, Sec. 6.1; H&S Sec. 3.2 p. 40.


11.20:

#pragmaとは何物で、何の役に立つのか。

A:

#pragmaは、うまく定義された唯一の"非常口"を与える。#pragmaは、 実装に固有の制御や機能の拡張を行うのに使うことができる。たとえ ばソースコードの表示方法の制御や、警告メッセージの抑制(古い lintの/* NOTREACHED */というコメントのように)に使える。

References:
ANSI Sec. 3.8.6; ISO Sec. 6.8.6; H&S Sec. 3.7 p. 61.


11.21:

#pragma onceは何を意味しているのか。ヘッダーファイルの中にいく つか書いてあった。

A:

いくつかのプリプロセッサーで、ヘッダーファイルの「べき等」を作 るのを(二度以上読み込まれても問題が発生しないようにするのを)容 易にするための拡張として用意されている。質問10.7で説明した #ifndefを使った技と本質的に同じである。


11.22:

char a[3] = "abc";は正しいのか。何を意味するのか。

A:

ANSI Cでは文法的に正しい(たぶんANSI C成立以前のシステムのいく つかでも正しく動くだろう)。ただし役にたつかどうかは別である。 これは大きさが3の配列を宣言し、中身を'a', 'b', 'c'で初期化する。 通常の終端記号の'\0'は付かない。 よってこの配列はC言語の意味で の文字列ではなく、strcpyやprintf %fなどでは使えない。

たいていの場合、配列を初期化するときは、初期化指定子の個数はコ ンパイラに数えさせるべきである。(上の初期化指定子"abc"の場合は もちろん計算結果は4になる)。

References:
ANSI Sec. 3.5.7; ISO Sec. 6.5.7; H&S Sec. 4.6.4 p. 98.


11.24:

なぜvoid *ポインターを相手に算術演算をすることができないのか。

A:

コンパイラがポインターが指す先のオブジェクトの大きさを知ること ができない。算術演算を行う前に、ポインターをchar *または操作し ようとしているデータ型のポインターにキャストする(質問4.5を見て からにすること)。

References:
ANSI Sec. 3.1.2.5, Sec. 3.3.6; ISO Sec. 6.1.2.5, Sec. 6.3.6; H&S Sec. 7.6.2 p. 204.


11.25:

memcpy()とmemmove()の違いは。

A:

memmove()が、コピー元とコピー先に重なりがあったときもうまく扱 うことを保証しているのに対し、memcpy()はそんな保証はしていない。 ひょっとしたらmemcpy()はそれを利用して効率のいい実装方法を取っ ているかもしれない。疑しいときはmemmove()を使うほうが安全であ る。

References:
K&R2 Sec. B3 p. 250; ANSI Sec. 4.11.2.1, Sec. 4.11.2.2; ISO Sec. 7.11.2.1, Sec. 7.11.2.2; Rationale Sec. 4.11.2; H&S Sec. 14.3 pp. 341-2; PCS Sec. 11 pp. 165-6.


11.26:

malloc(0)は、どういう動作をすべきなのか。ヌルポインターを返す のか、0バイトの領域を指すポインターを返すのか。

A:

ANSI/ISO規格にはどちらでもかまわないと書いてある。動作は実装依 存(質問11.33参照)である。

References:
ANSI Sec. 4.10.3; ISO Sec. 7.10.3; PCS Sec. 16.1 p. 386.


11.27:

なぜANSI規格は、外部識別子の7文字目以降に意味があることや大文 字小文字の違いを保証しないのか。

A:

問題は古いリンカーにある。古いリンカーはANSI規格の管理下にない し、古いリンカーが載ったシステムを使ってCコンパイラを開発して いる人の管理下にもない。識別子は最初の6文字しか有効でないと制 限しているのであって、識別子の長さを6文字に制限するということ ではない。この制限はうんざりさせられるが耐えられないものでもな い。この制限は、規格の中で「すたれつつある」と記述されている。 将来の改定で制限は緩和されそうである。

制限を持つ現状のリンカーへの譲歩は、一部の人がどんなに反対しよ うと、行わなければならない(Rationaleはこの制限を残しておかなけ ればならないことを「一番辛いこと」と書いている)。もし賛成でき ないなら、または制限のあるリンカーを使ったコンパイラが外部識別 子にもっと多くの文字数を有効に扱っているとプログラマーに見せる トリックを思い付いたら、X3.159のRationale(質問11.1を参照)の 3.1.2項を読むこと。この章はいくつかの方法を議論し、なぜそうい う方法を強制することができなかったか説明している。

References:
ANSI Sec. 3.1.2, Sec. 3.9.1; ISO Sec. 6.1.2, Sec. 6.9.1; Rationale Sec. 3.1.2; H&S Sec. 2.5 pp. 22-3.


11.29:

私の使っているコンパイラは、考えられる限りの一番単純なテストプ ログラムに対しても、ありとあらゆる文法エラーではねつける。

A:

きっと君が使っているのはANSI規格が決まる前に作られたコンパイラ で、関数プロトタイプの類をはねつけるからである。

質問1.31, 10.9, 11.30も参照のこと。


11.30:

ANSIコンパイラを使っているのに、ANSI/ISO規格のライブラリールー チンで未定義となるものがあるのはなぜか。

A:

ANSIの構文は受け付けるけれどANSI互換のヘッダーファイルやランタ イムライブラリーが導入されていないコンパイラは珍しくない。質問 11.29, 13.25, 13.26も参照のこと。


11.31:

誰か古い書き方のCプログラムをANCI Cに変換するプログラムや、そ の反対をおこなうツール、また自動的に関数プロトタイプを生成する ツールを持っていないか。

A:

protoizeとunprotizeというのが、関数プロトタイプから古い書き方 の関数定義や宣言へ、あるいはその逆を実行するツールである(これ らのツールは、"古い"書き方のCとANSI C間の完璧な変換ツールでは ない)。FSFのGNU Cコンパイラの正式な配布の一部である。質問18.3 も参照のこと。

プログラムunproto(サイトftp.win.tue.nlのファイル /pub/unix/unproto5.shar.Z)は、プリプロセッサーとコンパイラの間 に入って、ANSI Cを古いCに(コンパイル時に)変換するフィルターで ある。

GNU GhostScriptパッケージにはansi2knrという、ちょっとしたプロ グラムがついている。

ANSI Cで書いたプログラムを古いCに変換する前に、このような変換 は安全に行うことも自動的に行うこともできないことに注意すること。 ANSI CはK&Rにない新しい機能を導入し、より複雑になっている。プ ロトタイプ付きの関数の呼び出しには特に注意すること。たぶん明示 的なキャストが必要となるだろう。質問11.311.29も参照のこと。

関数プロトタイプの自動生成ソフトはいくつか存在する。多くはlint に手を入れたものになっている。CPROTOというプログラムが1992年3 月にcomp.sources.miscにポストされた。他にもcextractというツー ルが存在する。コンパイラのベンダーの多くは単純なユーティリティー を用意している。質問18.16も参照のこと。(古いコード用にプロトタ イプを生成するときは"狭い"引数に注意すること。質問11.3も参照の こと。)

最後に一言。大量の古いコードをANSI Cに変換する必要が本当にある のか。古い書き方の関数の構文はそのまま使える。あわてて変換する とバグを持ち込みやすい。(質問11.3を参照。)


11.32:

なぜANSI準拠が売り物のCコンパイラ轟天が、このコードをはねつけ るのか。私はこのコードはANSIに規定された通りだと考える。なぜな らgccなら受け付けるからである。

A:

たいていのコンパイラは規格外の拡張機能をいくつか用意している。 gccはその最右翼である。はねられたコードが、そのような拡張機能 を使っていない自信があるか。言語の性質を調べるのに特定のコンパ イラだけで実験することはよい考えではない。すなわち、規格が拡張 を容認しているかもしれないし、実験に使ったコンパイラの誤りかも しれない。


11.33:

実装により定義された(implementation-defined)動作、不定の (unspecified)動作、未定義の(undefined)動作を区別することを世の 中では重視するようだ。違いは?

A:

簡潔に説明する。実装により定義された動作とは、どう振る舞うかを 実装が選択して、その振る舞いを文書にすることを意味する。不定の 動作とは、どう振る舞うかを実装が選択しなければならないが文書に する必要はないことを意味する。未定義とは、本当にどんなことがお こっても不思議ではないことを意味する。どの場合にも規格は、必要 条件を課していない。最初の2つの動作について規格は時々ありそう な振る舞いの集合について少し提案している(その集合から選択しな ければならないかもしれない)。

規格は未定義の動作に直面したときのコンパイラの動作について何も 必要条件を設定していないことに注意すること。だからコンパイラは 本当に何をしてもいいことになる。プログラムの中に未定義の動作が あっても何とかやっていけると考えるのは大変危険である。比較的簡 単な例が質問3.2に出てくる。

移植性のあるコードを書きたいのなら上の3つの違いを無視すること ができる。これら3つの動作のどれにも頼らないコードを書こうと思 うだろうから。

質問3.911.34も参照のこと。

References:
ANSI Sec. 1.6; ISO Sec. 3.10, Sec. 3.16, Sec. 3.17; Rationale Sec. 1.6.


11.34:

ANSI規格には多くの論点が未定義のまま放置してあることを考えると ぞっとする。規格の仕事はこれらの問題を標準化することではなかっ たのか。

A:

C言語は昔からその一部はコンパイラやハードウェアがどう実装され てどう振る舞ってもいいようになっている。こういうふうにわざとはっ きりさせないことで、コンパイラはありそうもないような状況にまで 適切に定義された動作を保証することのためにすべてのプログラムに 余計なコードの重荷を背負わせることなく、よくある状況で効率のい いコードを生成することができる。よって規格はそれまでに存在した 習慣を単に明文化したにすぎない。

プログラミング言語の規格は言語の使用者とコンパイラの実装者の間 の条約と考えればいい。その条約の一部はコンパイラ実装者が提供を 約束した機能で、使い手は用意されているものだと思いこんでいい。 しかしながら、別の部分はユーザーが従うと約束した決まりで、実装 者は守ってもらえると思いこんでいい。両方が自分の約束を守るかぎ り、プログラムは頑張れば動く可能性がある。どちらかでも公約を破 れば、どんな部分についても確実に動くとは保証できない。

質問11.35も参照のこと。

References:
Rationale Sec. 1.1.


11.35:

i = i++の振る舞いが未定義だとうるさくいう人がいるけれどANSI準 拠のコンパイラで試して私が思うとおりの結果を得た。

A:

未定義の振る舞いに出くわしたらコンパイラは好きなように振る舞う (かつ、ある範囲内で、実装が定義した振る舞い、あるいは不定の振 る舞いに出くわしたとき)。その中にはあなたが期待した結果も含む。 こんなことに頼るのは馬鹿げている。質問11.3211.3311.34を参 照のこと。

目次へ戻る