2017-02-25 29 views
11

我不能cppreference.com找出printf說明書的以下部分的意圖:printf%n在同一個調用中的值 - 無意義?

有每個轉換 指定符的動作後的序列點;這允許存儲在同一 變量多個%n個結果和打印相同 呼叫內的%N較早存儲的值。

此全文如果一個(或甚至幾個)%n轉換指定(一個或多個)的結果可能在相同的printf語句來打印出來。 但我不能瞭解其所能實現的,因爲進入printf的身體前通過的printf調用所有參數進行評估(有說法評估後的序列點)。因此,到一個%n會寫評價一個變量的值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」是否有機會「在相同的調用中早先打印由%n存儲的值」,那麼不是printf規範的相應部分是無意義的還是令人誤解的?

什麼是c99 standard語句的實際意義:

7.19.6格式化輸入/輸出功能 (1)格式的輸入/輸出功能應表現爲,如果有一個序列點 後與每個說明者相關的動作。

難道是爲了減少未定義行爲的「機會」嗎?

問題是標有C++和C,因爲我認爲這個主題適用於以同樣的方式兩種語言。

+0

C11 ref是'7.21.6 1'。 – chux

+1

cppreference現在不那麼沒有意義 – Cubbi

回答

4

你的代碼確實只打印你已經正確識別的原因零。

在標準的聲明仍然是必要的,因爲毯措辭的其他地方,如果一個對象被寫入不止一次沒有插入序列點程序的行爲是不確定的。實際上,聲明有必要說你的代碼沒有未定義的行爲(不像i = i++;)。

+0

這就是說有一個序列點的部分。但是他突出顯示的部分的重點是什麼?這不可能,是嗎? – Barmar

+0

@Barmar:當然,你不能*閱讀*早期的寫作,但粗體部分是序列點唯一重要的結果!如果您只寫入不同的輸出位置,則不需要序列點。 –

+0

@Kerrek這是否意味着printf()不能寫成普通的用戶定義函數? –

8

這可能是瘋了,但我認爲以下是合法的:

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

%hhn需要一個字符指針。它將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中。這會不符合?

現代編譯器已經在printf()執行小的優化,例如,當轉換到printf("Hello world\n");puts("Hello world");printf("\n");fputchar('\n');。他們還檢查格式字符串和參數一致性...進一步優化會導致上述情況。

+0

「*這是不符合嗎?*」 - 是的。編譯必須保留語義。 – melpomene

+0

我想這是不符合的,因爲 - 即使對於像printf這樣的庫函數,參數在輸入funcion的代碼之前被評估。優化不能改變這種語義,對吧? –

相關問題