簡體   English   中英

C變量分配問題

[英]C variable assignment issue

我認為標題不太適合我的問題。 (我很感激,如果有人建議編輯的話)我正在用“學習C的艱難方法”來學習C。 我正在使用printf使用格式說明符輸出值。 這是我的代碼段:

#include <stdio.h>

int main()
{
    int x = 10;
    float y = 4.5;
    char c = 'c';

    printf("x=%d\n", x);
    printf("y=%f\n", y);
    printf("c=%c\n", c);
    return 0;
}

這按我預期的那樣工作。 我想測試它在轉換方面的行為。 因此,一切正常,除非我通過將char轉換為float來打破這一行:

printf("c=%f\n", c);

好的,我正在編譯它,這是輸出:

~$ cc ex2.c -o ex2
ex2.c: In function ‘main’:
ex2.c:13:3: warning: format ‘%f’ expects argument of type ‘double’, but argument 2 has type ‘int’ [-Wformat=]
printf("c=%f\n", c);
^

該錯誤清楚地告訴我,它無法從int轉換為float ,但這不會阻止編譯器生成目標文件,而令人困惑的部分是在此處運行目標文件:

~$ ./ex2
x=10
y=4.500000
c=c
c=4.500000

如您所見, printf會打印之前打印的最后一個float值。 我用y其他值測試了它,並在每種情況下為c打印y的值。 為什么會這樣?

您的編譯器會警告您有關未定義的行為。 什么都可能發生。 從看起來有用到惡魔般的一切。 關於這個主題的一個很好的參考是每個C程序員應該了解的未定義行為

通常,int可以將double轉換為double:

int i = 10;
double d = i; //works fine

printf是一種特殊的功能。 由於可以接受任意數量的參數,因此類型必須完全匹配。 當給定一個char ,它被晉升為int傳入時, printf ,然而,使用%f你給它得到一個double 那是行不通的。

這是如何實現自己的可變參數功能,摘自此處

int add_nums(int count, ...) 
{
    int result = 0;
    va_list args;
    va_start(args, count);
    for (int i = 0; i < count; ++i) {
        result += va_arg(args, int);
    }
    va_end(args);
    return result;
}

count是后面的參數數。 函數沒有被告知就無法知道這一點。 printf可以從字符串中的格式說明符推導出來。

另一個相關部分是循環。 它將執行count時間。 每次,它使用va_arg來獲取下一個參數。 注意它如何給va_arg類型。 假定為這種類型。 為了使va_arg調用正常工作,該函數需要依賴調用方傳遞一些提升為int

對於printf ,它有一個格式說明符的定義列表,每個格式說明符告訴它要使用的類型。 %dint %fdouble %c也是int因為char被提升為int ,但是printf在形成輸出時需要將該整數表示為字符。

因此,任何采用可變參數的函數都需要調用者的配合。 可能出錯的另一件事是給printf太多了格式說明符。 它將盲目地獲得下一個參數,但是沒有更多參數了。 嗯,哦。

如果還不夠,則該標准在C11(N1570)§7.21.6.1/ 9中明確表示fprintf (用它定義了printf ):

如果任何參數都不是相應轉換規范的正確類型,則行為未定義。

總之,感謝您的編譯器在不與printf合作時警告您。 它可以使您免於遭受一些非常糟糕的結果。

由於printf是varargs函數,因此無法將參數自動轉換為該函數期望的類型。 調用varargs函數時,參數會進行某些標准轉換,但是這些轉換不會在不同的基本類型之間進行轉換,例如在整數和浮點數之間進行轉換。 確保printf的每個參數的類型適合於相應的格式說明符,這是程序員的責任。 一些編譯器會警告不匹配,因為它們會額外檢查printf ,但是該語言不允許它們轉換類型printf只是一個庫函數,對其調用必須遵循與任何其他函數相同的規則。

這是一個非常籠統的描述,根據所使用的編譯器的不同可能略有不同。


當調用printf("...",a,b,c)

  • 字符串"..."的地址被壓入堆棧。

  • 每個變量abc被壓入堆棧:

    小於4個字節的整數值在壓入堆棧時會擴展為4個字節。

    小於8個字節的浮點值在壓入堆棧時會擴展為8個字節。

  • 程序計數器(或稱其為-指令指針)跳轉到內存中函數printf的地址,然后從那里繼續執行。

  • 對於傳遞給函數printf的第一個參數所指向的字符串中的每個%字符,該函數從堆棧中加載相應的參數,然后-根據%字符后指定的類型-計算要打印的數據。


當調用printf("%f",c)

  • 字符串"%f"的地址被壓入堆棧。

  • 變量c值擴展為4個字節,並壓入堆棧。

  • 程序計數器(或稱其為-指令指針)跳轉到內存中函數printf的地址,然后從那里繼續執行。

  • 函數printf在第一個參數指向的字符串中看到%f ,並從堆棧中加載8個字節的數據。 如您可能理解的那樣,在好的情況下這會產生“垃圾數據”,而在壞的情況下會產生內存訪問沖突。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM