【C言語】
size_t型のprintf書式は %zu,%zx

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 でも負値になってしまいます。


参考:

長さ修飾子 (length modifier)