関数 - みる会図書館


検索対象: 月刊 C MAGAZINE 1992年1月号
140件見つかりました。

1. 月刊 C MAGAZINE 1992年1月号

よいだろう。 サイン ( 正弦 ) 関数 SIN を計算する具体例 を見てみよう。これが SQRT と同様に問題の ある例て、あるのを私は承知している。各自 が一連の関数に取り組んだとしても , この ふたつは最後まて残されるものだ。プロの 書いたプログラムを利用したほうが , まず 間違いないだろう。とは言うものの , 関数 SIN は多くの人が知っている関数て、あり , 多 くの有益な原理を含んている。 私たちが考えている SIN 関数についての級 数表現は Y = X ー X3 / 3 ! 十 X5 / 5 ! ー X7 / 7 ! 十 て、始まる。 こて階乗が分母に使われ ているが , この階乗の値というのは急速に 増加するのて、分母にくる係数としてすばら しい役割りを果たしている。つまりどんなに X が大きな値だとしても , ある程度高次にな れば分母 ( 階乗 ) が分子 ( X のべき乗 ) を圧 倒してしまうのて、 , その項を無視すること がて、きる。 このケースに限ればもっとよい点がある。 それは必ず計算しなければならない X の値の うち最大値は 4 , すなわち約 0.7854 だとい うことて、ある。この値は 1 より小さいのて、 , 高次になるにつれて X のべき乗値 ( 分子 ) も 小さくなっていく。これて、サインが級数展 開を使った多項式による近似にむいている 理由がわかるだろう。 もちろんトリックは一 X ー > な / 4 となる X に対しては SIN (X) を計算しないことだ。サ インとコサイン ( 余弦 ) は、、円〃関数とも 呼ばれているのを思い出してほしい。つま り X の 2 ごとの変化を 1 周期として , 同じ関 数値を繰り返していくのだ。 1 周期内て、もこ の二つの関数はさらに対称性がある。そのた めー X ー < / 4 となる X に対して COS (X) も 計算してしまえば , どんな X の値に対する S IN て、も COS て、も簡単に構成可能だ ( COS を近 似する級数は SIN とよく似ている ) 。その結 22 C MAGAZINE 1992 1 果の値は , X の値を一 X ー < 4 となるよう に換算したときの SIN(X) か COS(X) のどち らか , あるいはそれらの符号を反転したも のて、ある。 角度 X を換算する簡単な方法は , X から 4 の倍数を引くことだ。このときの 4 に対 する乗数て、は最下位の 2 ビット ( 値にすれば 0 から 3 まて、 ) 以外はすべてがムダて、ある。 残りのビットは X が円の周りを回った回数を カウントするだけの意味しかない。この最 下位の 2 ビットにより X の存在する象限が決 定される。したがって , それが SIN と COS の どちらを計算するか , そして結果の符号を 反転するかどうかを決めることになる。そ のロジックはわざわざここてお見せするほ どのものて、はないて、しよう。残念ながら , この簡単な角度換算の方法は最善策て、はな い。 X に 2 が加えられるたびに , 仮数部の ビット ( 精度 ) がいくっか落ちてしまう。そ のためー X ーが大きくなるに従って SIN (X ) は、、て、こばこ〃に ( なめらかて、なく ) なる。 そして一 X ーが十分に大きくなると , X がど の象限にいるかの情報はまったくなくなり , 円の周りを回った回数の情報さえもなくな る可能性がある。それならば X から 2 の倍 数を引いたとしても , これ以上は悪くはな らないと思ラかもしれないが , 実際には悪 くなることもあるのだ。 よく考えないて、換算角度を選択してしま うと , 関数がて、こばこになったり不連続に なったりするのて、 , 関連した角度について の多くのサインやコサインを計算したとき の結果がとんて、もないものになることもあ こて、はこれ以上は踏み込まない。し ばしば見逃す問題を警告したかっただけだ。 SIN ( X ) の級数表現に話を戻そう。私たち 経済化へのテクニック が現在考えている区間て、の SIN ( X ) の最大値 は SIN(7t/4), すなわち SQRT(1/2) または 約 0.7071 て、ある。 X15 の項は 45 番目のビット あたりに , X17 の項は 54 番目のビットあたり に , X19 の項は 63 番目のビットあたりに影響 している。浮動小数点数表現を IEEE754 標準 に適合させているモダンなコンヒ。ュータ上 て、倍精度の計算結果を得るためには 53 ビッ トの精度が必要だ。 ということは最後の項以降 , つまり X19 の 項以降を落としても何の問題もないという ことだ。そして X17 の項はどうかわからない が , X15 の項は間違いなく保持しなければな らないだろう。 偶数次の項にかかる係数 0 をすべて取り除 くために , 多項式を X * P (X2) と書き換える ことがて、きる ( かっそうすべきだ ) 。その場 合対象とする区間全体て、必要な精度を得る ためには , X2 についての 8 次の多項式が必要 になる。しかし残念なことに , 最高次のひ とつかふたつの項は引数値が最大の場合だ けに必要なのて、ある。この式による近似値 は元の値にすばらしく近い。誤差はすべて 区間端に集められている。 ご想像どおり , これは関数のベストな近 似て、はない。目的のカープに沿って上下に うねるような近似を選択するのが理相だ 諸し、、 0 そして上下へのうねりはすべてほば同量 ( 少 量 ) の偏差て、あるべきだ。理想かそれに近い カープを発見するテクニックがある。しか しそれは大仕事になる。て、も簡単な仕事て、 済むからといって高次の項を切り捨てた級 数を使うと , このように精度が悪い。これ らの中間はないのだろうか ? もう少し仕 事を増やしたら , もっと経済的な近似が得 られるのて、はないだろうか ? 無理な相談て、はない。、、経済化〃と呼ばれ るテクニックは関数の適切な近似て始まる。 そして , そこからいくつかの高次の項を削

