warning: format specifies type ‘int’ but the argument has type ‘unsigned long’
警告: %dは’int’型の書式だが、引数の型は’unsigned long’です。
[-Wformat]
■この記事の概要
この記事では、C言語のsize_t
型をprintf
で正しく出力するための書式指定子%zu
や%zx
の使用を推奨しています。古い書式指定子%d
やキャストによる警告回避は問題を潜在化させる可能性があるため、避けるべきとしています。64ビット環境での例を交えて正しいコーディング方法を解説しています。
■sizeof()にキャストするな
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>
//巨大な配列
//gcc -m32 32bitコンパイラではコンパイルできない
//gcc -m64 64bitコンパイラではコンパイルできる
typedef struct {
int id;
char buf[INT_MAX];
} big_data ;
int main(void){
if(sizeof(big_data) > INT_MAX){
//%d:gcc -m64で警告発生
//キャストで警告抑止⇒問題の潜在化
printf("NG %d byte\n", sizeof(big_data));
printf("NG %d byte\n",(int)sizeof(big_data));
//%ld:gcc -m32で警告発生
//%zu推奨10進
//%zx推奨16進
printf("NG %ld byte\n",sizeof(big_data));
printf("OK %zu byte\n",sizeof(big_data));
printf("OK %zx byte\n",sizeof(big_data));
}
}
(1) 32bit(ILP32)コンパイラ時代は
size_t型を表すのに
%dでも
%ldで実行結果は同じでした。
コンパイル時警告は出たかも?
それ以前に巨大データはコンパイルできない?
(2) 64bit(LP64,LLP64)コンパイラ時代は
size_t型を表すのには
%zu(10進)か
%zu(16進)を使いましょう。
コンパイル時警告は出ません。
(3)コンパイラの警告を消すために
%d書式に対して(int)sizeofとキャストするのは止めましょう。
理由は以下の実行結果を参照してください。
■実行結果(gcc -m64)
NG -2147483644 byte
NG -2147483644 byte
NG 2147483652 byte
OK 2147483652 byte
OK 80000004 byte
せっかくコンパイラが警告を出しているのに、
キャストすると警告が抑止され
問題が潜在化します。
この例では巨大オブジェクトのサイズが
INT_MAXを超えたので負値になっています。
■実行結果(Visual Studio64bit版)
NG -2147483644 byte
NG -2147483644 byte
NG -2147483644 byte
OK 2147483652 byte
OK 80000004 byte
Visual Studio 64bit版では
longのサイズと
intのサイズが同じなので
書式 %ld でも負値になってしまいます。
参考: