2017-02-25 6 views
11

私はcppreference.comprintf明細書の以下の部分の意図を見つけることができません。同じ呼び出しで%nの値をprintf - 無意味ですか?

は、各変換 指定のアクションの後に一連のポイントがあります。同じ 変数に複数の%n結果を格納することが可能になり、同じ コールの中で先に%nによって格納された値をに出力することができます。

1つ(またはさらに数)%n変換指定子(S)の結果は、同じprintf -statementでプリントアウトすることができれば、これは読み込みます。 printfのボディが入力される前に、printfの呼び出しに渡されたすべての引数が評価されるため(これは引数の評価後にシーケンスポイントが存在するため)、これを達成する方法を見つけることはできません。

#include <stdio.h> 

int main(int argc, char* argv[]) 
{ 
    int n = 0; 
    printf("Hello, world!%n (%d first n); %n (%d second n)", &n ,n, &n, n); 
    // will print out "Hello, world! (0 first n); (0 second n)" 

    return 0; 
} 

私の質問:「があるにISN場合printfは、「これまでに書かれた文字の数」で、この変数の値を上書きする機会を持つ前に、したがって、%nを書きますする変数の値が評価され、 「同じ呼び出しの中で先に%nによって格納された値を出力する」可能性は、printf仕様のそれぞれの部分が無意味であるか誤解を招くものではないでしょうか?シーケンスポイントが 後にそこにあるかのように(1)書式付き入力/出力機能が

7.19.6書式付き入力/出力機能 振る舞うもの:c99 standard文の実際の意味は何

各指定子に関連付けられたアクション。

未定義の動作を取得する可能性を減らすことはできますか?

質問は、このトピックが両方の言語に同じように当てはまると思うので、C++とcでタグ付けされています。

+0

C11 refが「7.21.6 1」である。 – chux

+1

cppreferenceは今やあまり意味がありません – Cubbi

答えて

4

あなたが正しく識別した理由のために、あなたのコードは実際には0しか表示しません。

介在するシーケンスポイントなしでオブジェクトが複数回書き込まれた場合、プログラムの動作が未定義であることを他の場所でブランケット表現するため、スタンダードのステートメントがまだ必要です。実際には、あなたのコードには定義されていない振る舞いはありません(例えば、i = i++;とは異なります)。

+0

シーケンスポイントがあると言う部分に当てはまります。しかし、強調した部分のポイントは何ですか?それは本当に可能ではありませんか? – Barmar

+0

@Barmar:もちろん、以前の書き込みは*読むことはできませんが、太字の部分はシーケンスポイントの唯一の重要な結果です!異なる出力位置に書き込んだだけの場合は、シーケンスポイントは必要ありません。 –

+0

@Kerrekこれは、printf()が通常のユーザー定義関数として記述できないことを意味しますか? –

8

これは夢中になるかもしれませんが、私は次は合法だと思う:

char s[2]; 
s[1] = '\0'; 
printf("Hi, world!%hhn%s", s, s); 

%hhnはcharへのポインタを取ります。これは10(これまでに書かれた文字数)をs[0]に書いています。次に、文字列sを出力します。これは"\n"または(char[]){ 10, 0 }(ASCIIと仮定します)と同等です。

+1

Cの標準がこれを法的にしなかったなら、私たちが何をしたのか分かりません。 :) – Barmar

+0

それは狂っているだけでなく、もっともらしい。 –

+0

クリエイティブなアプローチ:小さな問題では、 'hh'はC++ではサポートされていますが、Cではサポートされていないということです。それでも、狭い意味で'%n'値を出力すると仮定しましょう。 ASCII文字として)は不可能であり、cppreference.comの太字部分は誤解を招く恐れがあります。とにかく、創造性のために+1! –

2

printfへの呼び出しを、fputs()への個々の呼び出しのシーケンスに変換して、変換ハンドラへの呼び出しによって計算された文字列の断片を想像することができます。この実装では、nの値が出力される前に値をnに格納することがあります。これは適合しないでしょうか?

現代のコンパイラは、すでにそのようなfputchar('\n');puts("Hello world");printf("\n");printf("Hello world\n");を変換するように、printf()に小さな最適化を行います。また、書式文字列と引数の一貫性をチェックします。さらに最適化を行うと、上記につながります。

+0

"*これは準拠しないだろうか?" - はい。コンパイルはセマンティクスを保持する必要があります。 – melpomene

+0

printfのようなライブラリ関数であっても、関数のコードが入力される前に引数が評価されるので、それは非適合であると思います。そして、最適化はこのセマンティクスを変更してはいけません、そうですか? –