暗黙の型変換と符号拡張とは?
C言語を勉強してきた中で私の致命的な思い違いシリーズの第二弾です。
今回は、暗黙の型変換と符号拡張についてです。
- 関連記事:コンパイラの最適化とは?
- 関連記事:Linuxで読込中のファイル削除の影響
暗黙の型変換と符号拡張で起きる問題
次のコードの出力結果はどうなるでしょうか?
#include<stdio.h> int main(){ char c = -1; printf(" c %d,%x,%dn",sizeof(c),c,c); return 0; }
答えは以下の通りです。
$ ./test.exe c 1,ffffffff,-1
この結果、初めは何もおかしくないと思っていました。
しかし、よく考えるとおかしな点が2点あります。
- 1バイト分の2桁表示となるはずが、4バイト分の8桁表示になっている
- 1バイト分は右2桁がffでも納得できるが、あと3バイトが勝手にffffffになっている
1バイトが4バイトで表示される疑問
char型の変数cは1バイトであるはずです。その証拠にsizeof(c)を行った際の値は1。しかし、変数cを%xでprintfした結果が4バイト分8桁表示されています。想定していたのはffのみです。それがffffffffと表示された事に疑問を抱きました。
なぜ4バイト分表示されたかの謎は調べたらすぐにわかりました。printfは引数がint型としているので、char型を引数に入れても暗黙のうちに型変換が生じ、int型として4バイト8桁で表示されたのです。
ちなみに、この現象を暗黙の型変換と言うそうです。
暗黙の型変換時に3バイトがfで埋められている疑問
もう1つの疑問です。1バイトが4バイトで表示されるのは暗黙の型変換が生じているという理由で納得しました。では何故000000ffではなくffffffffなのでしょうか?
これはchar型がsignedかunsignedかによって左右されるようです。実験的に下記のコードを試してみました。
#include<stdio.h> int main(){ signed char c = -1; unsigned char uc = -1; printf(" c %d,%08x,%dn",sizeof(c),c,c); printf(" uc %d,%08x,%dn",sizeof(uc),uc,uc); return 0; }
結果は以下でした。
$ ./test2.exe c 1,ffffffff,-1 uc 1,000000ff,255
signedの場合、暗黙の型変換時に拡張された3バイト分がfで埋められていて数値として-1になります。unsignedの場合は3バイト分が0で埋められていて数値としては255になります。ちなみに-1と255のビットパターンは同じでsignedかunsignedかによって数値の表示が変わることは、さすがに知ってました。(でもコンパイル時にunsigned char型の変数に-1を代入しようとしてwarningも出ないことにはびっくりしましたけどね)
この結果から符号の整合性を保つために型変換した際の3バイト分を、ffffff又は000000とすることで数値としての整合性を保っています。
この現象を符号拡張と言うそうです。
まとめ
今回のような、暗黙の型変換や符号拡張を知らないと、何が正しく、何がおかしいのか正確に判断できないので1人では何も解決できなくなってしまいます。C言語には落とし穴がいっぱいですね。
サンプルコードのダウンロード
- ダウンロードには、株式会社シンメトリックの発行するメルマガへの登録が必要です。登録後、ダウンロードURLを登録メールアドレスに送信します。
- サンプルコードは、コンパイルすることなくWindows環境ですぐに動作します。記事の理解に是非お役立てください。
入力したメールアドレスにダウンロードURLをお送りしましたのでご確認ください。