2. 月刊 C MAGAZINE 1992年1月号

d 血 c 嚇解講座・ llello 刪面 arg_state を状態配列として指定します。 char *valloc(int i) ・ イバを使って , 複数のファイルをいったん 複数の独立な乱数列を使いたい場合は , i バイトの領域を , 仮想記憶のページのち ひとつのファイルにまとめた上て、 , それを 複数の状態配列を initstate( ) て、それぞれ初 ようど先頭からはじまるようなアドレスて、 compress を使って圧縮するのが普通て、す。 期化しておき , setstate ( ) て切り換えること 割り当てて返します。 圧縮には Lempel-Ziv 法を用いているの がて、きます。 なお , djgcc のページサイズは 4096 バイト て、 , 速度が高く , 圧縮率も悪くありません 呼び出し前に使われていた状態配列のア て、す。 ( LHA ほどて、はありませんが ) 。 ドレスを返します。 ただし , 圧縮の際のビット数の上限をデ アップデート情報 フォルトの 16 ビットにすると , 大量のメモ remove. C リを消費するのて、 , MS ー DOS 上て、は従来は int remove(char * f) ・ djgcc の patch4 て、配布ファイルに加えられ compress を実用的に使うことはて、きません パス名 f のファイルを消去します。成功し た compress は , UNIX て、は標準的なファイ て、した ( ビット数を落とすと圧縮率が下がっ たら 0 , 失敗したら一 1 を返します。 ル圧縮ツールとして使われています。フリ てしまいます ) 。 ーソフトウェアを圧縮して配布する場合も , djgcc の compress は g032 上て、動くため , valloc. c tar または shar などの圧縮機能のないアーカ のメモリの制約にとらわれずに , UNIX と同 ( ここて、は int とする ) , このマクロを List A ー②のよう より信頼性の高い方法となるて、あろう。 * 2 typeof とは別に , 式の 型を参照する方法として , に安全に定義することがて、きる。 builtin classify type( ) 5.3 typeof による型の参照 という , コンパイラ組み込 この埋め込み文は定数式中ては使用て、きない。列 み関数を使う方法もありま 挙型定数の値 , ビットフィールドの幅 , 静的変数の 式の型を参照するための方法として , typeof を使う す。この関数は , 引数の型 の種類に応じて値を返す , 初期値などは定数式て、ある。 方法もある ( * 2 ) 。 typeof を使うための構文は sizeof と というものて、す。この評価 はコンパイル時に行われま オペランドの型がわからない場合て、も同じことが 同じて、あるが , 働きは typedef 定義された型名と同じ す。 可能てあるが , この場合には typeof または型の命名を 具体的には , この関数は て、ある。 次のような値を返します。 使用する必要がある。 typeof の引数を記述するには式を与える方法および void 型 : 0 整数型 : 1 型を与える方法のふたつがある。式を使用した例を 5.2 式の型の命名 文字型 : 2 列挙型 : 3 示す。 フ・一一ノレ雪 ! ・ 4 typedef 宣言に初期化子を記述することによって , typeof (x[0] ( 1 ) ) ポインタ型 : 5 6 この例て、は x が関数型への配列型て、あると仮定して 式の型に名前をつけることが可能て、ある。力の型に オフセット型 : 7 〃の召という名前をつけるには次のようにすればよ いる。この記述によって表現される型はその関数の 実数型 : 8 複素数型 ; 9 返り値の型て、ある。 関数型 : 10 メソッド型 : 型を引数に与えた場合の例を示す。 typedef 4 襯召 = 召工カ ; 構造体 ( レコード ) 型 : 12 この機能は式の中て、の文とともに使用すると便利 共用体型 : 13 typeof (int * ) 配列型 : 14 て、ある。安全な最大値マクロを次のように記述して , この記述によって表現される型は int 型へのポイン 文字列型 : 15 16 すべての算術型に対応て、きるようにすることが可能 タて、ある。 ファイル型 : 17 もし ANSI C プログラムからインクルードされた場 他の言語依存の型 : てある。 18 この中には , C や C 十十以 合にも動作しなければならないへッダファイルを書 #define max(), b) \ 外の言語用に予約されてい るものも含まれています。 ({typedef ta= (a) , typeof_ を使 いているならば , typeof の代わりに これらの値は列挙型とし 用しなければならない。 typeof 形式は , typedef 名が tb b=(b) ; て typeclass. h< 定義されて ta a = (a ) ; いるのて , このヘッダをイ 使用て、きる場合ならどこて、て、も使用することがて、き ンクルードすれば , 具体的 な数値て、なく void type_cl 局所変数名をアンダースコアて、始めているのは , る。たとえば , 宣日 , キャスト , あるいは sizeof や t ass ( = 0 ) のようなシンポ ype 。 f の引数の中て使用することがてきる。 リックな表現も使えます。 a や b と置換される式の中に出現する変数名との衝突 たとえば , を防ぐためて、ある。将来新しい構文を導入して , 初 ・次の例て、は y を x の指す型と宣言する。 #include <stdio. h> 期化後にスコープが始まるような変数を宣言てきる typeof ( * x) y : main ( ) ようにしたいと考えている。このような衝突を防ぐ ・次の例て、は y を x の指す型の配列と宣言する。 djgcc 詳解講座・ He110 GCC World 77

3. 月刊 C MAGAZINE 1992年1月号

