GAIO CLUB

2022年12月02日

【第1回】Cコンパイラは、なぜ怒る?

静的解析/コンパイラ技術
いまさら聞けない 静的解析/コンパイラ技術
C言語プログラムに関するお話をしたいと思います。

C言語に限らず、プログラミング言語には記述ルール、文法があります。C言語の文法を守って書いたプログラムであれば、Cコンパイラは怒りません(エラーを出しません)。

しかし、エラーがなければ正しく動くというわけではありません。なかなか1発で動くプログラムは書けないですよね。有名な格言?「プログラムは思った通りには動かない、書いた通りに動く」、のとおりです。

私が新入社員教育を担当していた時に、素晴らしい例を作ってくれた後輩がいました。
こんなプログラムです。うっかりすると見落としますが、みなさんは気付くでしょうか。
#include <stdio.h>
int A;

void test(void)
{
    if ( -2 < A < 1 )
       printf(" TRUE(A:%d) \n",A);
    else
       printf(" FALSE(A:%d) \n",A);
}
int main(void)
{
   A = 0, test();
   A = -2, test();
   return 0;
}
main関数はtest関数内if条件式の真と偽の処理が通るようにAの値を変えて2度呼び出しています。
このプログラムをコンパイルして動かしてみると、以下の結果が得られます。
FALSE(A:0)
TRUE(A:-2)
しっかりと、if文の真条件と偽条件のprintfが通りました。
main関数の2行目を変えてみましょう。
#include <stdio.h>
int A;

void test(void)
{
    if ( -2 < A < 1 )
       printf(" TRUE(A:%d) \n",A);
    else
       printf(" FALSE(A:%d) \n",A);
}
int main(void)
{
   A = 0, test();
   A = 1, test();   // 代入値を-2から1に変更
   return 0;
}
こちらの動作結果はこうなります。
FALSE(A:0)
FALSE(A:1)
どちらも偽になりました。ダメですね。実は、変更前の出力結果もよろしくありません。
Aが-2より大きく、1より小さい時を真にしたいのだと思いますが、結果は逆になっています。
FALSE(A:0)
TRUE(A:-2)
このプログラム記述には、残念ながら文法的な誤りがありません。あってほしいのですが。。。

そもそも、if ( -2 < A < 1 ) の記述がいけません。気持ちは解りますが、書くなら if ( -2 < A && A < 1 ) ですよね。

C言語文法は、if ( 式 ) ですから、式が書いてあればエラーにはなりません。エラーにならなければ間違いに気付かず、「思った通りではなく、書いたとおり」に動いてしまいます。

では、if ( -2 < A < 1 ) の書いたとおりの動作はどうなるのでしょうか。
( ( -2 < A ) < 1 )
括弧をつけてわかりやすくしました。
先に( -2 < A )を評価し、その結果が真(1)か偽(0)のどちらかになり、この値を1と比較します。


静的解析を行うツールがあります。プログラムの記述内容を調べて、様々な情報を提示してくれるツールです。
静的解析ツールのなかには、Cコンパイラよりも厳しく記述内容をチェックし、文法上の誤りがなくても正しく動作しない恐れのある個所を指摘してくれるものがあります。
Cコンパイラのなかにも、エラーではないが「警告」という形で危険個所を指摘してくれるものがあります。
皆さんがお使いのツールは、このプログラムに対して何か指摘をくれるでしょうか。

筆者紹介

浅野 昌尚(あさの まさなお)

ガイオ・テクノロジー株式会社

開発1部 QTXグループ

1980年代から30年以上にわたり汎用構造のCコンパイラ開発に従事し、その間に8ビットマイコンからRISC・VLIW・画像処理プロセッサまで、さまざまなCPU向けのクロスCコンパイラを開発。

人気のコラム

最新のコラム