web-dev-qa-db-ja.com

printfファミリーを使用して、どのようにしてsize_t変数を移植可能に印刷できますか?

size_t型の変数がありますので、printf()を使用して印刷したいです。移植できるように印刷するために、どの形式指定子を使用しますか?

32ビットマシンでは、%uは正しいようです。私はg++ -g -W -Wall -Werror -ansi -pedanticでコンパイルしましたが、警告はありませんでした。しかし、そのコードを64ビットマシンでコンパイルすると、警告が表示されます。

size_t x = <something>;
printf( "size = %u\n", x );

warning: format '%u' expects type 'unsigned int', 
    but argument 2 has type 'long unsigned int'

私がそれを%luに変更すれば、予想通り、警告は消えます。

問題は、32ビットマシンと64ビットマシンの両方で警告なしでコードをコンパイルするには、どうすればコードを作成できるかということです。

編集:回避策として、unsigned longのように十分に大きい整数に変数を「キャスト」し、%luを使用して印刷することが一つの答えであると思います。どちらの場合でもうまくいきます。他に何か考えがあるのなら私は探しています。

338
Arun

z修飾子を使用してください。

size_t x = ...;
ssize_t y = ...;
printf("%zu\n", x);  // prints as unsigned decimal
printf("%zx\n", x);  // prints as hex
printf("%zd\n", y);  // prints as signed decimal
418
Adam Rosenfield

使用しているコンパイラーによって異なるようです(blech):

  • gnuは%zu (または%zx、または%zdであるが、署名されているように表示するなど)
  • Microsoftは%Iu (または%Ix、または%Idと言っていますが、これも署名済みなど)—しかし、cl v19(Visual Studio 2015)では、Microsoft %zuをサポートthis reply to this comment

...もちろん、C++を使用している場合は、代わりにcoutAraKで推奨 として使用できます。

82
T.J. Crowder

C89の場合、%luを使用して値をunsigned longにキャストします。

size_t foo;
...
printf("foo = %lu\n", (unsigned long) foo);

C99以降の場合は、%zuを使用します。

size_t foo;
...
printf("foo = %zu\n", foo);
57
John Bode

Windows用Adam Rosenfieldの回答を拡張する。

私はVS2013 Update 4とVS2015のプレビューの両方でこのコードをテストしました:

// test.c

#include <stdio.h>
#include <BaseTsd.h> // see the note below

int main()
{
    size_t x = 1;
    SSIZE_T y = 2;
    printf("%zu\n", x);  // prints as unsigned decimal
    printf("%zx\n", x);  // prints as hex
    printf("%zd\n", y);  // prints as signed decimal
    return 0;
}

VS2015はバイナリ出力を生成しました:

1
1
2

vS2013によって生成されたものは言いますが:


zx
zd

注:ssize_tはPOSIXの拡張機能であり、SSIZE_TWindowsデータ型 の場合と似ているため、<BaseTsd.h>参照を追加しました。

さらに、以下のC99/C11ヘッダーを除いて、すべてのC99ヘッダーはVS2015プレビューで使用可能です。

C11 - <stdalign.h>
C11 - <stdatomic.h>
C11 - <stdnoreturn.h>
C99 - <tgmath.h>
C11 - <threads.h>

また、C11の<uchar.h>が最新のプレビューに含まれています。

詳細については、標準的な適合性については、これ oldnew を参照してください。 。

9
vulcan raven
std::size_t s = 1024;
std::cout << s; // or any other kind of stream like stringstream!
5
AraK

C99拡張を必ずしもサポートしていないC++でこれを行うことについて話している人のために、私は心からboost :: formatをお勧めします。これにより、size_t型のサイズに関する質問は議論の余地がなくなります。

std::cout << boost::format("Sizeof(Var) is %d\n") % sizeof(Var);

Boost :: formatにはサイズ指定子が必要ないので、値をどのように表示したいのか心配することができます。

5
swestrup
printf("size = %zu\n", sizeof(thing) );
5
nategoose

AraKが言ったように、c ++ストリームインタフェースは常に移植可能に動作します。

std :: size_t s = 1024; std :: cout << s; //またはstringstreamのような他の種類のストリーム!

C stdioが欲しいのなら、 "ポータブル"という特定の場合には、これに対するポータブルな答えはありません。これまで見てきたように、間違ったフォーマットフラグを選択すると、コンパイラに警告が表示されたり、誤った出力が表示されたりする可能性があるため、厄介です。

C99は "%" PRIdMAX "\ n"のようなinttypes.hフォーマットでこの問題を解決しようとしました。しかし、 "%zu"と同じように、誰もがc99をサポートしているわけではありません(2013年以前のMSVSのように)。これに対処するために "msinttypes.h"ファイルが浮かんでいます。

別の型にキャストすると、フラグによっては、切り捨てまたは符号の変更についてコンパイラから警告が出る場合があります。あなたがこの道を行くならば、より大きな関連固定サイズタイプを選んでください。 unsigned long longと "%llu"、またはunsigned long "%lu"のいずれかが機能するはずですが、32ビットの世界ではlluも大きすぎると遅くなるかもしれません。 (編集 - %lu、%llu、およびsize_tがすべて同じサイズであっても、%lluがsize_tと一致しない場合、私のMacは64ビットで警告を出します。また、%luと%lluは同じサイズではありません。)一致する形式をキャスト+使用する必要があるかもしれません。)

それに関しては、int64_tのような固定サイズ型を使うことができます。ちょっと待って!今、私たちはc99/c ++ 11に戻り、そしてより古いMSVSは再び失敗します。さらにキャストもあります(例えばmap.size()は固定サイズ型ではありません)。

Boostなどのサードパーティ製のヘッダやライブラリを使用することができます。まだ使っていないのであれば、プロジェクトをそのように膨らませたくないかもしれません。この問題のためだけに追加したい場合は、c ++ストリーム、または条件付きコンパイルを使用しないでください。

それで、あなたはc ++ストリーム、条件付きコンパイル、サードパーティのフレームワーク、あるいはあなたのためにうまくいくなんらかの移植性にかかっています。

2
Rick Berge

32ビットの符号なし整数を%lu形式に渡すと警告が表示されますか?変換は明確に定義されており、情報を失うことはないので、問題ありません。

フォーマット文字列リテラルに挿入できるマクロを<inttypes.h>で定義しているプラ​​ットフォームもあると聞きましたが、私のWindows C++コンパイラではそのヘッダが見えないため、クロスプラットフォームではない可能性があります。

0
Kylotan