char型を文字列以外の用途で用いちゃいけない訳

 急に思い出したけど僕は開発者なので、たまには開発者っぽいことも書いていこうと思う。C言語の話。先日char型の変数のせいでえらい目にあった。
 簡単なコーディングなので見てみてほしい。例えば下記のようなコード。


char flag = -1;
if (flag == -1) {
hoge();
}

 このコード、char型のflagが-1になっていたら、hoge()関数が実行されるんですが、先日僕がこのようなプログラムを動かしたところ、どうもhoge関数が実行されない。だってif文の前で-1ってやってんだよ!? 意味分からないじゃない?
 したらお前、ここがC言語というかコンパイラの罠ですよ。

if(flag == -1)

の判定文。なんとflagが4バイトのintにキャストされてたわけです。しかもえらく変な感じでキャストされてた。charは1バイトの整数値なので、それがintにキャストされるとどうなるかと言うと、

符号付きcharの-1の16進数表記 : FF → -1
↑を4バイトintにキャストしたときの16進数表記:00 00 00 FF → 255

つまり僕が直前で-1と定義した値がすばらしきコンパイラさんのおかげで255になっていたわけです。そら駄目だ。
 もちろん頭のいいコンパイラによっては、intにキャストしたとしても-1として扱ってくれる事もあります。が、僕のプロジェクトで使ってる妙な野良コンパイラはアホなのでこういうことになったわけ。
 でもでも。コンパイラがアホだからーで済ませる問題ではない。このコード、頭いいコンパイラでコンパイルされるとは限らないからだ。通常、書いたコードが他に流用されないなんてことはあり得ない。色んなプロジェクトで流用されると考えたほうがよい。流用されるプロジェクトによってはアホコンパイラもあるだろうし、賢いコンパイラもいるわけで。そういった事を考えるとコンパイラ依存の上記コードは絶対書いちゃいけない。
 だから、こう直しましょうね。っていう話。

int flag = -1; ★
if (flag == -1) {
hoge();
}

 バカみたいな話だけど、misra-cのコーディング規約にも明記されている話だったりする。

http://www.openrtp.jp/wiki/_hara/ja/RtORB/MISRA-C-RULE.html

6.1 R 単なるchar型は、文字データの格納及び使用に限って用いなければならない。
6.2 R signed char型及びunsigned char型は、通知データの格納及び使用に限って用いなければならない。

この1文だけ見ると、なんで? って思いますがこういう事例があるとよく分かりますね。
 そもそも僕がこんなflagをchar型で定義してしまったのは、なぜかAndroidで書いたコードをC言語に流用してよ? 流用だから簡単でしょ? 3キロLineを1ヶ月で。とかキチガイみたいなことを言われ、時間もねーしjavaのbyte型をそのままcharに置換してたからで、最初からC言語で書いてたらこういうことはしなかったっつーかぶつぶつ……
【ニコ動でゲーム実況してました。】
ニコニコ動画ゲーム実況一覧

【twitter】気軽にフォローしてください→@yhei_hei

char型を文字列以外の用途で用いちゃいけない訳」への5件のフィードバック

  1. SECRET: 0
    PASS: 74be16979710d4c4e7c6647856088456
    パルスのファルシのルシがパージでコクーンってことで…おk?

  2. SECRET: 0
    PASS: 74be16979710d4c4e7c6647856088456
    バロスwwww確かに専門外の人が見たらその例えは的を射てるwww

  3. SECRET: 0
    PASS: 74be16979710d4c4e7c6647856088456
    ARM GCC だと char は符号無しなんですよね。。。。
    コンパイラの警告レベルを最大にしてもまれに見逃してしまいます。

  4. SECRET: 0
    PASS: 74be16979710d4c4e7c6647856088456
    同じところにはまったPGさん>
    >ARM GCC だと char は符号無しなんですよね。。。。
    あー! なるほど。実はこれビルドしたのARMコンパイラなんすよ。
    するてーと char hoge = -1の時点で255に解釈されてるんすかね。
    それで-1と比較して駄目になってた的な。
    勉強になります。いずれにしてもcharに数値、駄目、絶対ですね。

  5. SECRET: 1
    PASS: 74be16979710d4c4e7c6647856088456
    即値(つまり-1)を使ってる時点で、左辺と右辺の型が違います。
    即値はint型です。
    処理系が違う場合は、元の処理系と移植先の処理系の動作の違いをキチンと調べてから流用すべき。
    構造体一つとっても、処理系によってアライメントが違うので、memcpyしていると、アウトです。

コメントは停止中です。