英語のコメント

プログラム中のコメントの書き方。
プログラマにとって、最も代表的な議論の種ではないでしょうか。

コメントの書き方に関しては宗教論的な側面もありますが、あきらかにやってはいけない書き方というのも存在します。

そのようなコメントはソースコードの可読性をさげるだけでなく、オリジナルソース変更時にわけのわかならいバグを誘発したりします。
今回はそんなコメントについての話として、「英語コメント」をとりあげます。

プロジェクトによっては「コメントは英語」と決まっているところもあるかと思います。しかし、英語コメントは非常に危険な側面を持っています。

・英語がわからないのでコメントをかかない。あるいは極端に少ない。
・英語が間違っていて理解ができない。あるいはウソになっている。

前者はまだましですが、後者は悲惨です。こんなコメントならないほうがはるかに良いでしょう。

そもそもなぜ英語なんでしょうか?
プロジェクトに外国人がいるとか海外でも使用するというならわかりますが、あきらかに日本でしか使わない、日本人しか読まない、というものまで英語になっていることがあります。これは無意味です。

プログラマが英語が苦手な場合は、初めから日本語で書いた方が保守性ははるかにあがるでしょう。無意味な規約やプライドなんかは捨てて、素直に日本語でコメントを書きましょう。

【関連リンク】
プログラミングの禁じ手Web版 C言語編 - コメントに関するパターン

ポインタ引数とconst指定

最も賢い者と最も愚かなものだけが、決して変わることがない。
孔子
今回のテーマはconst修飾子。非常に有用な修飾子ですが、意外と使われていない方も多いんではないでしょうか。

constはいろんなところに登場しますが、今回は特にポインタ変数のconst指定について書きたいと思います。

