2

C99 標準のセクション 7.19.6.1パラグラフ8:

cl長さ修飾子が存在しない場合、int引数は に変換され unsigned char、結果の文字が書き込まれます。

C99 標準のセクション 7.19.6.1パラグラフ 9では、次のようになります。

対応する変換指定に対して正しい型でない引数がある場合、動作は未定義です。

  • fprintf関数にはint引数が必要ですか?

たとえば、unsigned int結果を渡すと、未定義の動作が発生します。

unsigned int foo = 42;

fprintf(fp, "%c\n", foo); /* undefined behavior? */

これは、実装が(セクション 6.2.5パラグラフ 15)charと同じ動作をするように定義できた可能性があるため、心配です。unsigned char

これらの場合、整数の昇格により、一部の実装で に昇格charすることが指示される場合があります。したがって、次のコードをそのままにしておくと、これらの実装で未定義の動作が発生する危険があります。unsigned int

char bar = 'B';

fprintf(fp, "%c\n", bar); /* possible undefined behavior? */
  • int変数とリテラル定数は、指定子intで値を渡す唯一の安全な方法ですか?fprintf%c
4

2 に答える 2

5

%cの変換指定には引数fprintfが必要です。値は、デフォルト引数のプロモーションの後intのタイプでなければなりません。int

unsigned int foo = 42;
fprintf(fp, "%c\n", foo);

未定義の動作:fooである必要がありintます。

char bar = 'B';
fprintf(fp, "%c\n", bar);

未定義の動作ではありません:可変引数関数のように昇格されbarます (デフォルトの引数の昇格) 。intfprintf

編集:公平を期すために、未定義の動作になる可能性のある非常にまれな実装がまだいくつかあります。たとえば、が (この実装のように)ですべての値を表現できるcharわけではない符号なし型の場合、デフォルトの引数昇格は に行われます。charintunsigned int

于 2013-07-26T14:14:50.010 に答える
3

はい、引数printf"%c"必要intです。多かれ少なかれ。

引数の型が よりも狭い場合int昇格されます。ほとんどの場合、昇進は への昇格であり、int明確に定義された動作を伴います。ごくまれに、plaincharが unsigned でありsizeof (int) == 1(これは を意味しますCHAR_BIT >= 16)、char引数が に昇格されunsigned int、未定義の動作を引き起こす可能性があります。

文字定数はすでに 型intであるためprintf("%c", 'x')、特殊なシステムでも適切に定義されています。(トピック外: C++ では、文字定数の型はcharです。)

これ:

unsigned int foo = 42;
fprintf(fp, "%c\n", foo);

厳密に言えば、未定義の動作があります。 N1570 7.1.4p1 言います:

関数への引数が、可変数の引数を持つ関数によって予期されない型 (昇格後) を持っている場合、動作は未定義です。

そして、そのfprintf呼び出しは明らかにそれに反しています。(指摘してくれたouahに感謝します。)

一方、6.2.5p6 は次のように述べています。

各符号付き整数型には、対応する (ただし異なる) 符号なし整数型 (キーワード unsignedで指定) があり、同じ量のストレージ (符号情報を含む) を使用し、同じ配置要件があります。

そして 6.2.5p9 は言う:

符号付き整数型の負でない値の範囲は、対応する符号なし整数型の部分範囲であり、各型の同じ値の表現は同じです。

脚注付き:

同じ表現とアラインメントの要件は、関数への引数、関数からの戻り値、および共用体のメンバーとしての互換性を暗示するものです。

脚注には、値が両方の型の表現可能な範囲内にある限り、型intとの関数引数は交換可能であると書かれています。unsigned int(典型的な 32 ビット システムでは、値が 0 から 2 31 -1の範囲内になければならないことを意味します。-2 31intから -1 の値、および2 31から 2 32 -1 の値は範囲外です。他のタイプのものであり、互換性はありません。)unsigned int

しかし、C 標準の脚注は非規範的です。それらは一般に、規範的なテキストに記載されている要件を明確にすることを目的としており、新しい要件を課すことを目的としていません。ただし、ここでの規範的なテキストは、対応する符号付きおよび符号なしの型が同じ表現を持つことを示しているだけであり、関数の引数と同じ方法で渡されることを必ずしも意味するわけではありません。原則として、コンパイラはその脚注を無視できます。たとえば、パスintunsigned int引数を異なるレジスタに配置してfprintf(fp, "%c\n", foo);未定義にすることができます。

しかし実際には、実装がそのようなゲームをプレイする理由はなく、期待どおりに動作することに依存できますfprintf(fp, "%c\n", foo);。それが機能しない実装を見たり聞いたりしたことがありません。

個人的には、これに頼らないほうがいいと思います。私がそのコードを書いている場合、キャストを介して明示的な変換を追加します。これにより、これらの質問が最初から発生しなくなります。

unsigned int foo = 42;
fprintf(fp, "%c\n", (int)foo);

または、最初に を作成foointます。

于 2013-07-26T14:55:37.393 に答える