簒 のを防ぐためて、ある。 の変わるヘッダファイルを書く必要がある場合に は , これらふたつのマクロの両方を調べて , 以下 このオプションが指定されると , GNU C 十十と A の四つの場合を区別しなければならない T&T C 十十との違いにかかわる警告も出力される。 しかし , C 十十の言語仕様と ANSI のドラフトとて、 GNU C, 伝統的 GNU C, 非 GNU ANSI C コン パイラ , 非 GNU 伝統的 C コンパイラ は , 構文的に同じ構造が異なった意味に解釈される のて、 , この警告はあまり実際の役には立たないだろ ・既定義マクロ cplusplus は , C 十十 2.0 て、のコン パイルを識別するために定義される。 C 十十 1.2 て、 う ( この理由から , 現在のところ , このフラグは完全 に実装されていない ) 。 は , この識別マクロとして c plusplus が使われる。 GNU C 十十は Ver. 2.0 のシンタックスを実装して いるのて、 , 前者が定義され , 後者は定義されない -traditional' 伝統的な C コンパイラをサポートしようとする。具 また , GNUC 十十独自の機能が条件下て、使えるよ 体的には , うに , マクロ GNUG が定義される。 ・すべての extern 宣言はたとえ関数定義の中にあっ ても大域的に扱われる。関数の暗黙的宣言も同様 を一 0 ' て、ある。 最適化を行う。最適化コンパイルは大きな関数に 三対して時間およびメモリをより多く必要とする。 ・予約語 typeof, inline, signed, const および volat ile は無効となる ( この場合て、も代替の予約語ー typ -O を指定しない場合 , コンパイラの目的はコンパ inlinie などは有効て、ある ) 。 : イル時のコストを減らし , デバッグ時に予期された eof ・ポインタと整数の比較は常に許される。 三結果を出すことて、ある。実行文は独立て、ある。文と ・整数型 unsigned short および unsigned char は unsi : 文との間にプレークポイントを設定してプログラム 三を止め , 変数値を変更したりプログラムカウンタを gned int へ変換される。 ・範囲外の浮動小数点定数はエラーとならない 三同じ関数中の別の文へ変更したりしても , 結果はソ ・文字列「定数」は定数て、なくてかまわない。文字列 ースプログラムから予期したものになる。 定数は書き込み可能なメモリ領域に置かれる。ま -O なしの場合 ,register と宣言された変数のみがレ : ジスタに割り付けられる。コンパイル結果のコード た , 同一の文字列て、も別々の領域を割り当てる。 ・ register 宣言された自動変数はすべて longjmp て、保三は一 O なしの C 十十 / PCC によるコードよりやや悪いも 存される。通常 GNU C は ANSI C に従う。つまり ! のとなる。 volatile 宣言されていない自動変数は破壊されるこ ー O が指定された場合 , コンパイラはコード量およ 三び実行時間を減少させることを試みる。 とがある。 ・プリプロセッサにおいて , コメントは空白に変換三後述する -f オプションのいくつかはある種類の最適 されずにまったく取り除かれる。この機能によっ三化を許可 / 禁止する。 て伝統的な字句連結が可能となる。 ・プリプロセッサにおいて , マクロ引数はマクロ定 義の文字列定数の中て、も解釈されるにの場合引数三デバッグ情報を DBX 十フォーマットて、出力する。 は引用符なして、文字列化される ) 。プリプロセッサ三このオプションをつけてコンパイルされたプログラ ては文字列定数は改行文字て、常に終了する。 : ムは , GDB を使って C 十十ソース言語レベルて、デバッ -traditional が指定されると既定義マクロ STDC 三グすることがて、きる。 C 十十のスコープ解決 , メンバ は定義されない。しかし GNUC ーは定義され : 関数 , 仮想関数 , 静的クラスメンバ , インライン関 る ( なぜなら GNUC の拡張機能は -traditional によ三数 , メンバへのポインタなどを , C のデバッガて、扱う らず有効だからて、ある ) 。ー traditi 。 na によって動作三のと同じ方法て、自然に扱うことが初めてて、きる。 46 C M AGAZIN E 1992 1

4. 月刊 C MAGAZINE 1992年1月号

