本物のC

礼儀作法:可読性


たとえ同じ機能を実現する為であっても、コードの書き方は複数考えられるのが通例です。

こうした場合の基本にして最も重要な指導原理は可読性(readability)を高める事、即ちより分かりやすいコードを書く事です。

分かりやすさの重要性

分かりやすいコードは弄りやすい

大抵のコードは一度書いて終わりではなく、バグを修正したり機能を追加したりする為に弄り回す必要があります。

一人で小規模なコードを書いている内はどう書いたところで変わらない気がするかも知れませんが、それはコード書いた時の自分の意図を憶えているからです。

多人数での開発や、一人でも数千行規模のコードを書いたり何ヶ月も前のコードを弄ったりする場合、書いた時の意図なんて分からない方が当たり前であり、そこでこそ「分かりやすさ」の価値が顕わになるのです。

分かりやすいコードはバグが少ない

分かりやすいコードは挙動をより正確に把握する事ができます。

バグとは結局プログラマの意図しない挙動(の内仕様で片付けられないもの)なので、分かりやすいコードを書く様に心掛ければテストする前の段階からバグを減らす事ができます。

分かりやすいコードは最適化される

分かりやすいコードは人間だけでなく、コンパイラにもその意図がよく伝わります。

意図が分かればどこを尊重しどこを妥協すべきかもはっきりするので、より適切な最適化が可能となります。

今時のコンパイラは非常に優秀なので、浅知恵でコードを工夫するよりは簡単で分かりやすい書き方をした方が高速になると言っていいでしょう。

どう書くべきか

見た目に美しく

まずは整然とした見た目が大切です。

一貫した命名規則やコーディングスタイルはコードを秩序立たせ、よりぱっと見での理解がしやすくなります。

定石に倣う

「お決まりの書き方」というのは大抵のプログラマにすぐその意図が伝わります。

簡単な例ではn回同じ処理をする場合の

for (i = 0; i < n; i++)
{
...
}

や、変数の値をスワップする場合の

tmp = a;
a = b;
b = tmp;

などです。ループをwhile (n --> 0)で書いてみたり、スワップを XOR で実行したりする事も可能ではありますが、そう書く事に可読性を犠牲にするほどの価値があるかどうかはよく考える必要があります。

また標準ライブラリも十分に利用すべきです。車輪の再発明は原則避けましょう。(勿論よりよい車輪が必要な場合もあります、特にrandなど。)

より大きな視点ではプログラムの設計そのものにも定石があり、基本はモジュール化です。プログラムは明確な機能を持った部分(モジュール)の組み合わせとして構築される事で、全体像の把握が容易になります。

どういったモジュールの組み合わせにより目的の機能を実現するかについては、GoF なる四人組の提案したデザインパターンが参考になるでしょう。

意味を示す

数字一つ取ってもその意味は明確にすべきです。

例えば

Request request[128];
char *buffer[128];
...
for (i = 0; i < 128; i++)
{
...
}

というコードがあったとして、for文の128は何の数字なのかが全く分からない訳です。(プログラム中で唐突に現れる謎の数字はマジックナンバーと呼ばれます。)このfor文ではrequestの要素を一つずつ処理しているのかも知れませんし、bufferを 1 バイトずつ処理しているのかも知れません。頑張ってコードを読めば判ったりもしますが、コードが長くなればなるほどそれは難しくなるのです。

こうした場合、C ではマクロによって名前を付けるのが定石です。

#define N_REQUEST 128
#define BUFFER_SIZE 128
Request request[N_REQUEST];
char *buffer[BUFFER_SIZE];
...
for (i = 0; i < N_REQUEST; i++)
{
...
}

(n は number (of) の略としてよく使われます。)

これならば、ループ処理はrequestの各要素に対して行われているのが明確になります。

また「C の知識:列挙型」で示した様に、数自体に実質的な意味が無いならば列挙型を使う方が有効でしょう。

基本的にはこうしてコード自体で意図が明確になる様にし、それでは不足な場合に限りコメントを書くべきです。具体的には、複雑なデータ構造やアルゴリズムの実装、技巧的にならざるを得ないコードなどです。

/* Calculate greatest common divisor with Euclidean algorithm */
int gcd(int x, int y)
{
...
}