6f - Essays Room

Last modified on : Oct. 31, 2002.

信じられないCソース

とある企業から、C言語のソースを入手しました。これを最適化し、実行速度をあげると、その結果に応じて、報酬を下さるそうです。

が、ソースを見てびっくり! とてもじゃないけれど、読めたものではありません。現在、私は、その報酬と、私がこうむる精神的苦痛を比べ、手をつけるか否か、「本気で」悩んでいます(笑)。

では、「恐怖のC」の世界へ、一緒に参りましょう。

誤りなどのご指摘などは、掲示板(B)メイル(E)からお願いします。

また、このソースの提供者を特定させない目的で、ソースの変数名など、一部を改変しています。ご了承ください。

ソース内容

ソースは、いくつかのファイルに分かれていた。まぁ、多少なりとも規模が大きくなれば、当然のことだ。

案の定、どれから手をつけるべきか、迷った。というのも、それぞれのファイルが、何のためのものなのか、全く判別がつかないのだ。せめて、仕様書でもあればよいのだが。

仕方がない、片っ端から見ていくか・・・。

1つ目のファイルを開いた。拡張子は.cだったから、これはきっと、Cソースだ。C++ではないだろう(普通、C++の拡張子は.cpp)。で、開いてみて唖然あぜんとした。

コメントがない!!!

いや、ファイルの先頭に、大まかな説明と著作権表示はあるのだが、関数や変数の説明が全くないのだ。かといって、分かりやすい変数名でもない。あちこちに、aやbといった1文字の変数名がひしめいている。はじめはカウンタかと思ったが、そうでもないらしい。私はこれを解読するのか!? い、いかん、早くも頭痛がしてきた。

いや、それ以前に、この関数の定義は何だ?