特集いと G にバッフアへのポインタ , 第 2 引数にバッフ アのサイズ , 最後の引数に書き込み回数を 指定する。実際に書き込んだバイト数を得 るには , 41 行目にある iocount ( ) というメソ ッドを使う。 File クラスには , 直則に行った read や wr ite て、読み書きしたバイト数を保存する変数 が用意されており , iocount ( ) は , その変数 の値を返すメソッドなのて、ある。何やら , 「結果をグローバル変数に書き込んて、おい て , 後て、読み出す」のに似ているような気も するが , 保存がオプジェクトごとに行われ る点が , 決定的な違いて、ある。 書式っき出力も , 当然サポートされてい る。 FiIe クラスの書式っき出力は , form と いうメソッドて、あり , printf などとまったく 同じように使用て、きる。 入力のためのメソッドは , 少し増えて 6 個 になっている。 C て、の fread と fscanf に対応したメソッドに ついては , いわずもがな , といったところ て、あろうか。それぞれ , read, scan という 関数て、ある。引数については , write, form と同様て、ある。また , iocount() て、 , 読み込 んだバイト数を得ることもて、きる。 List 9 の 68 行目の , FiIe& get (char& c) ・ が , 1 文字入力のためのメソッドて、ある。 fg etc と異なる点は , 入力文字をリターン値と して返すのて、はなく , 引数とされた変数に 直接代入してしまうことて、ある。引数につ いている、、 & クは , そのための特殊な引数渡 しを指定するものなのて、ある。 C に限らず , おおよそ A1g01 系の言語一般 て、は , ルーチンへの引数として , 通常 , そ の「値」のみが渡される。引数は , 値をコピ ーされた「別の変数」なのてある。それゆえ , ルーチンがその内部て、引数の値を変更して も , 渡した側の変数には何の変化も起きな C 十十においても , 通常は当然「値渡し」が 行われているのだが , 場合に応じて , 「参照 渡し」と呼ばれる方法をとることがて、きる。 引数に「参照」が渡されている場合 , 関数内 て、使用される引数は値をコピーして作った 新たな変数て、はなく , 呼び出し関数の変数 そのものになるのて、ある。 ということは , 関数内て、引数の値を変更 すると , 呼び出し関数の変数の値も変わる ことになる。「参照渡し」の指定は , 引数の 型を記述する際 , その末尾に演算子 & をつけ るだけて、よい get(char& c) ; は , プログラム中て、は , 次のように使うことになる (file は FiIe 型のオ プジェクトとする ) 。 Char C ; file. get (c) ; / / 変数 c に文字が読み込まれる 「参照渡し」実現のメカニズムは , 至極単 純て、ある。実際に引数として渡されるのは 変数のアドレスて、あり , 関数側て、は , それ をポインタとして変数にアクセスしている だけなのてある。 C て、も中級以上のプログラ マて、あれば必須のテクニックて、あろう。も ちろん , C と比較して , 格段に書きやすくな っていることは確かなのだが。 File クラスには , fgets 系の文字列読み込 みメソッドが三つもある 0List 9 の 76 ~ 78 行 目をご覧いただきたい。いすれも , 読み込 んだバイト数を得るためには , iocount ( ) を 使えばよい get と getline のふたつは , 同じ型の引数を 持ち , また , その引数の意味するところも まったく同じて、ある。 第 1 引数がバッフアへのポインタ , 次の引 数が読み込む文字列の最大の長さて、ある。 通常は , バッフアの大きさを与えることに なろう。最後の引数が , 文字列の区切りを 指定するものて、ある。「 = ' \ n ' 」と書かれて いるのは , この引数が省略された場合 , こ の値を使用しなさい , という「デフォルト引 数」の指定て、ある。すなわち , これらの関数 は , 2 引数関数としても , 3 引数関数として も , 使用て、きるのてある。 プログラム中て、は , char s [100] ; get(), 10 の ; 以下のようになる。 このように第 3 引数を省略したときは , 改行 コードまて、 ( つまり 1 行 ) を読み込み , char s [ 100 ] ; のようにすべての引数を指定すれば , その キャラクタまて、にの場合タブコードまて、 ) を読み込むわけて、ある。 get と getline の違いは , 区切り文字を読み 込むか否か , という点にある。 get て、は , 区 切り文字はバッフア中に読み込まれないが , get ⅱ ne は区切り文字まて、含めてバッフアに 格納する。 もうひとつの文字列読み込みメソッド FiIe& gets (char * * s, char termina tor は , バッフアを指定しなくても , ヒープ領 域に勝手にバッフアを確保してくれるもの て、ある。第 1 引数が「参照型」風に使われてお り , 次のようにして確保したバッフアへの ポインタを得ることがて、きる。 char* S ; gets (&s) ; / / 変数 s にバッフアへのポインタが入る この第 1 引数をなぜ、、 char * & s" と参照 型にしなかったのか , 筆者にはわかりかね る。単に趣味の問題て、あろうか ? 何か理 由があるのかもしれないが・・・ いずれに しろ , 読み込みを何度も繰り返し行うタイ プのプログラムて、は , 前 2 者のほうが有利て あることはいうまて、もない FiIe クラスは , ある程度のエラー処理を自 前て、行う ( あまり期待されても困る。あくま ても「ある程度」なのて ) 。 OOP の基本は , オプジェクトが自身てこ なせるような間題は , なるべく上位のプロ 工ラー処理 特集 C 十 + と DJGPP 57

5. 月刊 C MAGAZINE 1992年1月号

のようにしてファイルをオープンしておく 必要がある。 やすい いるものよりもずっとわかりやすく , 使い ストリーム関数やファイル関数て、使われて 合の挙動などを指定するものて、あるが , C の のモードや , ファイルが存在しなかった場 る。これらは , ファイルをオープンする際 cess mode というふたつの引数を必要とす これは , ファイル名のほかに , io mode, ac が , もっとも利用頻度の高いものて、あろう。 m, access mode a) ・ File(const char * filename, iO mode ふたつめのコンストラクタ , 10 mode としては , 次の 5 個の定数が使用 て、きる ー 0 append appendonly readwrite writeonly readonly 読み込み専用 書き込み専用 読み書き両用 追加専用 読み込み , 追加両用 いずれも読んて、字の如して、 , 説明するのも 恥ずかしいくらいて、ある ()o append が読み 込み可能て、あることは , ちゃんとソースに 注釈がついている ) 。 C の記号定数て、行われ ているような変な省略はない もっとも , C におけるネーミングの省略 は , 識別子の長さに厳しい制限があった時 代の名残りて、あって , 別段 C 自体に罪はない のだが ( いまだに関数名や記号定数など , 省 略することを美徳と勘違いしている C プログ ラマは多いが , 最近のコンパイラの識別子 の長さを考えれば , ナンセンスとしかいいよ うがない ) 。 一方の access mode に指定可能な定数は , 以下の四つて、ある。 a createonly 新しくファイルを作る。ファイルがす て、に存在している場合はエラーとなる。 a create 新しくファイルを作る。ファイルがす て、に存在している場合はその内容を破 56 C MAGAZINE 1992 1 棄し , 新たなファイルとして扱う。 a useonly 既存のファイルを利用する。ファイル が存在しない場合はエラーとなる。 既存のファイルを利用する。ファイル が存在しない場合は新たに作成する。 し , create することも ( 当たり前だが ) 不可能 ロノし日 みき両用に設定することはて、きない てある。 なモードは io readonly と io writeonly のみ 御は引数 io mode による。ただし , 指定可能 し , 書き込みも可能て、ある。読み書きの制 字列を入れておいて読み出すこともて、きる のみを用意するものて、ある。バッフアに文 ファイルとの関連づけを行わず , バッファ 最後のコンストラクタは , ディスク上の 利て、ある。 めのオプジェクトを作ったりするのには便 るようにするものて、ある。標準入出力のた し , これを File 型のオプジェクトとして扱え ム関数て、使用される FILE * 型の変数を指定 5 番目のコンストラクタは , C のストリー 使用されるものて、はないだろう。 ode を指定せねばならない。それほど頻繁に 同様 , オープンしたときと矛盾しない io m ものて、ある。第 2 引数には , C の fd 叩 en() と ドル ) を指定して , インスタンスを生成する ンされているファイル記述子 ( ファイルハン 第 4 のコンストラクタは , すて、にオープ かもしれない こちらに慣れている人には扱いやすい もわかりやすいとはいいがたいものなのだ 、、 w クなどの文字を組み合わせた , お世辞に る。 fopen ( ) て、のアクセスモードは , 、、ドや などと同様の文字列によって行うものて、あ は , アクセスモードの指定を , C の fopen() ar* m) FiIe (const char * filename, const ch 第 3 のコンストラクタ 定義されている。 io mode と access mode は , Fmodes. h て、 て、ある。「データがたいした量て、はないの て、 , テンボラリファイルを使うのは懸命て、 ない」ような場合に , ファイルとの入出力を 前提としているクラスを「騙す」ような使い 方もて、きる。 これらのコンストラクタと対応する叩 en 関数が 4 種類用意されている。 List 9 て、は , 28 行目から 31 行目にあたるが , とくに説明 の必要はないて、あろう。それぞれ同じ引数 をもつコンストラクタの解説を参照してい ただきたい ( すて、にお察しの方もあろうかと 思うが , コンストラクタ達は , 内部て、それぞ れの open ( ) メソッドを使用している ) 。 さて , 次は FiIe クラスの入出力用メソッド をひととおり眺めてみたい ますは出力用のメソッドて、あるが , FiIe ク ラスて、は 4 種類が用意されている。 1 文字の出力を行うのが , 69 行目の put ( c har c) : て、あり , 文字列を出力するのが , 75 行目の put(constchar* s) ; て、ある。そ れぞれ , C のストリーム関数 fputc, fputs に 相当する。 このように , C 十十て、は , 同じ関数名て、あ っても , 引数の数や型に違いがあれば , 別 の関数としてきちんと識別される。 C におい てプログラマは , 関数名の衝突を避けるた めにたいへんな努力を要求され , それが C に おける難解な関数名のひとつの要因て、もあ った そういう無意味な努力は , C 十十において は大分解消されている ( なくなったわけて、は ない。クラス名の衝突には気を配らねばな らないし , 関数名以上に衝突の材科となり 得る記号定数については , いまだに野放し て、あるからだ ) 。 バイトサイズて、の出力を行うのが write ( v oid* x, int sz, int n) ; て、ある。これは , C の fwrite に相当する関数て、あり , 第 1 引数 ファイルとの入出力

6. 月刊 C MAGAZINE 1992年1月号

djg 詳解講座・ llello 刪Ⅷ List 2 0 29 : 30 : 32 : 33 : 34 : 35 : 36 : 38 : 40 : 41 : 42 : 43 : 45 : 46 : 47 : 48 : 49 : 十 } else if(strcasecmp(cp, ” zcat ” ) ニの { + #else if(strcmp(cp, ” uncompress") do—decomp = 1 : } else if (strcmp(cp, ” zcat ” ) zcat—flg ニ 1 ; do—decomp = 1 ; + #endif / * GNUDOS * / 39 : * * * 587 , 595 * * * * ー 593 , 611 ー exit_stat if (do—decomp) { 44 : + #ifdef GNUDOS = の { ニの ( / * 8 十 3 type case—insensitive / * Check for . Z suffix * / / * DECOMPRESS ION * / fi lenames 十 十 十 十 十 if (strcasecmp(*fileptr + strlen(*fileptr) ー 1 , ” Z ” ) ! = の { / * No . Z: tack one on * / strcpy(tempname, *fi leptr) ; if ((cp=rindex(tempname, ' / ' ) ) ! = NULL) cp + + : else cp = te 叩 name; compress の改造 この問題を解決するため , 圧縮後のファ イル名がもとの名前と重ならずに makefile → makefile. Z rensaill . txt → rensaill . txZ となるように compress を改造してみまし た。 rensaill. txZ を uncompress すると rens aill . txt て、なく rensaill . tx になってしまう のが悲しいて、すが , これて、 ( Z て、終わる名前 のファイルを除いて ) どんな名前のファイル にも対応て、きるようになるはずて、す。 また , この改造と同時に , compress の起 動ファイル名の比較方法を変更して , 8 文字 0 5.11 構成子式 る初期化の例を List D に示す。 て、は定数て、なくてよい。実行時に変化する要素によ を用いて記述している。これは List E ー③のように書 れているとき , List E- ②の例は struct f00 を構成子 List E ー①のように struct f00 と structure が定義さ ればならない る。型は構造型 , 連合型 , 配列型のいずれかて、なけ よって指定された値を要素に持っオプジェクトとな の値はキャストによって指定された型 , 初期化子に 期化子のついたキャストのような形をしている。そ GNUC は構成子式をサポートする。構成子は初 いた場合と等価て、ある。 GNU C て、はコンパイラの関数呼び出し最適化を 5.12 関数の修飾子宣 者は通常の C の初期化と同じて、ある (List E ー⑤ ) ( * 3 ) 。 って、ある。前者は switch 文より遅いて、あろうし , 後 クセスすることと配列変数を初期化することのふた この場合の正しい使い方は 2 通りしかない。要素をア 成子式は左辺値て、なくなるためあまり便利て、はない。 配列構成子の要素が単純な定数式て、ない場合 , 構 がて、きる (List E- ④ ) 。 左辺値て、あり第一要素へのポインタへ変換すること 子の中に使うことがてきる。この場合 , 構成子式は 純な定数式から成っている場合 , 構成子式を初期化 配列を構成することもて、きる。構成子の要素が単 Li st D 定数でない初期化 2 : f00 (float f, float g) float beat—freqsC2] = ( f-g, f + g } ; 構成子式 5 : 4 : Li st E ① ② ③ ⑤ ④ struct f00 (int a; char bC2];) structure; char * * f00 = (char * ロ ) { "x" structure = temp; struct f00 temp = {x + y, a structure = ((struct f00 ) (x + y, output = ((int ロ ) ( 2 , x, 28 )) [input]; 助けるような宣言を関数に対して行うことがて、きる。 abort や exit に代表されるいくつかの関数は戻って こない。このような関数は volatile と宣言されるべき て、ある。たとえば , extern volatile void abort ( ) ・ この宣言は abort 関数が戻ってこないと仮定してよい とコンパイラに教えている。 この宣言により出力されるコードはわずかに効率 のよいものとなるが , それよりもっと重要な効果は 変数が初期化されていないなどのにせの警告が出な くなることて、ある。 * 3 これは前者の例てす。 switch 文て書くと switch(input) { case 0 : output = 2 : brea k case 1 : output = x : brea k case 2 : output = 28 : break などとなります。 djgcc 詳解講座・ Hello GCC World 81

7. 月刊 C MAGAZINE 1992年1月号

特集いと G ・自動変数が初期化されずに使用された場合 の独特の機能を使うことについての , より詳しい情 この警告は最適化コンパイルを行った場合にの 報は GDB のドキュメントを見てほしい。 み可能になる ( 最適化時にのみ計算されるデータフ System V 環境下て、の仕事を強いられている人々の ロー情報を必要とするため ) 。 -O を指定しなかった ための仕事もいくつか進められている。これは GNU 場合 , 単にこの警告は出力されない C のソースコード (GNU C 十十のほとんどは GNU C この警告はレジスタ割り付けの候補に対しての からの借りたコードて、ある ) を大量に変更する必要が み出力される。したがって , volatile 宣言された変 あるのて、 , これらの変更は , このリリースて、はサポ 数 , アドレスが取られた変数 , または大きさが 1 , ートされていない 2 , 4 , 8 バイトて、ない変数に対しては出力されな このリリースて、は , コンパイラに多重継承 , 静的 い。また , 構造体 , 共用体 , 配列変数に対しては , メンバ関数 , そして C 十十のリンケージて、宣言された それらがレジスタに割り付けられていても出力さ すべての関数が随時に自動的に多重定義されるなど れない。使用されない値の計算のみに使用される の , 多くの機能が追加されている。デバッガは , 実 は , 多重継承の構造においてどのようにメンバを探 : 変数に対してもこの警告は出力されない。そのよ うな計算は警告の出力以前にデータフローから取 索するかを知らず , 静的メンバ関数を理解てきない り除かれるためて、ある。 そして , a. out シンポルテープル中て、の監視のために 誤りがあるように見えても正しく動作するコー グローバル多重定義関数の実装はとても難しい ドすべてを検出て、きるほど GNUC 十十が賢くない れらの新しい機能を使うなら , デバッガが正しく扱・ えないコードがて、きてしまうかもしれないことに気三 ため , この警告はオプションになっている。たと えば次のようなコードが該当する。 をつけてほしい そして , GNUC 十十は , このデバッグ機能をより 生かすために , ほかの多くの C コンパイラと異なり , int X ; switch (y) ー g と一 O の同時使用を認めている。最適化されたコー ドはしばしば驚くべき結果をもたらすことがある。 たとえば , 宣言した変数がなくなってしまうとか , case 1 : x = 1 ; 制御の流れが予想もしなかったところへ行ってしま break ; うとか , ある文は値が定数だったりすて ' に近くて計 case 2 : x = 4 , 算されているため実行されないとか , ある文はルー break ; プの外へ移動されてしまい異なる場所て、実行される case 3 : x = 5 ; など。それて、も , 最適化された出力をデバッグする ことが可能となる。これにより , バグが潜んて、いる f00 (x) ; 可能性のあるプログラムのコンパイル時に最適化を 行うことが当たり前のこととなる。 y の値が常に 1 , 2 , 3 のどれかて、あるならば x は常 に初期化されるが , GNUC 十十はそれを知るすべ もうひとつのよくある例 , がない int save_y , if (change_y) save_y =y, y =new_y , if (change_y) y =save_y , デバッグ情報を古い GDB フォーマットて、出力す る。このオプションは時代遅れとなった。 以下の事例に対する警告メッセージを出力に加え 特集 C 十十と DJGPP 47

8. 月刊 C MAGAZINE 1992年1月号

FUJITSU lnformation from C0mpiler Makers High C CompiIer Vl. 7 ・従来 1 冊だった「 High C Comp とえば main. c と sub. c のふたつのフ High C Compiler の その他 レベルアップ iler ユーザーズマニュアル」が言 ァイルからなるプログラムのコン 語マニュアルとライプラリマニ パイルおよびリンクは , TOWNS 用のライプラリ (TBIO High C Compiler が VI. 4 から ュアルの 2 分冊になりました。 S. LIB ) などは従来どおり提供され VI. 7 にレベルアップしました。 hC386 main. c sub. c の一操作て済みます。 ・コマンドオプションや , 関数仕 回はレベルアップ内容を中心に ますが , TownsOS V2.1 対応に従 様をまとめた , クイックリファ その概要をご紹介します。 って , いくっかの関数の拡張がな ライプラリ レンスが付属します。 今回のレベルアップ内容のおも されています。 High C VI. 7 ては , ANSI 準拠 な点は , 以下のとおりてす。 Table 1 変更された実行プログラム名 ライプラリ , MS-C 互換ライプラリ ・コンパイラドライバの提供 コンバイラドライバ HC386. EXE を中心に , ライプラリの拡張がな ・ライプラリの拡充 リアル版コンバイラ HCD386. EXE 言語仕様の拡張 されています。関数の数は , VI. ネイテイプ版コンノヾイラ HCD386P. EXP 4 の約 190 に対し VI. 7 ては 500 を越 TabIe 2 DOS コール関数 (dos. h) コンバイラドライバ えています。 関数 VI. 7 て、拡張されたおもな関数 VI. 7 から , コンパイル , リンク dos allocmem を一括して実行する , コンパイラ は , 以下のようなものがあります。 dOS close dOS creatne 、Ⅳ ドライバが付属します。これによ このうちヾ dos. h 〃て定義されてい dosexte 「「 る , DOS コールと割り込み関係の って , 単独または複数のソースフ dos findfirst ァイルからなるプログラムの作成 関数を TabIe 2 にあげておきます。 dos findnext が , 一度の操作ててきるようにな ・ ANSI 準拠関数のフルサポート dos freemem dos getdate (bsearch, fsetpos, ldiv, memm ります。これにともなって , コン dos getdiskfree パイラの実行プログラムの名称が , dOS g etd rive ・低レベル入出力関数 ( 叩 en, TabIe 1 のように変更されていま dos getfileattr dos getftime す。 close, read, write ・・ dos gettime ・ DOS システムコール関数 ( dos コンパイラドライバは , 特に指 dOS keep 定しなければ , コンパイル後に付 d os open ・ I / 0 操作関数 (inp, outp ・・・ 属のリンカてある TLINK. EXE を dos 「 ead dos setblock ・割り込みサービス関数 ( _getpv 使ってリンクを実行し , ! EXP ″フ dOS setdate ァイルを作成します。リンカに対 ect, setpvect ・・ d os setd rive するコマンドラインオプションは , dOS setfileattr 言語仕様の拡張 dos setfti me コンパイラ本体とは違っているの dos setti me HighCV1.7 ては VI. 4 から次の て , 注意してください。 dos write ような言語仕様上の拡張がされて 詳しくはマニュアルを参照して disable ください。 enable segread ・イタレイターのサポート また , コンパイラドライバはド int86 ・ C 十十のコメントスタイル ( 〃 ) ライバコンフィギュレーションフ int86x ・レジスタ変数のレジスタを指定 ァイル (HC386SET ℃ (F) を参照 intdos intdosx するのて , デフォルトのライプラ する g etpvect リやコンパイルオプションの設定 ・ short enum, long enum のサポ getrvect などは , このファイルをて記述し setpvect ・任意基数の定数 ておくことがてきます。 setrvect setrpvect 正しく設定されたコンフィギュ ・ Huge ポインタ レーションファイルを使うと , た C M AGAZINE 1 2 1 162 機能 メモリ確保 ( 0X48 ) 八ンドルクローズ ( Ox3e ) ファイルの作成 ( Ox3c , Ox5b ) 工ラーコードの取得 ( 0X59 ) ティレクトリのサーチ ( Ox4e ) ディレクトリのサーチ ( Ox4f ) メモリの開放 ( 0X49 ) 日付の取得 (Ox2a) ディスク空き容量の取得 ( 0X36 ) カレントドライプの取得 ( 0X19 ) ファイル属性の取得 ( 0X43 ) ファイル日付の取得 ( 0X57 ) 時間の取得 ( Ox2c ) TRS プログラムの設定 ( 0X59 ) ファイルのオープン ( Ox3d ) ファイルの読み込み ( Ox3f ) メモリサイズの変更 ( Ox4a ) 日付の設定 ( Ox2a ) カレントドライプの設定 ( OxOe ) ファイル属性の設定 ( 0X43 ) ファイル日付の設定 ( 0X57 ) 時間の設定 ( Ox2d ) ファイルの書き込み ( 0X40 ) 割り込みの禁止 割り込み許可 セグメントの取得 割り込みの発行 割り込みの発行 ( セグメントつき ) int2 1 コールの発行 int2 1 コールの発行 ( セグメントつき ) プロテクトモード割り込みべクタの取得 リアルモード割り込みべクタの取得 プロテクトモード割り込みべクタの設定 リアルモード割り込みべクタの設定 両割り込みべクタの設定

9. 月刊 C MAGAZINE 1992年1月号

プログラ ング添削 ージ / ヒュージのラーシコードモデル ( コー , こて、配列 b と c の違いに着目しましよう。 b はコンパイル時にあらかじめ大きさがわか ドすなわちプログラムが 64K バイトを越える りますが , c は実行時に入力された大きさに ことのてきるメモリモデル ) てなければなり よって確保する配列て、す。 ません。 プログラムは , 変数 a と配列 b , c のセグメ 実際にコンパイル・リンクを行って実行 ントおよびオフセットアドレスを表示しま 可能ファイルを作成しましよう。プログラ す。私のシステムて実行した結果は Fig. 3 の ムの実行例を Fig. 2 に示します。 ようになりました。 さて C の関数 rank は , 成績の順位を求める この実行結果から , プログラム実行時に ものて、す。関数自体は正しいようて、す。て 大きさを決定し確保する配列は , そうて、な すから , BASIC と C のリンクがうまくいって い配列と扱われ方が違うことがわかります。 ことがわかります。 マンティスさんは , ポインタのオフセッ マンティスさんのプログラムては , 配列 しかし暴走をすることなくきちんとプロ トのみて勝負しようとしたのてすが , これ a, r, count の先頭要素のアドレスを渡すた グラムが実行てきることなどから , リンク て、はまずいことがわかります。すなわちデ ( 関数の呼び出しなど ) というよりも「引数 めに , それらを VARPTR( ) 関数て調査し ています。それらの値を adderA, adderR, フォルトのデータセグメント ( 通常の変数が の受け渡いに問題があると推測てきます。 格納されるセグメント ) 以外のデータを正し adderC の各変数に格納しています。これら の値はちょっとおかしいようてす。 く扱うためには , セグメント・オフセット List 3 のプログラムて考えてみましよう。 の両方て正しく指す必要があります。 マンティスさんのプログラム ( C 部 ) QuickBAS ℃の引数渡しは , 基本的に 参照渡し (pass by reference) で行われ ます。 Quick に限らず C では , すべての 引数渡しを値渡し (pass by value) で行 います 0QuickBAS ℃で値渡しを行うと きには , BYVAL の指定が必要となりま す。 配列のアドレス 関数が正しく動作がおかしい場合は , 関数間の引数の受け渡しが正しく行わ れているかどうか確かめてみよ 引数が正しく受け渡されているかどうか のチェックは簡単てす。呼び出し側と呼び 出される側て、 , それぞれの値を表示すれば よいわけてす。 BASIC ならば PRINT 文 , C て、あれば printf 関数呼び出しを挿入するだけ て、 OK てす。 実行結果は示しませんが , 引数は正しく 渡されているようて、す。渡す引数の値自体 に問題があるようてす・・ Fig. 1 実行ファイルの作成方法 List 0 十レ 1 ー 1 0 ・ 1 十 0 1 」 0 0 れ 0 0 0 0 1 」 E 0 0 0 0 0 0 ・ 1 0 1 よりんれ 0 4 一 0 6 7- 8 9 0 、 1 りな 1 よ 1 14 0 Fig. 2 実行例 A > 生徒数 ? わテストの回数 ? 1 A > 面 1 = ? A ) 面い 2 = ? A > 面 3 A ) 亟 4 A > 面ね 5 = ? A > 面 6 = ? A > 血 7 = ? 四 A > 面ね 9 = ? 1 A > ね 10 ま ? 銀 A > 面 1 10 juni= 0 わね 2 = 20 juni= 0 わ面 3 = juni= 0 A 池忸 4 = 則 j 0 A > 面 5 0 A 〉面 6 = 60 jl.m ト 0 わ面 7 ま 70 jtmi= 0 A 〉 8 = 部 jtmi= 0 A > 面い 9 = 1 jtmi= 0 A > 亟忸 10 juni= 0 A> Am listl, 1i3t1 : A.uL / 0 / 1i8 .0 A > い / 1i8 ロ + li & t2 , 1 tl : ( ※下線部は入力箇所 ) VOID NWL INS REP CI CIJ CA プログラミング添削 147

10. 月刊 C MAGAZINE 1992年1月号

List 2 ル名のあとにつける , という単純な方法を 使っています。たとえば , compress file. C とすると , file. cZ という名前のファイルがて、 きます。 しかし , たとえば , compress makefile rensaill . txt とすると Fig. 2 のようにもとのファイル名と 衝突してしまいます。重複するファイルが 存在するかどうかのチェックは行っている のて , ファイルが消えてしまうことはあり ませんが , このままて、は使いにくいといえ char *cp, *rindex(), *malloc(); 9 : struct stat statbuf; 10 : void onintr(), OOPS(); 11 : 12 : ー 457 , 463 ー int overwrite = 0 : / * Do not overwrite unless given -f flag * / 14 : char tempname[100] ; 15 : char **filelist, **fileptr; 16 : char *cp, *index(), *rindex(), * 110C ( ) ; 17 : struct stat statbuf; 18 : void onintr(), oops(); 19 : 20 : 21 : * * * 489 , 497 * * * * 22 : ー 489 , 503 ー } else ( 23 : 24 : cp = argv[0]; 25 : 26 : + #ifdef GNUDOS / * 8 + 3 type case-insensitive fi lenames * / if(strncasecmp(cp, uncompress 27 : = の { 十 28 : + do decomp = 1 : 可変長配列 1 : FILE *concat—fopen (char *sl, char *s2, char *mode) char str[strlen (sl) + strlen (s2) + 1 ] : 3 : strcpy (str, sl); 4 : strcat (str, (2) ; 5 : return fopen (str, mode) ; 6 : List C 5.8 左辺値でない配列の要素参照 左辺値て、ない配列に対しても要素参照をすること がて、きる ( ただし単項・ & ' 演算子は適用て、きない ) 。次 の例は GNUC ては正しいが , ほかの処理系て、は正し くない struct f00 {int a[4] ; } : struct f00 f( ) ; 配列の長さはその配列が宣言されているプロック に入る時点て、計算され , sizeof て、参照て、きるように配 return f ( ). a[index] ; 列のスコープの間記憶される。 可変長配列名のスコープから脱出した場合にも領 域は解放される。スコープへの飛び込みは許されず , 5.9 void へのポインタおよび関数への 工ラーとなる。 ポインタに対する演算 all 。 ca を使って可変長変数と同様の効果を得ること GNUC て、は void へのポインタおよび関数へのポイ がて、きる。 alloca は GNUC 以外の多くの ( すべてて、は ンタに対する加減算が許される。この機能は void お ないが ) C 処理系て、もインプリメントされている。し よび関数の大きさを 1 と計算することて、行われる。 かし , 可変長配列のほうがエレガントて、ある。 この結果 , void および関数型に対して sizeof を使用 これらふたつの方法の間には , ほかにも違いがあ することが許され , その値は 1 となる。 る。 alloca によって割り付けられた領域はそれを含む -Wpointer-arith' オプションが指定されると , 関数の終了まて、保持されるが , 可変長配列の場合に の拡張機能が使用された場合に警告メッセージを出 は配列名のスコープが終わる時点て解放される ( 可変 長配列と alloca を同じ関数中て、使用した場合 , 可変長 力する。 配列領域の解放によって , その配列より後に a Ⅱ oca に 5.10 定数でない初期化 より割り付けられた領域も同時に解放されてしまう ) 。 aut 。変数に対する集合体の初期化要素は GNU C bar (int index) 80 C MAGAZINE 1 2 1