はい、引数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 標準の脚注は非規範的です。それらは一般に、規範的なテキストに記載されている要件を明確にすることを目的としており、新しい要件を課すことを目的としていません。ただし、ここでの規範的なテキストは、対応する符号付きおよび符号なしの型が同じ表現を持つことを示しているだけであり、関数の引数と同じ方法で渡されることを必ずしも意味するわけではありません。原則として、コンパイラはその脚注を無視できます。たとえば、パスint
とunsigned int
引数を異なるレジスタに配置してfprintf(fp, "%c\n", foo);
未定義にすることができます。
しかし実際には、実装がそのようなゲームをプレイする理由はなく、期待どおりに動作することに依存できますfprintf(fp, "%c\n", foo);
。それが機能しない実装を見たり聞いたりしたことがありません。
個人的には、これに頼らないほうがいいと思います。私がそのコードを書いている場合、キャストを介して明示的な変換を追加します。これにより、これらの質問が最初から発生しなくなります。
unsigned int foo = 42;
fprintf(fp, "%c\n", (int)foo);
または、最初に を作成foo
しint
ます。