2016-10-29 12 views
1

variadic関数はどのように数値定数をどのように扱うのですか?例えばVariadic関数と定数

myfunc(5, 0, 1, 2, 3, 4); 

関数は次のようになります。次のコードを考えてみva_argで単一の引数を反復処理するためには、

void myfunc(int count, ...) 
{ 
} 

今、私は、例えばそのサイズを、知っておく必要がありますint,short,charfloatなどですが、上記のコードで使用しているような数値定数の場合、どのサイズを仮定しますか?

テストは、ちょうど彼らのためにintを仮定すると、コンパイラはこれらの定数は、単一charまたはshortそれぞれに表すことができるにもかかわらず、intとしてそれらをプッシュすると思われるので、正常に動作するようですが示されています。

それにもかかわらず、私が見ている動作についての説明を探しています。可変定数に数値定数を渡すCの標準型は何ですか?これは明確に定義されているか、それともコンパイラに依存していますか? 32ビットと64ビットのアーキテクチャに違いはありますか?

ありがとうございます!

答えて

3

私はJonathan Leffler's answerが好きですが、移植可能なライブラリや可変長関数を備えたAPIを提供しようとする人にとっては、技術的な詳細を配っていると思いますので、詳細を掘り下げて調べる必要があります。

可変引数パラメータがdefault argument promotionsの対象となっている(C11ドラフトN1570 PDFなど、セクション6.5.2.2関数呼び出し、段落6):

..整数昇格は各引数に対して実行され、 のfloat型の引数はdoubleに昇格されます。これらは、デフォルト引数プロモーションと呼ばれます。

...昇進後の引数の型が昇進後のパラメータのものと互換性がありません、動作は次の場合を除き、未定義である[場合]:

  • 1つの昇格された型があります他の昇格型は対応する符号なし整数型であり、値は両方の型で表現可能です。

  • 両方のタイプは、彼らが1.0fのようfF(接尾辞されていない限り

浮動小数点定数は、タイプdoubleのある文字型の修飾または非修飾バージョンまたは無効

へのポインタであります)の場合は、タイプは floatです。

C99とC11の整数定数は、一致する場合はintです。 long(AKA long int)。それ以外の場合はlong long(AKA long long int)です。多くのコンパイラでは、サイズ接尾辞なしの整数定数は人為的なエラーまたはタイプミスであると仮定しているため、整数定数の型がintでない場合は常に接尾辞を含めることをお勧めします。 long int

  • luまたはulまたはLUかのためにunsigned int

  • lまたはLため

    • uまたはU

      整数定数もそのタイプを示すために、文字サフィックスを持つことができますULまたはunsigned long long int

    ためunsigned long intlong long int

  • llu又はLLU(又はULLまたはそれらの大文字または小文字の変異体のいずれか)のため

  • ll又はLL又はLlまたはlLためlU又はLu又はuLまたはUl

    integer promotionルールはセクション6.3.1.1にあります。

    • floatdouble

    • すべての整数型に昇格されています

      は(C89とC99に比べていくつかの追加が、有意な変化があった)C11のデフォルトの引数のプロモーションルールを要約しますintで表される値はintに昇格されます。 (これは符号無しとcharshort、及びタイプ_Boolintのビットフィールド、及び小さなunsigned intビットフィールドを符号付きの両方を含む。)

    • その値unsigned intで表すことができる(ただし、intすべての整数型)はunsigned intに昇格されます。 (これはunsigned intビット換言すれば、intCHAR_BIT * sizeof (unsigned int)のビットで表現することができないフィールド)、及びunsigned intのtypedefさエイリアスを含むが、それは私が考える、それをです。)

    • 整数タイプ少なくとも同じ大きintは変更されていません。これには、たとえばlong/long int,long long/long long intおよびsize_tが含まれます。

    私が指摘したいのですが、ルール内の1つの「落とし穴」があります:「符号なしに署名したが、署名への符号なしがあやふや大丈夫です」:

    • の場合引数は符号付き整数型に昇格されますが、関数は対応する符号なし整数型を使用して値を取得します。関数はモジュロ算術を使用して正しい値を取得します。

      つまり、負の値は、(1 +符号なし整数型の最大表現可能な値)だけインクリメントされたようになり、正の値になります。

    • 引数が符号なし整数型に昇格されますが、関数が対応する符号付き整数型を使用して値を取得し、その値が両方で表現可能な場合、関数は正しい値を取得します。値が両方で表現できない場合、動作は実装定義です。

      実際には、ほぼすべてのアーキテクチャが上記とは逆の動作をします。つまり、取得された符号付き整数値は(1 +符号なし整数型の表現可能な最大値)で引かれた符号なし値に一致します。私は、いくつかの奇妙なものが整数のオーバーフローや同様の奇妙なことを伝えるかもしれないと聞いたことがありますが、私はそのようなマシンで自分のミットを得たことはありません。あなたが指定子をprintfのために上記のルールを比較する場合

    man 3 printf manページ(Linuxのmanページプロジェクトの礼儀)は、非常に有益です。末尾のmake_message()関数(vsnprintf()に必要なC99、C11、またはPOSIX)も面白いはずです。

  • +0

    答えをありがとう!私はここでフォローアップをちょっとした:http:// stackoverflow。com/questions/40330749/why-does-my-variadic-function-work-both-int-and-long-long – Andreas

    1

    1と書くと、それはintの定数です。コンパイラが使用できる他のタイプはありません。別の型を必要とする関数の非可変プロトタイプがある場合、コンパイラは整数1を適切な型に変換しますが、それ自身では1int定数です。したがって、あなたの例では、6つの引数はすべてintです。

    呼び出されたvariadic関数がそれらを処理する前に、何らかの形で引数の型を知る必要があります。 printf()ファミリの関数では、フォーマット文字列は何を期待するかを示します。同様に機能のファミリのscanf()と同じです。

    デフォルトの変換は、可変長関数の省略記号に対応する引数に適用されることに注意してください。例えば、所与:使用

    int variadic_function(const char *, ...); 
    

    :へ

    char c = '\007'; 
    short s = 0xB0hD; 
    float f = 3.1415927; 
    

    コール

    int rc = variadic_function("c s f", c, s, f); 
    

    は実際intcsの両方を変換し、doubleからf

    +0

    ありがとうございました!私はここでフォローアップをちょっとした:http://stackoverflow.com/questions/40330749/why-does-my-variadic-function-work-with-both-int-and-long-long – Andreas

    関連する問題