A:
値を格納する領域をいくつか起動する側で用意して、それらの領域を 指すポインターを引数として渡し、関数にポインターの先に値を埋め てもらう。あるいは、関数の戻り値を構造体にして、その構造体に希 望の値を設定してもらう。あるいは(最後の最後の手として)global変 数を使うことを考える。質問2.7, 4.8, 7.5を参照のこと。
各引数は配列argvによって指されている。main()はargvを引数として 起動される。
References:
K&R1 Sec. 5.11 pp. 110-114; K&R2 Sec. 5.10 pp. 114-
118; ANSI Sec. 2.1.2.2.1; ISO Sec. 5.1.2.2.1; H&S Sec. 20.1 p.
416; PCS Sec. 5.6 pp. 81-2, Sec. 11 p. 159, pp. 339-40 Appendix
F; Schumacher, ed., _Software Solutions in C_ Sec. 4 pp. 75-85.
A:
最も移植性が高い解はテキストファイル(通常はASCII)を使うこと である。fprintf()を使って書き込み、fscanf()の類を使って読む(同 様の助言はネットワークのプロトコルにもあてはまる)。「テキスト ファイルは大きすぎて、その読み書きは遅すぎる」という意見は疑っ てかかること。その効率はしばしば実用に耐えるものであるし、マシ ン間で容易にデータ交換できることや標準的な道具だけで操作できる という利点は否定できない事実である。
バイナリーフォーマットを使わなければならない場合でも、移植性を 向上させる方法はある。標準化されたフォーマットを利用して既存の I/Oライブラリをうまく使うことも可能である。標準のフォーマット としてはSUNのXDR(RFC 1014)、OSIのASN.1(CCITTのX.409やISO 8825 で「基本符号化則:Basic Encoding Rules」として参照されている)、 CDF、netCDF、HDFが存在する。質問2.12や12.38を参照のこと。
References:
PCS Sec. 6 pp. 86,88.
A:
一番単純な方法は、関数の名前と関数へのポインターの対応表を用意 することである。
int func(), anotherfunc();そうして名前を求めてテーブルを探し、名前に対応する関数へのポイ ンターを使って関数を起動すればよい。質問2.15と19.36も参照のこ と。struct { char *name; int (*funcptr)(); } symtab[] = { "func", func, "anotherfunc", anotherfunc, };
References:
PCS Sec. 11 p. 168.
A:
charやintの配列を使う。マクロを使って、しかるべきインデックス にある望みのビットにアクセスできるようにする。以下にcharの配列 を使った簡単なマクロの例を紹介する。
#include <limits.h> /* for CHAR_BIT */ #define BITMASK(b) (1 << ((b) % CHAR_BIT)) #define BITSLOT(b) ((b) / CHAR_BIT) #define BITSET(a, b) ((a)[BITSLOT(b)] |= BITMASK(b)) #define BITTEST(a, b) ((a)[BITSLOT(b)] & BITMASK(b))(<limits.h>がないときはCHAR_BITのかわりに8を使う)
References:
H&S Sec. 7.6.7 pp. 211-216.
A:
ポインターを使う手がある。
int x = 1; if(*(char *)&x == 1) printf("little-endian\n"); else printf("big-endian\n");共用体を使って調べることも可能である。
質問10.16も参照のこと。
References:
H&S Sec. 6.1.2 pp. 163-4.
A:
質問の内容を本当に理解しているか確認すること。整数は内部的には 2進数で保存されている。ただし、数が8進だとか10進だとか16進など 自分に都合いいN進法で(底で)あると考えるのはたいていの場合間違 いである。どの底で(何進法で)数を表わすかは外界から数を読んだり 書いたりするときにだけ問題になる。
ソースコードの上では、10進法以外の場合は数の前に0や0x(それぞれ 8進、16進を表わす)を付けて表わす。入出力中は、整形済みの数が何 進数かは、printfやscanfの類では書式指定子(%d, %o, %xなど)を選 択することで、strtol()やstrtoul()では3番目の引数で制御可能であ る。ただしバイナリー入出力中は、ここでも何進法かはどうでもいい ことになる。
"バイナリー"入出力についてもっと知りたいならば、質問2.11を参照 のこと。質問8.6や13.1も参照のこと。
References:
ANSI Secs. 4.10.1.5,4.10.1.6; ISO
Secs. 7.10.1.5,7.10.1.6.
A:
2進数の定数は存在しないし、2進数用の書式も存在しない。底2の(2 進数の)文字列による表現をstrtol()を使って整数に変換することは 可能である。
A:
この手の"ビットをいじる問題"は参照用テーブル(lookup table)を使 うことで、高速かつ能率よく処理することができる(ただし以下の質 問20.13を参照のこと)。
A:
効率はcomp.lang.cのお気に入りの話題である。ただし人がよく、そ うであると考えているほどは、たいした問題ではない。たいていのプ ログラムのたいていのコードは処理速度を要求されているわけではな い。処理速度を要求しないコードの場合、最大限に効率化を計るより、 わかりやすさや移植性を考えるほうがずっと大事である(コンピュー ターは非常に高速である。だから"能率の悪い"コードも目立った遅れ なく走るかもしれないということをお忘れなく)。
プログラムの中でどこが問題点になるか予想することは非常に困難な ことで有名である。効率に関心があるのならプログラムのどの場所に 関心を置くべきか調べるために、処理の輪郭を記録(profiling)する ソフトウェアを使うことは大事である。たいていは、実際の計算時間 はI/Oやメモリの割り当てなどの周辺装置の操作に集中する。そして これらの作業は、バッファリングやキャッシングの技を使って高速化 することができる。
コードの処理時間の制約が厳しいところでも、コードの詳細を局所的 に最適化しなければならないほど重要であることはない。しばしば提 案される"効率的なコーディングの技"の多くは(たとえば2のべき乗を 掛けるときに掛け算の代りにシフト演算子をつかうこと)、単純なコ ンパイラでも自動的にやってくれる。不器用な"最適化"の試みはコー ドをふくらませることで性能を悪化させる可能性さえあるし、その移 植性が高いことはめったにない(すなわち、あるマシンでは高速化さ れかもしれないけれど、他のマシンでは遅くなってしまう)。いずれ にせよ、コードをいじってもせいぜい効率が線形に改善されるだけで ある。大きな見返りはよりよいアルゴリズムから得られる。
効率化のトレードオフに関する議論や、効率化が本当に重要なときの 良い助言についてはKernighanとPlaugerの『プログラム書法』の7章 やJon Bentleyの『プログラム改良学』を参考のこと。
A:
これらの質問や多くの類似の質問への正確な解答は、もちろん使用し ているプロセッサーやコンパイラに依存する。結果が知りたければテ ストプログラムで注意して測定するしかない(多くの場合、その差は 非常に小さいので、差を知るのに何万回もくり返しが必要となる。も し手に入るのなら、両方の技を使ったコードが別のコードを生成した かどうか、コンパイラの生成したアセンブラ出力を見比べてみること)。
たいていは大きな配列の中をアクセスしてまわるのにポインターを使 うほうが、配列の添え字を使うより高速であるが、その反対のプロセッ サーも存在する。
関数呼び出しは、インラインで記述するよりも、それだけ明らかに遅 くなるけれど、モジュール化やコードのわかりやすさに役に立ってい ることを考えれば、使わない理由なんてめったに存在しない。
i = i + 1といった書き方を書き直す前に、相手はCコンパイラであっ てキー入力をプログラムできる電卓を相手にしているのではないこと をお忘れなく。よくできたコンパイラならi++、++i、i = i + 1、ど れについても同じコードを生成する。i =i + 1を押しのけて++iやi += 1を使うのは、書き方の面からであって効率を問題にしているから ではない(質問3.12も参照のこと)。
A:
直接には不可能である。文字列を整数コードに対応させる別の関数を 用意して、その整数コードを使って処理を切り替えるのが適切な場合 がある。もちろん、その他の手としては、strcmp()に頼って通常の if/elseを並べることが考えられる。質問10.12, 20.18, 20.29を参照 のこと。
References:
K&R1 Sec. 3.4 p. 55; K&R2 Sec. 3.4 p. 58; ANSI
Sec. 3.6.4.2; ISO Sec. 6.6.4.2; H&S Sec. 8.7 p. 248.
A:
できない。switch文はもともとの設計でコンパイラがコードを変換す るのが楽なように単純になっている。だからcaseのラベルは単体の定 数の整数式に限られている。一つの式に複数のcaseのラベルを付ける ことは可能である。こうすれば、すべての場合をいちいち記入するこ とを面倒に思わなければ、小さな範囲を覆うことができる。
任意の範囲指定や定数以外の式をもってきたいなら、if/elseをずら ずら並べるしかない。
質問20.17も参照のこと。
References:
K&R1 Sec. 3.4 p. 55; K&R2 Sec. 3.4 p. 58; ANSI
Sec. 3.6.4.2; ISO Sec. 6.6.4.2; Rationale Sec. 3.6.4.2; H&S
Sec. 8.7 p. 248.
A:
省略可能だ。
大昔、Cの初期には、必要であった。そのころにCを学んだ人がたくさ んいるし、そのころ書かれたコードが今でも世の中に出まわっている。 それでカッコが今でも必要であるという考えが広まっている。
(ちなみにsizeof演算子も、オペランドが変数か単項式なら、カッコ は省略可能である。)
References:
K&R1 Sec. A18.3 p. 218; ANSI Sec. 3.3.3, Sec. 3.6.6;
ISO Sec. 6.3.3, Sec. 6.6.6; H&S Sec. 8.9 p. 254.
A:
Cのコメントが入れ子にできないのは、PL/Iのコメントが入れ子にで きないからである。CはPL/Iからコメントについてのアイデアを借用 している。コメントを含むような大きな領域を"コメントアウト"する ときは#ifdefや#if 0を使ったほうがいい(ただし、質問11.19を参照)。
/*や*/は二重引用句でくくられた文字列のなかでは特別な意味を持た ない。だからこれらの文字列がコメントを意味することはない。なぜな らプログラムが(特に出力としてCのコードを生成するプログラムが)、 これらの文字列を出力したいかもしれないから。
C++風の//で始めるコメントは現在のところCでは文法上許されていな いことに注意。だから、Cプログラムで使うのはよい考えではない(た とえ使用中のコンパイラが拡張機能として対応しているとしても)。
References:
K&R1 Sec. A2.1 p. 179; K&R2 Sec. A2.2 p. 192; ANSI
Sec. 3.1.9 (esp. footnote 26), Appendix E; ISO Sec. 6.1.9, Annex
F; Rationale Sec. 3.1.9; H&S Sec. 2.2 pp. 18-9; PCS Sec. 10 p.
130.
A:
呼ぶ側の関数のローカル変数に正しくアクセスするように入れ子の関 数を用意することは簡単な話ではない。そういうわけで、入れ子の関 数はCが単純さを失わないように故意に省略された(gccは拡張機能と して入れ子の関数を許している)。入れ子の関数があったとしたら使 えそうな状況の多くの(例:qsortの比較に使う関数)少し面倒だけれど 要求を満たす解としては、static宣言した隣り合う関数を使い、 static変数で情報をやりとりする方法がある。(そういう関数間で情 報をやりとりが必要な場合のもっときれいな解は、必要なコンテキス トを含んだ構造体へのポインターをやりとりすることである。)
A:
解答は、使用中のマシンやコンパイラが関数を起動する方法に依存す る。ひょっとしたらまったく不可能かもしれない。コンパイラの資料 を注意深く読むこと。ときどき「複数のプログラム言語を使ったプロ グラミングの手引き」が載っている。それでも引数を渡す方法や、実 行時の適切な立ち上がりを保証することは、神秘的ですらある。その 他にGlenn Geers作のFORT.Zからも手掛かりが得られる。このプログ ラムはsuphys.physics.su.oz.auのsrcディレクトリに置かれている。
cfortran.hというCのヘッダーファイルは、世の中に出まわっている 多くのマシン上で、C/FORTRAN間のインタフェースを簡単にする。こ れはzebra.desy.de(131.169.2.244)よりanonymous ftpで手に入れる ことができる。
C++では外部関数の宣言に"C"修飾子を付けることで、Cの関数の起動 方法に従って起動することを指定することができる。
References:
H&S Sec. 4.9.8 pp. 106-7.
A:
いくつかの自由に配布可能なプログラムが存在する。
p2c | Dave Gillespieによって書かれ、comp.sources.unixに1990 年4月(Volume 21)に投稿された。csvax.cs.caltech.eduから anonymous ftpすることもできる。ファイル名は pub/p2c-1.20.tar.Z。 |
---|---|
ptoc | これもPascalをCに変換するツール。これはPascalで書かれ ている(comp.sources.unixのVolume 10、パッチはVolume 13?にある)。 |
f2c | ベル研とBellcoreとカーネギーメロン大学の共同開発による FORTRANのプログラムをCに変換するソフトウェア。f2cにつ いて知りたければ「send index from f2c」というメッセー ジとともにnetlib@research.att.comあるいは research!netlibにメールを出すこと。(research.att.comの ディレクトリdist/f2cからのanonymous ftpも可能である。) |
A:
C++はCに起源を持ち、大部分Cに基づく。しかしC言語としては正しい 構造であるが、C++では文法違反なものがいくつか存在する。逆に、 ANSI Cはプロトタイプやconstのような、いくつかの機能をC++から受 け継いでいる。よってどちらが上位集合とか下位集合ということはい えない。これらの違いにもかかわらず、Cのプログラムの多くはC++の 環境で正しくコンパイルできるし、最近のコンパイラの多くはCとC++ 両方のコンパイルモードを用意している。
References:
H&S p. xviii, Sec. 1.1.5 p. 6, Sec. 2.8 pp. 36-7,
Sec. 4.9 pp. 104-107.
A:
文字列のおよその一致に関するよく書けた情報やアルゴリズムが、参 考文献の役に立つ一覧と共に、Sun WuとUdi Manberの論文"AGREP -- A Fast Approximate Pattern-Matching Tool"に載っている。
その他のやり方はとしては「soundex」がある。これは同じ様な発音 の単語を同じ数値のコードに写像する。soundexは同じ様な発音の名 前を捜すのに(ちなみに電話帳の助けになるように)設計されたが、任 意の単語を処理するようにすることもできる。
References:
Knuth Sec. 6 pp. 391-2 Volume 3; Wu and Manber,
"AGREP -- A Fast Approximate Pattern-Matching Tool" .
A:
ハッシュ法とは、文字列を整数に写像する処理のことをいう。通常、 整数は比較的小さな範囲のものを使う。「ハッシュ関数」は文字列 (または、その他のデータ構造を)を有界の整数("ハッシュバケツ (hash bucket)")に写像する。この整数は配列のインデックスとして 使えるし、くり返しの比較に使うのも容易である。(すぐわかるよう に、文字列の巨大になる可能性のある集合を整数の小さな集合に写像 すれば一意にはならないものである。ハッシュ法を使ったどんなアル ゴリズムも、この"衝突"が起こることを考慮にいれなければならない。) これまでに、さまざまのハッシュ関数や関連するアルゴリズムが開発 されてきた。これらを満足に論じることは、このFAQの範囲を越えて いる。
References:
K&R2 Sec. 6.6; Knuth Sec. 6.4 pp. 506-549 Volume 3;
Sedgewick Sec. 16 pp. 231-244.
A:
ルーチンmktime(Q12.6、Q12.7を参照)を使うか、Zellerの公式を使う か、sci.mathのFAQを参照するか、あるいは坂本智彦がポストした以 下の気の利いたコードを試すこと。
dayofweek(y, m, d) /* 0 = Sunday */ int y, m, d; /* 1 <= m <= 12, y > 1752 or so */ { static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4}; y -= m < 3; return (y + y/4 - y/100 + y/400 + t[m-1] + d) % 7; }質問13.14と20.32も参照のこと。
References:
ANSI Sec. 4.12.2.3; ISO Sec. 7.12.2.3.
A:
一つめの問題の答えはYES、二つめはNOである。グレゴリオ暦のカレ ンダー用の正しい式は以下の式で表わされる。
year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)詳細については、まともな天文学年鑑かその他の参考書を見ること。 (永遠に続く議論を未然に防ぐために言っておく。4000年に一度うる う年が来ると主張する文献は間違っている。)
A:
本当に移植性の高い自己再生プログラムを書くのは大変難しい。これ は特に、一重/二重引用符の使いかた、文字集合(ASCII, EBSDIC...) に何を使うかからくる困難による。
以下は古典的な例である(普通は一行で紹介される。しかし最初に走っ たときに自身を"修正"する)
char*s="char*s=%c%s%c;main(){printf(s,34,s,34);}";(このプログラムは、この種のプログラムの多くと同じように二重引 用符 " がASCIIのように34であると決めてかかっている.)
main(){printf(s,34,s,34);}
A:
とんでもなく曲がりくねって展開されたバイトコピーの関数である。 これはTome DuffがLucasfilmに在籍中に考え出した。"古典的"な形式 では、以下のようになる。
register n = (count + 7) / 8; /* count > 0 assumed */ switch (count % 8) { case 0: do { *to = *from++; case 7: *to = *from++; case 6: *to = *from++; case 5: *to = *from++; case 4: *to = *from++; case 3: *to = *from++; case 2: *to = *from++; case 1: *to = *from++; } while (--n > 0); }countバイトだけfromによって指された配列からtoで指された場所ま でコピーする(toが指している先はメモリマップされたデバイスの出 力レジスターである。だからincrementされない)。残りのバイトの処 理する問題を(countが8の倍数でないときに)、switch文に一度に8バ イトコピーするループを差し込むことで解決している。驚くなかれ、 swtich文に入れ子になっているブロックの中にcaseのラベルを埋めこ むことは合法である。この技のCで開発する人達や世の中への彼の声 明によれば、このCのswtichの構文は、特に"下に落ちる(fall through)"動きが、昔から物議をかもしてきた。「このコードはそう いう議論に論拠を出すことになる。ただし、賛成意見なのか反対意見 なのかはよくわからない。」
A:
コンテストのスケジュールは、授賞者の発表されるUSENETの会議の日 付に縛られている。このFAQを書いている時点では、この年に一度行 われるコンテストは10月に開かれる。 最新のルールやその外の情報 を得るにはSubject:に「send rules」と書いて
{apple,pyramid,sun,uunet}!hoptoad!judges or judges@toad.comに送ればよい(これらのアドレスは出展の申し込み用ではない)。
コンテストの受賞者は1月に行われる冬のUsenixの会議で最初に発表 され、その後ネットに投稿される。昔の(1984年以降の)受賞作品は uunetの~/pub/iocccというディレクトリから手に入れることができる (質問18.16を参照)。
どうやっても手に入れられなければ、過去の授賞作は上記のアドレス にSubject:として「send YEAR」を付けた電子メールを送ることによっ て得られる。ここでYEARは4けたの年か、年度の範囲指定、あるいは 「all」である。
A:
このキーワードはFORTRANのように関数が複数の異なる名前のエント リーポイントを持つことを許すことを考えて予約されていた。この機 能が実現された例を知っている人は誰もいない(このキーワードを使っ てどういう構文が考えられていたのかもだれも知らない)。この機能 は撤回され、ANSI Cのキーワードにはならなかった。(質問1.12も参 照のこと。)
References:
K&R2 p. 259 Appendix C.
A:
CはKen Thompsonの実験的なプログラミング言語Bから派生したもので ある。BはMartin RichardsのBCPL(Basic Combined Programming Language)から着想を得ている。BCPLはCPL(Cambridge Programming Language)の仕様を記述したものであった。しばらく、Cの後継言語は Dではなく、P(BCPLの3番目の文字)という名前になるのではないかと 噂されていた。しかしもちろん今日一番目につく後継言語はC++であ る。
A:
C言語のキーワードの「char」の発音の仕方は少なくとも3通りはある。 「char(チャー)」、「care(ケア)」、「car(カー)」のように発音す る。どれを使ってもいい。
A:
最新版はftp.eskimo.comのディレクトリu/s/scs/C-faq/から入手する ことができる。ネットからも引っ張ってくることができる。通常、ニュー スグループcomp.lang.cに月の始めに投稿される。Expires:行が付い ているので一ヶ月丸々消えないで残っている。並行して、簡易版も入 手可能である(投稿もされている)。大きく更新した版には変更の一覧 も用意している。
このFAQはさまざまな形でニュースグループcomp.answersや news.answersに投稿されている。いくつものサイトがこのFAQも含め て、news.answersへの投稿やその他のFAQを保管している。サイト rtfm.mit.edu(ディレクトリpub/usenet/news.answers/C-faq/と pub/usenet/comp.lang.c/)やサイトftp.uu.net (ディレクトリ usenet/news.answers/C-faq/) は、そのようなサイトのうちの2つで ある。archieサーバー(質問18.16参照)はそのようなサイトを見つけ る助けになる。archieに"prog C-faq"と聞いてみろ。ftpが使えない 環境なら、rtfm.mit.eduにあるメールサーバーを使ってFAQを手にい れることができる。まずは、本文に「help」とだけ書いて mail-server@rtfm.mit.eduに送る。その他の情報はnews.answersの meta-FAQ listから得られる。
このFAQの別の形態のものがWorld-Wide Webで読むことができる。3つ のURLは(それぞれ形態は異なる)、 http://www.lysator.liu.se/c/c-faq/index.htmlと http://www.hut.fi/~jkorpela/CFAQ.htmlと http://www.cis.ohio state.edu/hypertext/faq/usenet/C-faq/top.htmlである。(以下で紹 介する本に対応する)拡大版はもうすぐ、たぶん1995年の9月半ばに使 えるようになる。すべてのFAQを指すURL(話題による検索ができるも の)は http://www.cis.ohio- state.edu/hypertext/faq/usenet/FAQ-List.htmlと http://www.luth.se/wais/にある。
このFAQの拡張した版はAddison-Wesleyより『C Programming FAQs: Frequently Asked Questions』(ISBN 0-201-84519-9)として1995年11 月に出版されている。
このリストは発展し続ける資料である。単に今月の興味深い質問への 回答の寄せ集めではない。昔のリストは時代遅れで大した情報を持っ ていない。例外は、ときどきある入力ミスで、今回のリストに含まれ ていないものである。