signedとunsignedが出会ったら

C言語のスキルアップがこの一連の文章の主目的だが、 その道筋を示すため、まずは実例から入りたい。

以下のプログラムの出力がどうなるか考えてみよう。

#include <stdio.h>

int main()
{
    int var1;
    unsigned int var2;
    int var3;

    var1 = -2;
    var2 = 2;
    var3 = var1 / var2;
    printf("%d\n", var3);

    return 0;
}

可能なら実際にコンパイルして実行してみよう。

-1とはならなかったはずだ。 コンパイラのバグではない。 ランダムな値でもない。 C言語の規格通りにコンパイルされて、プログラムは動作したまでだ。 そういうわけで、intが32bitの環境なら、値は2147483647のはずである。

何が起きているのか

C言語では二項算術演算で、左辺値と右辺値の型が異なっていると、同じ型になるように暗黙的に変換を行う。

前に修飾子がないintはsigned intであるため、var1はsigned intである。 var2はそのままunsigned intであるため、var1とvar2は違う型である。 よって、var1とvar2の割り算では左右の型を合わせるために暗黙的に変換が行われる。 C言語の規格では、左右の型が同じサイズの整数型で片方がsignedでもう片方がunsignedであったとき、 unsignedにそろえる決まりになっている。 そのため、例えばintが32bitの環境では、 signedの-2がunsignedの(2^32)-2 = 4294967294 に変換されてから、割り算が実行されているのである。 4294967294/2 = 2147483647 というわけだ。

では、左辺値と右辺値の型を同じintにして、暗黙的な変換は起きないようにしてみよう。 具体的には、6行目のvar2の型の宣言からunsignedを消して、コンパイルして実行してみよう。 今度は-1が表示されたはずだ。

対策を考えよう

始めに紹介したプログラムは、var1が-2でvar2が2のときに、2147483647と表示するC言語のプログラムである。プログラムは間違いなくそう書かれている。 しかし、こういったソースコードで、開発者が本当にこういう挙動を意図としている場合はどれほどあるだろうか。 意図していなかったのならば、それはバグだ。

では一方で、他人の書いたソースコードをデバッグしているときに、こういったバグをすぐに全て見つけることは出来るだろうか。 さらに、新米のプログラマが、こういったバグを入れ込むのを現実的にどう防げるだろうか。

こういった問題に対して、大きく分けて以下の3点を提案したい。

暗黙的型変換について、この3点を完遂するまでの道のりは長い。 そこで、より簡単な事象を用いて、この3点についてまずは説明してみたいと思う。

戻る