execute_main(map_main, map_sub, col_map,
        map_out, pat, fp, wid,
        hei, rate, stat)
    MAPDATA *map_main;
    MAPSUB  *map_sub;
    MAPRGB  *col_map;
    MAPDATA *map_out;
    double  *pat;
    int     rate;
    float   *fp;
    int     wid, hei;
    char    stat;
{

ポインタを表す*の位置も気になるし、fpという、名前からは使用目的がわかりにくい謎の引数も気になるが、それ以上に、この書式はK&R C(カーニハン・リッチー)の書式じゃないか!

これって、10年以上前の書式だ。

これ、書かれたのはいつだ? 89年? じゃぁ、仕方ないか。って、修正日が「99年」になっているのは目の錯覚だろうか? 修正者、何をしているんだ・・・。

「10年前」。これを甘く見てはいけない。日進月歩ならぬ、秒進分歩と呼ばれる世界での話なのだ。この比喩が、仮に本当なら、「10年前」は「86万年前」に相当する(笑)。

私は考古学者じゃない。

さて、この段階で、独自の型名が出てきている。その正体を探るために、私は、先頭のほうでincludeしてあったヘッダファイルを開いた。

あまりの素晴らしさ(もちろん皮肉)に、私は目を見張った。最初の著作権表示、適宜、散りばめられた空白行。それ以外のほとんどの行が、#で始まっていた。いや、もっと正確には、わずかなifdefifndefelifelseendif命令を除き、その9割以上がdefine命令

define命令は、最近は嫌われる傾向にある。単純な置き換え命令なので、意図しないバグが簡単に生じてしまうからだ。この人はよほどdefineが好きなのだろうか。私は、このプログラマにDefine Maniaという識別名コードネームを与えずにはいられなかった。

#define MSG_Info    0     /*_F77_s: MINFO */
#define MSG_Debug   1     /*_F77_s: MDEBG */
#define MSG_Warning 2     /*_F77_s: MWARN */
#define MSG_Error   3     /*_F77_s: MERR  */
#define MSG_Fatal   4     /*_F77_s: MFATL */

ま、名前の付け方は95点だ。できれば、マクロ定義は全て大文字という慣例に従って欲しかったが、枝葉末節だから気にせず行こう。が、このコメントは一体・・・?

どうやら、フォートラン77からの移植らしい。いや、それはともかく、define中にコメントを書いてもいいのか、D.M.氏(ディファイン・メイニア氏)は、ちゃんと確認したのかな?(不安になったプログラマに一言。defineの展開とコメントの削除(正確には、スペース文字への置き換え)は、共にプリプロセッサの仕事だが、コメント処理が先に行われることになっているので、大丈夫だ。/**/ではなく、C++の//〜という形式を使ったときでも、コメントはそのマクロ内で完結する。)

しかし、この書式は美しくない。ANSI Cでは、ちゃんとenumが使えるので、活用しよう。

enum {
    MSG_Info,     /*_F77_s: MINFO */
    MSG_Debug,    /*_F77_s: MDEBG */
    MSG_Warning,  /*_F77_s: MWARN */
    MSG_Error,    /*_F77_s: MERR  */
    MSG_Fatal,    /*_F77_s: MFATL */
};

こうしておけば、いざ、定数を一つ追加したときでも、番号がちゃんと、ずれてくれる。

#define MODE_A   1
#define MODE_B   2
#define MODE_C   3
#define MODE_D   4

こんな形でも問題ない。

enum {
    MODE_A = 1,
    MODE_B,
    MODE_C,
    MODE_D,
};

enumは通常、0から数字を割り当てていくが、このように特定の場所でも、数字を与えられるのだ。enumは、K&R Cでは無かったらしい(噂に聞いた)ので、まぁ、defineの嵐も致し方ないが。

次の命令は、なかなか独創的だった。

#define FLAG1_N 0
#define FLAG1_A 1
#define FLAG1_B 2
#define FLAG1_C 4
#define FLAG1_D 8
#define FLAG1_E 0x10

・・・。フラグだから、組み合わせて使うのだろう。でも、フラグなら、普通は16進数か8進数で書くところ。10進数だと、桁が増えたときに見づらくなってしまうからだ。現に私は、何か、フラグ以外の特殊な用途に使うものかと、一瞬、勘違いした。

案の定、いきなり16進数が出てきている。何とも言えずこの最後の1行、無計画性を示唆しているようじゃないか! まるで、この、一貫性のない増改築しすぎのプログラムの縮図だ。そうは思わないかい? ちなみに、他の部分で、

#define FLAG2_E 16
#define FLAG2_F 32
#define FLAG2_G 64
#define FLAG2_H 128

などという部分も発見。D.M.氏は、16進数に恨みでもあるのだろうか。

しかし、ここまでdefineが多いと、うんざりしてくる。

#define WIDTH_MAX 512

const変数に直しておけよ・・・。

#define TYPE_X 1

enumはどうした・・・。

#define NUM_COUNT int

またdefineか・・・。defineは別の形で書き・・・

・・・って、

ちょっと待てーーー!

あああああ!!! 型名をdefineで定義しちゃってるよ、この人!

放っておけば、#define P_INT int *なんてのも、平気で出てきそうな気がする・・・!(え? このマクロの恐ろしさが分からない? それじゃ、P_INT x, y;と宣言してみよう。xint*型で、yint型になるのだ。)

こいつはtypedefを知らんのかッ!

まさに、背筋が凍る思いとはこのことだ。型名の再定義はtypedef。これは基本だよ・・・。

しばらく読み進むと、このファイルは終わった。次は、先頭でincludeしてあった別のヘッダファイルを開こう。

どうやらこっちは、グローバル変数と関数のプロトタイプ宣言のヘッダファイルらしい。が、画面をスクロールさせてすぐ、変な文章を発見した。

union {
    unsigned char *p_char_u; 
    float         *p_float_u; 
    double        *p_double_u; 
    int           *p_int_u; 
    short         *p_short_u; 
} p_union;

何コレ???

実際の使い方を見てみると、unsigned char型ポインタをp_union.p_char_uに放り込み、別の型のポインタとして取り出していた。

・・・何ソレ(怒)!

Cコンパイラは、ポインタの型チェックに厳しい。違う型へのポインタの代入は、エラーになるのだ。が、これはしばしば、必要になる型変換なので、Cにはちゃんと、抜け道が用意してある。こんなの、共用体を使わなくとも、キャストで充分だ。何が哀しくて、こんな共用体を、わざわざ・・・。私も、共用体をこんな用途に使っているのは、初めてお目にかかった。カーニハン博士、リッチー博士が見たら、卒倒するぞ、多分。それに、そんな一時利用の変数は、グローバルにするんじゃない。他の開発者と名前が衝突したら、どうするんだ。

しかもこの共用体、タグ名を書いていない。書いておいて損はないので、普通は書くものだ。_tag???という名前に統一しておけば、他の変数名とタグ名が衝突するという心配もあるまい。個人的には不要の共用体だが、いざ、もう一つ使うときに、タグはあると便利だ。

その後、こんな文章を見つけた。

union {
    unsigned char *p_char_u; 
    float         *p_float_u; 
    double        *p_double_u; 
    int           *p_int_u; 
    short         *p_short_u; 
} p_union_2;

union {
    unsigned char *p_char_u; 
    float         *p_float_u; 
    double        *p_double_u; 
    int           *p_int_u; 
    short         *p_short_u; 
} p_union_3;

ほ〜ら、やっぱり必要になってる。D.M.さん、きっと、タグの存在を知らなかったんだな。まぁ、キャストも知らないんじゃ、無理ないか。

そして次を見た。先ほどもツッコんだが、この共用体をグローバルにしていた理由が分かった。

#define p_u_char   p_union.p_char_u
#define p_u_float  p_union.p_float_u
#define p_u_double p_union.p_double_u
#define p_u_int    p_union.p_int_u
#define p_u_short  p_union.p_short_u

もちろん、p_union_2p_union_3の分も、あった。

そんなに . が嫌いかい?

無駄な構造体を15行。無駄なdefineを15行。まったく、情けないったらありゃしない。

次は関数のプロトタイプ宣言だ。

execute_main( ... );

ん〜? 何か、見たことがある関数名だな・・・。そうそう、最初に見た関数だ。

・・・コラ。引数はドコに消えた!? プロトタイプ宣言が何のためにあるのか、分かっているのか、コイツは・・・。こんなことだと、随所に見受けられるfunction( int arg1, ... );というプロトタイプ宣言、まさかとは思うが、こいつらも引数書くのが面倒だから省略したんじゃないよな? ましてや、コンパイラを黙らせるために省略したんじゃないよな!?(引数の...は、printf関数みたいに、引数の個数が不定の時に使う。決して、書くのが面倒な人のための救済措置ではないし、コンパイラの出すエラーを止めるための回避手段でもない。)

このソース、コンパイルしてまともに動くのが不思議だ。

これを手直しするなんて、正気の沙汰じゃない。他にも、K&R CとANSI Cでは、引数の型の格上げ問題もあり、混在させるのは危険だ。今までの人が、手をつけられなかった理由が、よく分かった。ここまでいい加減な書き方をされたのでは、ソースを修正できない。安心してANSI Cにも移行できないし、このソースはずっとK&R Cで行くしかないのだ。

こんなソースは、人目につかぬよう、会社の恥として厳重に封印すべき。

世の中、探せば色々なものがある。しかし、これはなかなかの破壊力だった。こんなソースでも、動けばお金が貰えるなんて、世の中、何かがおかしいよね。

最後に、

struct line_3d {
    int sx;
    int sy;
    int sz;
    int dx;
    int dy;
    int dz;
    int idx;
    int idy;
    int idz;
};

という構造体が定義されていた、ということだけ記しておこう。

おお、ちゃんとタグを使っている!

え? この11行がどこに書かれていたのかって? それはね、7つあったCソースファイルのうち、4つのファイルに重ねて書かれていたのさ。

ねぇD.M.さん・・・。あなたにとって、ヘッダファイルの存在意義って、何ですか?

戻る(B)
トップ(T) | 掲示板(B) | メイル(E)
Valid XHTML 1.1! このページは正当なXHTML 1.1 文書であると評価されました。
W3CHTML検証サービスを用いこのページを検証(V)