目次: C言語とlibc
突然printfの動きがおかしくなって、引数で与えた数値を表示したりしなかったりするようになりました。
で、調べてみるとこーんなプログラムになってたわけです。
#include <stdio.h>
int main(int argc, char *argv[])
{
long long int a;
int b, c;
a = 0x1234567887654321LL;
b = 200;
c = 300;
printf("a:%d, %s, b:%d, c:%d \n", a, "strings", b, c);
return 0;
}
実行してみると
$ gcc a.c $ ./a.out Segmentation fault
見事に落ちました。
このプログラムのまずいところは変数aは8バイト(long long int型)あるのに、printfには %d書式(signed int型の指定)と指示しているため、printf側が4バイトしか見ない、ってところです。残った4バイトは次の %s指令のデータと見なされて、その結果変なアドレスを見に行ってプロセスが死にます。
なので、この場合は %dじゃなくて %lldと書いてlong long signed int型であることを指定すべきです。正しく動いたときの結果はこんな感じ。
$ ./a.out a:1311768467139281697, strings, b:200, c:300
整数だからといってなんでもかんでも %dにしちゃだめですよ、って教訓ですな。
$ gcc --version gcc (GCC) 4.1.2 20061115 (prerelease) (Debian 4.1.1-21) Copyright (C) 2006 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. $ gcc -Wall a.c a.c: In function 'main': a.c:12: warning: format '%d' expects type 'int', but argument 2 has type 'long long int'
ちなみにgccなら -Wallオプションを指定すれば、printfの書式指定が間違っていたときに教えてくれます。
もちろん好きこのんでこんな状態を作ったわけではありませんので、お間違いなく…。
この問題に出会うきっかけとなったプログラムは、言うなれば「typedef地獄」でしょうか。ぱっと見ても、整数なのか浮動小数点数なのか、はたまた構造体なのか…型が全くわかりません。何よりひどいのはtypedefが連鎖しまくっていることでしょうか。
例えばAライブラリのA_TypeがBライブラリのB_Typeのエイリアスだったとして、そのB_TypeがさらにCライブラリのC_Typeのエイリアスで、それがさらにDの…というように、ひねりのないtypedefが延々と続きます。
そのくせ最後まで辿ってみると無条件でtypedef int X_Type;(単なるint)とかいうオチが多いので、ウザいことこの上ない。
やがて調べるのが面倒くさくなって、どうせlongかintだろって思ってなめてたら、long long intのエイリアスがいくつか混ざっていて、警告オプション -Wallもご丁寧に抹消されており、上記の問題にはまったわけです。
C言語において、ダメなマクロの話は良く聞きますが、ダメなtypedefの使い方はそうそうないと思う。
< | 2008 | > | ||||
<< | < | 01 | > | >> | ||
日 | 月 | 火 | 水 | 木 | 金 | 土 |
- | - | 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 | - | - |
合計:
本日: