C言語のNOT演算(チルダ~)でよくわからない現象

H8マイコン用のプログラムとして、ビット演算をしているときに見つけました。環境は、windowsの上でcygwinを動かしていて、コンパイラはgcc4.3.4(20090894)とかいうやつです。

#include <stdio.h>
main(){
	char c;
	c = ~0x80;
	
	printf("%d\n", c);
}
> warning: overflow in implicit constant conversion
#include <stdio.h>
main(){
	unsigned char c;
	c = ~0x80;
	
	printf("%d\n", c);
}
> warning: large integer implicitly truncated to unsigned type

なんとなく、NOTされたときに8ビット目(上位の範囲を超えた)が立っちゃってるんじゃないの的なことを匂わせます。

#include <stdio.h>
main(){
	char c;
	c = (char)~0x80;
	
	printf("%d\n", c);
}

まぁ、こうやれば警告も出ません。なんとなく気持ちが悪いのですが、調べる気力もなかったので誰か理由を教えてください。

追記(2011/2/5 15:18)

コメントで答えが出ているのでそちらを参照。

C言語のNOT演算(チルダ~)でよくわからない現象」への5件のフィードバック

  1. mrxptn

    一般的な処理系では16進リテラルはintとして評価されてて、それをchar変数に代入しようとしてるからですねー。
    ついでに
    代入文の右辺値を静的に評価できる場合には暗黙の型変換チェックがこういう形で行われて、
    動的な評価を伴う代入文の場合は自動的な型変換チェックが無理なので(責任を持つと表明する意味で)明示的キャストが必要となる…というかんじでしょうか。

  2. baw 投稿作成者

    >> mrxpthさん
    前半が答えですが、どちらかというと後半に興味をそそられました。

    16進リテラルがintとして評価され、かつ右辺が静的に評価されるパターン。
    char a = 0x70;
    では警告が出ませんでした。これはどう説明が付くのでしょうか。

    もし、まだ見てくださっていれば教えてください。

  3. mrxptn

    0x70(より正確には0x00000070)はcharのビット的な意味での最大値(0xff)以下なので、暗黙の型変換によってcharに代入してもオーバーフローが生じないことが*コンパイル時に*わかってしまうので、安全な型変換と見なされ、警告は出ないわけです。オーバーフローが生じる代入がどう安全じゃないかまではたぶん言う必要ないと思います。
    で、0xffを越えるリテラルを代入しようとすればコンパイル時にcharの範囲を越えてることがわかるので、先ほどのように警告が出ます。

  4. baw 投稿作成者

    なるほど。私は馬鹿なので、もっと簡単に言うと、

    ~0x80 → 0b11111111 11111111 11111111 01111111

    というわけですね。
    これで、PDR8 &= ~0x80;というお馴染みの操作においてなぜ警告が出ないのかはっきりわかりました。

    ありがとうございました。

  5. mrxptn

    そうですね、その通りです。
    対策としては、キャストよりベターな方法として、0x80をconst charな変数に入れてしまうことですね。
    const char hoge = 0x80;
    そうすれば、
    char t = ~hoge;
    この文は完全に安全なものであり警告は出ず、hogeを名前つき定数として適切に命名すれば見通しも向上します。
    (#defineによる名前つき定数に対して型安全性やスコープや最適化などの点でメリットがあります)

    ついでに、今さらだけど、
    CではなくC++の場合、一応こういうコードでリテラルがint扱いになることを確認しました。
    http://ideone.com/z1w8L

コメントを残す

メールアドレスが公開されることはありません。