どうすればprintfファミリーを使用してsize_t変数を移植して印刷できますか?


405

タイプの変数があり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ビットの両方のマシンで警告が発生しないようにコードを記述するにはどうすればよいですか?

編集:回避策として、1つの答えは、変数を十分な大きさの整数に「キャスト」して、たとえばunsigned longを使用して出力することだと思い%luます。それはどちらの場合でも機能します。他にアイデアがあるかどうか探しています。


4
unsigned longlibc実装がz修飾子をサポートしていない場合は、キャスト先が最良のオプションです。C99標準では推奨していますsize_t以上の整数変換ランク大きなを持っていないlongあなたが合理的に安全だので、
クリストフ



1
Windowsプラットフォームでは、size_tはlongよりも大きくなる可能性があります。互換性の理由から、longは常に32ビットですが、size_tは64ビットにすることができます。したがって、unsigned longにキャストすると、ビットの半分が失われる可能性があります。すみません:-)
Bruce Dawson

回答:


483

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

7
+1。これはC99の追加ですか、それともC ++にも適用されますか(C90は手元にありません)?
avakar

6
これはC99の追加でありprintf()、2009-11-09のC ++ 0xドラフトの長さ修飾子のリストには含まれていません(672ページの表84)
Christoph

3
@Christoph:最新のドラフトn3035にもありません。
GManNickG 2010年

11
@avakar @Adam Rosenfield @Christoph @GMan:ただし、n3035§1.2の規範的な参照では、C99標準のみが参照され、§17.6.1.2/ 3は「C標準ライブラリの機能が提供されています」と同じ状態です。私はこれを、特に明記しない限り、C99の追加のフォーマット指定子を含め、C99標準ライブラリのすべてがC ++ 0x標準ライブラリの一部であることを意味すると解釈します。
James McNellis、2010年

9
@ArunSaha:これはC99ではなくC ++の機能です。でコンパイルする-pedantic場合は、C ++ 1xドラフトをサポートするコンパイラを入手するか(ほとんどありません)、コードをC99としてコンパイルされたファイルに移動する必要があります。そうでなければ、あなたの唯一のオプションは、あなたに、変数をキャストすることでunsigned long long、使い%llu最大限に移植できるように。
Adam Rosenfield、2010

88

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

...そしてもちろん、C ++を使用している場合は、AraKの提案に従ってcout代わりに使用できます。


3
znewlib(つまりcygwin)でもサポートされています
Christoph

7
%zdは正しくありませんsize_t。これは、に対応する符号付きの型には正しいですsize_tが、size_tそれ自体は符号なしの型です。
キーストンプソン

1
@KeithThompson:私も言及し%zuました(%zx16進数が必要な場合も)。%zuおそらく、リストの最初にあるはずです。修繕。
TJクラウダー2013年

10
@TJCrowder:私は%zdリストに含まれるべきではないと思います。私が使用する理由を考えることができない%zdのではなく%zu印刷するsize_t値を。値がを超えると、それは無効になります(動作は未定義です)SIZE_MAX / 2。(完全%zoを期すために、8進数で言及しても構いません。)
キース・トンプソン、

2
@FUZxxl:POSIXでは、にssize_t対応する符号付きの型である必要はないため、とのsize_t一致は保証されません"%zd"。(これはおそらく、ほとんどの実装である。)pubs.opengroup.org/onlinepubs/9699919799/basedefs/...
キース・トンプソン

59

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

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

C99以降では、次を使用します%zu

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

7
2013年を考慮して、「C99以降」および「C99以前:」を提案します。ベストアンサー。
chux-モニカを2013年

8
こんなことしないで。size_tが64ビットでlongが32ビットの64ビットWindowsでは失敗します。
イットリル

1
@イットリル:では、64ビットウィンドウの答えは何でしょうか。
John Bode

1
@JohnBodeたぶんunsigned long long
James Ko

2
または:aにキャストしてから、フォーマット指定子を含むinttypes.h uint64_tPRIu64マクロを使用できます。
James Ko

9

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によって生成されたものは言う:

zu
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>は最新のプレビューに含まれるようになりました。

詳細については、標準への準拠について、この古いリストと新しいリストを参照してください。


VS2013 Update 5は、Update 4と同じ結果になります。
Nathan Kidd 2017

6

必ずしもC99拡張をサポートしているわけではないC ++でこれを行うことについて話している人のために、boost :: formatを強くお勧めします。これにより、size_tタイプのサイズに関する質問が無効になります。

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

boost :: formatでサイズ指定子を必要としないので、値をどのように表示するかを心配するだけで済みます。


4
おそらく欲しい%u
GManNickG

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

8
ええ、でも質問者は特にprintf指定子を求めます。私はそれらがstd::cout問題を使用するようにするいくつかの他の明言されていない制約を持っていると思います。
ドナルフェロー

1
@Donal C ++ストリームがC ++プロジェクトでどのような問題を引き起こす可能性があるのでしょうか。
AraK

9
@AraK。彼らはとても遅いですか?あまり理由がないので、LOTがたくさん追加されます。ArunSahaは自分の個人的な知識で知りたいだけですか?個人的な好み(私は自分自身をfstreamするよりもstdioを好みます)。多くの理由があります。
KitsuneYMG 2010年

1
@TKCrowder:まあ、元のリクエストは(タグ付けによって)Cソリューションが必要であると言っていました。たとえば、出力形式記述子がメッセージカタログからプルされている場合など、C ++でストリームを使用しないことには十分な理由があります。(必要に応じて、メッセージのパーサーを作成し、ストリームを使用することもできますが、既存のコードを活用できる場合は、多くの作業が必要です。)
Donal Fellows

