C言語編 目次

 関数設計

 

・関数名の命名規則
・プログラミングに出る!英単語

 ポインタ

 

・データ型とポインタ

 データ型

 

・char *とconst char *は違う
・符号付きと符号なし

 演算子

 

・三項演算子とデータ型の問題

 制御構文

 

・条件式で代入する
・三項演算子を使ったswitch

 構造体

 

・構造体のサイズとアライメント
・構造体メンバのサイズを知る

 配列

 

・配列使用時の注意
・配列の要素数を知る

 メモリ管理

 

・メモリスタック
・動的メモリ確保とメモリリーク

 モジュール設計

 

・モジュール分割
・汎用モジュールとアプリ依存モジュール

 パフォーマンス
  徹底チューニング

 


・どんな処理に時間がかかるのか
・ファイル入出力の効率化
・アルゴリズムを考える1
・アルゴリズムを考える2

 プリプロセッサの便利機能


・2重インクルード防止

 


トップページへ戻る

符号付きと符号なし

 今回は、符号付き(signed)と符号なし(unsigned)のデータ型についてです。ここは、思わぬ落とし穴になりがちなところです。では、次のサンプルプログラムを見てください。

#include <stdio.h>

int main(int argc, char *argv[])
{
    long            lv = -10000;
    unsigned long   ulv = 10000;
    
    if (lv >= ulv)
    {
        printf("long value is greater.\n");
    }
    else
    {
        printf("unsigned long value is greater.\n");
    }
    
    return 0;
}

 long型とunsigned long型の変数を比較しているだけですが、この結果がどうなるかわかりますか?このプログラムを実行すると、なんと"long value is greater."が出力されます。マイナスの値とプラスの値を比較したのに、マイナスのほうが大きいという結果になってしまいます。なぜでしょうか。

 その理由は、long型とunsigned long型を比較するときは、long型の変数がunsigned long型に変換されるからです。-10000をunsigned long型に変換すると、4294957296になります。当然こちらのほうが大きいです。このような演算時の型変換は"算術変換"と呼ばれます。

 算術変換は、このほかにも浮動小数点数についても実行されますが、特にここでは取り上げません。大切なことは、signedとunsignedでは、unsignedの方が上位になるということです。従って、変数にマイナスの値が入っていると、とんでもなく大きいプラスの値になってしまうことがあるということです。

 ちなみに、VCでこのような比較をしようとすると、

warning C4018: '>=' : signed と unsigned の数値を比較しようとしました。

という警告が出ます。また、gcc -Wallでは警告は出ないようです。

 プログラムを書くときには、long型とunsigned long型の比較はしないようにしましょう。もともとlong型とunsigned long型は用途が違います。用途の違う変数同士を比較しないといけなくなったとしたら、どこか設計がおかしいんじゃないか?と疑ったほうがいいでしょう。

 上のサンプルのように、あからさまに書いてあればまだわかりやすいですが、では、次のような場合はどうでしょうか。

#include <stdio.h>
#include <time.h>

int main(int argc, char *argv[])
{
    time_t   tv = -10000;
    long     lv = 10000;
    
    if (tv >= lv)
    {
        printf("time value is greater.\n");
    }
    else
    {
        printf("long value is greater.\n");
    }
    
    return 0;
}

 time_tというのは時刻を保持するのに使われるデータ型ですね。でもここでちょっと考えてしまいませんか?time_tが果たしてunsigned longなのかsigned longなのか、それによって結果が変わってくることはもうわかりますよね。

 こんな風に、typedefされているデータ型なんかを使うときは、気をつけていないとうっかり他のデータ型と比較して、思わぬバグを生む原因になります。

 ちなみに答えを言うと、上のプログラムは"long value is greater."が出力されます。time_tはlong型です。

 では、次のサンプルプログラムを見てください。

#include <stdio.h>

int main(int argc, char *argv[])
{
    short           sv = -10000;
    unsigned short  usv = 10000;
    
    if (sv >= usv)
    {
        printf("short value is greater.\n");
    }
    else
    {
        printf("unsigned short value is greater.\n");
    }
    
    return 0;
}

 今度はshort型とunsigned short型を比較しています。この場合はどうなるでしょうか。このプログラムを実行すると、実は"unsigned short value is greater."が出力されます。long型の時とは結果が違ってきます。これはどういうことでしょうか。

 その理由は、演算(この場合は比較演算)が実行される前に、short型はint型に型変換されるからです。(ここではint型を4バイトとしています。)。short型もunsigned short型もint型に変換されるので、int型同士の比較となり、usvのほうが大きくなります。

 このような型変換は、本によっては"単項変換"と書かれています。これは算術変換より前に実行されます。int型より小さいデータ型は、演算前にint型に変換されます。