例えば、次の関数プロトタイプがあったとします。
/* position値が有効かどうか調べる */
bool
is_valid(struct position_t* p_pos);
また、この関数を使用する次のコードがあります。
{
  struct position_t pos;
  ...
  if (is_valid(&pos)) {
  ...
}
このis_valid()呼び出しでは、posのアドレスを渡しています。 ポインタ渡しなので、posの内容は書き換えられる可能性があります。 そのため、呼び出し側ではposの値がis_valid()呼び出し後も変化していないということを期待することはできません。 その後も、posを使用するのであれば、posの値をコピーする、あるいはコピーの方をis_valid()に渡す必要があります。

とはいえ、普通is_valid()のような関数はposを書き換えたりはしないでしょう。 そこで、通常は次のように、const指定をつけます。
bool
is_valid(const struct position_t* p_pos);
これで、呼び出し側は安心してポインタを渡せます。

よくコメントで、INPUTのみでしか使いませんと書いてあるのもありますが、それだけでは不十分です。コメントはあくまで補足。INPUT引数には、const指定をつけましょう。

【関連書籍】
CプログラミングFAQ―Cプログラミングのよく尋ねられる質問 スティーブ・サミット
プログラミング言語C ANSI規格準拠 B.W.カーニハン D.M.リッチー
独習C ハーバート・シルト
Cプログラミングの落とし穴 A.コーニグ

計算途中の桁あふれ

最近はHDD容量やメモリ量も大きくなって、32bitで不足する値というものが増えてきました。
32bitで表せるのは2^32(2の32乗)=4294967296(4G)です。
そのため、例えば4G以上のファイルサイズは、32bitではbyte単位で表しきれません。
実際、多くのシステムでは、ファイルシステムやファイルにアクセスする関数で使用する型が、符号付型(signed long)であるために、2G以上のファイルを扱う際にはいろいろと問題が発生します。
この件については、また別で扱うとして、今回は桁あふれのお話です。

先ほどのファイルの話では、2GB以下の場合32bitでも問題ないと書きました。そのため、2GB以下のファイルを扱うプログラムの場合は、この32bit境界を意識しなくても良いかというとそういうわけにはいきません。

例えば、次の例があったとします。

int
calc_ratio(int size1, int size2)
{
assert(size1 < 2 * 1024 * 1024 * 1024);
assert(size2 < 2 * 1024 * 1024 * 1024);

return size1 * 100 / size2;
}

size1のsize2に対する割合(%:百分率)を計算する関数です。
size1, size2とも実用上2G以下しか使わないというassertを入れているので、このシステムではsizeには2G以下しか指定されないのでしょう。

よって、この式は問題ないのでしょうか?

実際は、問題があります。
それは、size1が2G近い値の場合です。
その場合、size1 * 100は32bitでは表しきれない値となってしまい、桁あふれを起こします。その桁あふれを起こした計算結果をsize2で割るので、その結果も不正なものになってしまいます。
対策としては、size1や定数100を64bitでキャストするなどして、計算途中での桁あふれを防ぐことです。

非常に初歩的なバグですが、このようなバグの問題は、意外に後になって発見されることが多いように思います。なぜなら、size1が大きな値にならない限り問題が表面化しないからです。
境界値テストを重要性を物語ってもいます。

charの落とし穴 - 暗黙の型変換と符号拡張

変化というものは、たとえ良い方向に変わっているときでさえ、常に障害と不快がともなう。
アーノルド・ベネット
前回unsignedでよく陥りがちなバグについて触れました。今回はその続編で、char型での落とし穴として、いわゆる符号拡張(sign extension)と暗黙の型変換(inplicit conversion)について説明します。

次のコードの問題点はわかるでしょうか?
typedef char value_t;
#define INVALID 0xff

/* valがINVALIDなら0、それ以外で1を返す */
int
check(value_t val)
{
  switch (val) {
  case INVALID:
    return 0;
  default:
    return 1;
}
一見問題なさそうに思えますが、実際このコードをコンパイルして、valにINVALID(0xff)を指定しても1が帰ってきます。なぜでしょう?

C言語のswitch分では、比較値はint型として扱われます。よって、valがswitchに渡されるときに、valの値はint型に暗黙の内に拡張されます。変換時には符号拡張が行われ0xff(charの-1)は0xffffffff(intの-1:intが32bitの場合)となってしまいます。よって、0xffとは一致しないのです(※1)。

これは上の例の用に、charをtypedefした型と、#defineで定義したマクロ値で、列挙型のような使い方をしようとした時に起こりがちなバグです。列挙型(enum)だとint相当でサイズが4byteになってしまうということで、このような使い方はよく見かけます。回避策としては、value_tをunsigned charでtypedefすることです。unsignedであれば、符号拡張はされないので、bitパターンはそのまま保持されます。

※1 charが符号付きかどうかは処理系依存です。動作することもあります。

【関連記事】
unsignedの落とし穴
テキストとしてのコーディング規約
warningに気を配る

【関連書籍】
Cプログラミングの落とし穴 A.コーニグ
CプログラミングFAQ―Cプログラミングのよく尋ねられる質問 スティーブ・サミット
プログラミング言語C ANSI規格準拠 B.W.カーニハン D.M.リッチー
独習C ハーバート・シルト

unsignedの落とし穴

よく見かけるバグのパターンなんかも、ちょっとずつ紹介していこうかなと思います。
最初のテーマは、符号無し型(unsigned)の落とし穴です。

符号無し型を、符号有り型(signed)と混在させて使い、結構いろんなバグの原因になっているのをよく見かけます。
次の例は、実際に経験された方も多いのではないでしょうか。

typedef unsigned int UINT;

{
UINT data[MAX];
UINT i;

for (i = MAX - 1; i >= 0; i--) {
/* dataの処理 */
func(data[i]);
}
}

実際に、上記のようなコードを書いて実行すると何が起こるでしょう?
答えは、永久ループです。

iはunsignedのため、決して0未満にはならないのです。
i==0の時の、i-1は、-1ではなく、0xffffffff(intが32bitの場合)です。

iがintあるいは他のsignedなら何の問題もないのですが、iの比較対象がunsigned型の定数だったりすると、コンパイル時にwarningが出て、ついついiの型をUINTで宣言してしまうことがあります。
そもそも、逆順に処理している(i--)が問題なのですが、これは事情によってそうせざるを得ないこともあります。

例えば、対応は下記のようになります。

{
UINT data[MAX];
UINT i;

for (i = MAX; i > 0; i--) {
/* dataの処理 */
func(data[i - 1]);
}
}

unsignedには他にもいろいろ注意が必要なので、またおいおい紹介していきます。

使われ方を知る

学問なき経験は、経験なき学問に勝る
イギリスの諺
プログラミング技術は磨くには、先人のソースコードをみるのが一番。
よく言われることです。
最近はGNUをはじめ、素晴らしいオープンソースコードがたくさん手に入るので、その点では、非常に恵まれた環境が整っています。
まさに宝の山です。

さて、この宝の山、使ったことのないライブラリ関数を使う時なんかにも、非常に役に立ちます。
マニュアルやmanページを長々と読むよりも、実際の使われ方を見た方がはるかに分かりやすいってことは良くあります。
その他、こんなクラス作りたいんだけどな、と思ったときの参考あるいは下敷きにもなります。

そんな時に便利なのが次のソースコード検索エンジンです。
Koders - Source Code Seaech Engine
http://koders.com/

(情報元:百式さん)

関数名を調べれば、その使用例、allocator等のキーワードで調べれば実装例なんかが見つかります。
一度は調べてみる価値があります。

ちなみに、gonzuiという和製ソースコード検索エンジンもあるようです。
まだ使ったことはないのですが、なかなか便利そうなので、一度使ってみようと思います。

【関連書籍】
Code Reading―オープンソースから学ぶソフトウェア開発技法 トップスタジオ まつもとゆきひろ 平林俊一
プログラミングテクニック―UNIXコマンドのソースコードにみる実践プログラミング手法 多治見 寿和
GNUソフトウェアプログラミング―オープンソース開発の原点 マイク・ルキーダス アンディ・オラム

ドキュメント自動生成

仕様書書くのって面倒ですよね。
でも、ユーザーへの説明や引継ぎを考えると全くないっていうのも考え物。

Javaだと、javadocっていうのがあって、コメントからhtmlやlatexのドキュメントを自動生成する枠組みが用意されています。
C/C++でも同様のことを実現してくれるのがdoxygenです。
glibcのSTLのコードにも使われたりして、結構普及しているようです。

こちらのサイトにマニュアルの日本語訳と使い方の説明なんかがあります。
http://www.fides.dti.ne.jp/~oka-t/doxygen.html
Let's use doxygen!


使用されたことのない方は一度試されることをお勧めします。

趣味と職業の違い

「職業としてのプログラミング」となんだかすごいタイトルをつけてしまいましたが、私自信まだまだプログラマあるいはソフトウェア技術者としての経験はまだまだです。
なので、そんなにえらそうなことを言える立場でもないですが、今からプログラマを目指そう、あるいは、自分の技術をステップアップしていこうという方にとって、なにかしら役に立つことが書けたらなと思います。

最初に、私が考える、「趣味としてのプログラミング」と「職業としてのプログラミング」の違いについて書きたいと思います。

■趣味としてのプログラミング
私も昔は趣味でプログラミングをしていた時期があります。主に作っていたのは、簡単なゲームなんかです。別にゲームをしたいってわけではなくて、プログラムを書くということが楽しくてやっていました。インターネットもそれほど普及する前で、作ったものをオープンソースで公開するとかいうこともなかったんですが、自分なりに綺麗なコードを書くこと目指してせこせこがんばってました。

このような趣味のプログラミングの特徴を書くとすれば、次のような感じでしょうか。
  • 最も重要な目的は、自分自信が満足すること
  • 自分のやりたいようにできる
  • 作ったものに対する責任はなし
  • 締め切りはなし!
例外もあるでしょうが、だいたいこんな感じではないでしょうか。
趣味なんだから、とにかく自分が楽しいことが重要ですよね。

■職業としてのプログラミング
職業としてのプログラミングは、当たり前ですが就職してからがメインです。就職前も、WebページのCGI等はちょっとだけやってました。ちなみに現在の仕事はいわゆるSEではなく、組み込み機器のソフト屋さんです。

職業としてのプログラミングになると、趣味のころには無かったような、いろんな制約が発生します。具体的には次のようになります。
  • お客さんの満足が第一義
  • 複数人での開発になることが多い
  • 開発を行う上でのルールがある
    (コーディング規約、言語、プロセス管理)
  • 作ったものに対する責任があり、品質確保が必須
  • コスト(時間、お金)に制限がある
職業としてプログラミングをする場合は、このような制約が必ず発生します。
個々について、もう少し詳しく説明しておきます。

○お客さんの満足が第一義
なんといっても、この点が最も違う点でしょう。仕事でやっている以上、お客さんが最も重要なのは言うまでもありません。
そんなの当たり前と思っていても、本人が良かれと思っていることが、案外技術的な自己満足で終わってしまっているというのもよくある話。
与えられた要求仕様を確実にこなすことは勿論ですが、潜在的な要求を見出していくのも、職業プログラマには必要な技術が必要になってきます。

また、時には嫌な(面白くない)仕事だってやらないといけません。

○複数人での開発
大規模なプロジェクトになると、大抵複数人での開発になります。複数人での開発では、進捗管理、リソース配分等が必要になります。勿論的確な意思疎通を行うコミュニケーション能力も必要です。

○開発上のルール
開発のルールといってもいろいろありますが、使える言語が指定されているというところから、コーディングルール、ドキュメント作成など、趣味の時とは違って、自由にならないことがたくさんあります。

○仕事に対する責任
仕事で作成したソフトウェアの多くは、なんらかの対価と引き換えにお客さんの手にわたります。早い話、お金を頂くわけですから、その成果物に対しては、作成者としての責任が発生します。
重大なバグ等は、お客さんへのご迷惑をおかけするだけでなく、製品の回収、あるいはアップデート費用等で会社にも被害を与えます。高品質ソフトウェア開発は非常に重要なテーマです。

○コスト
企業は利益をあげないといけません。社会への貢献も何もかも、つぶれてしまっては何もできないのです。開発作業では、決められたお金、時間の中で開発を進めますが、それぞれの開発者がコスト意識をもって開発にあたらなければ、良いものはできません。プログラミングそのものの技術だけでなく、プロジェクト管理等の技術も必要とされています。

とりあえず、思いつくままだらだらと書いてしまいましたが、おいおい掘り下げた内容を書いていきたいと思います。

ことはじめ

ブログ始めました。

プログラミングに関するTIPSやツールの紹介を中心に、その他雑多なことなんかを書いていこうかなと思います。

とりあえず間借りするblogを決めないといけなかったんですが、これが結構迷いました。
最近は無料blogサービスもたくさんあるんですね。

とりあえず私は、下記のblog等を参考にさせてもらって、FC2に決めました。

http://ebn.livedoor.biz/
【無料ブログ比較なら】まあ待て、ブログを借りる前にここを読め。


決め手は、自由度が高そうなとこでしょうか。
記事意外の部分のいじれるし、アフィリエイトもできるということで。
gooとかアメーバブログもちょっと試したんですが、記事投稿以上のことがあんまりできなさそうだったので落選。
最後に、SeeSaaと迷った結果、FC2の方が軽そうなのでこっちになりました。

せっかく始めたので、時間見つけて気長に書いていきたいと思います。

本のおすすめ

4274065979

4844337858

482228493X

4904807057

4873114799


プロフィール

  • Author:proger
  • 組み込み関係で仕事してます

ブログ内検索