1
@Donal:タグはCおよびC ++でした。私は決してC ++のI / Oストリームのものを支持しているわけではありません(私はそれのファンではありません)。質問元々* "... printf指定子に仕様を尋ねたのではない "と指摘しているだけです。
TJクラウダー


2

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

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

C stdioが必要な場合、「ポータブル」の特定のケースではこれに対する移植性のある答えはありません。また、これまで見てきたように、間違ったフォーマットフラグを選択するとコンパイラの警告が表示されたり、誤った出力が表示されたりする可能性があるため、醜くなります。

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

別の型にキャストすると、フラグによっては、切り捨てや符号の変更に関するコンパイラ警告が表示される場合があります。このルートに行く場合は、関連する固定サイズのタイプを大きくしてください。unsigned long longと "%llu"またはunsigned long "%lu"のいずれかが機能しますが、lluは32ビットの世界では非常に大きいため、処理速度が低下する可能性もあります。(編集-%lu、%llu、およびsize_tがすべて同じサイズであるにもかかわらず、私のMacは64ビットで%lluがsize_tと一致しない場合に警告を発行します。また、MSVS2012では%luと%lluが同じサイズではありません。 +キャストする必要があるかもしれません+一致するフォーマットを使用してください。)

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

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

つまり、C ++ストリーム、条件付きコンパイル、サードパーティのフレームワーク、またはたまたま機能するポータブルなものに行き着くのです。


-1

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

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


1
ほとんどのコンパイラは、間違ったサイズの何かをprintfに渡しても警告しません。GCCは例外です。inttypes.hはC99で定義されているため、C99に準拠しているすべてのCコンパイラがそれを持っています。それでも、コンパイラフラグを使用してC99をオンにする必要がある場合があります。いずれの場合でも、inttttypes.hは、それぞれ独自のサイズ指定子「z」および「t」を取得するのに十分重要であると判断されたため、size_tまたはptrdiff_tの特定の形式を定義しません。
swestrup

を使用する%lu場合は、size_t値をにキャストする必要がありますunsigned long。への引数には暗黙的な変換(プロモーション以外)はありませんprintf
キーストンプソン2013年

-2

C99では "%zd"などを定義しています。(コメント提供者に感謝)C ++には、そのための移植可能な形式指定子はありません。これらの2つのシナリオでは単語を使用できます%p、移植可能な選択肢ではなく、16進数で値を指定できます。

または、ストリーミング(stringstreamなど)またはBoost Formatなどの安全なprintf置換を使用します。このアドバイスは限られた用途に限られていることを理解しています(C ++も必要です)。(私たちは、ユニコードのサポートを実装するときに、ニーズに合った同様のアプローチを使用しました。)

Cの根本的な問題は、省略記号を使用するprintfは設計上安全ではないことです。既知の引数から追加の引数のサイズを決定する必要があるため、「得たものをすべて」サポートするように修正することはできません。したがって、コンパイラが独自の拡張機能を実装しない限り、あなたは運が悪いです。


2
zサイズmodidfier標準Cですが、いくつかのlibcの実装は、さまざまな理由で1990年に立ち往生している(例えば、Microsoftは基本的にC ++の賛成でCを放棄し、 - C# -最近で)
クリストフ・

3
C99は、サイズ指定子「z」をsize_t値のサイズと定義し、「t」をptrdiff_t値のサイズと定義しました。
swestrup

2
%zd間違っています%zu。署名されていないため、
David Conrad、

-3

一部のプラットフォームおよび一部のタイプでは、特定のprintf変換指定子を使用できますが、場合によっては、より大きなタイプにキャストする必要があります。

このトリッキーな問題を、コード例http://www.pixelbeat.org/programming/gcc/int_types/でドキュメント化し 、新しいプラットフォームとタイプに関する情報で定期的に更新しています。


1
リンクのみの回答は推奨されないことに注意してください。SOの回答は、ソリューションの検索のエンドポイントである必要があります。参照としてリンクを維持しながら、ここにスタンドアロンの概要を追加することを検討してください。
クレオパトラ2013

-5

size_tの値を文字列として出力する場合は、次のようにします。

char text[] = "Lets go fishing in stead of sitting on our but !!";
size_t line = 2337200120702199116;

/* on windows I64x or I64d others %lld or %llx if it works %zd or %zx */
printf("number: %I64d\n",*(size_t*)&text);
printf("text: %s\n",*(char(*)[])&line);

結果は:

番号:2337200120702199116

テキスト:私たちの上に座っている代わりに釣りに行こう!

編集:私は彼の問題が%lluまたは%I64dではないことに気付いた反対票のために質問を読み直しましたが、異なるマシンのsize_tタイプはこの質問を参照してくださいhttps://stackoverflow.com/a/918909/1755797
http:// www。 cplusplus.com/reference/cstdio/printf/

size_tは、32ビットマシンではunsigned int、64ビットで
はunsigned long long intですが、%llは常にunsigned long long intを想定しています。

size_tの長さはオペレーティングシステムによって異なりますが、%lluは同じです


4
これはなんてナンセンスなの?!
Antti Haapala

char配列の最初の8バイトをsize_tポインターを介してunsigned long long 64bitにキャストし、printf%I64dを使用してそれらを数値として出力することは本当に素晴らしいことではありません。もちろん、型のオーバーフローを防ぐためのコードはありませんでしたが、それは問題の範囲外です。
Andre
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.