[英]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
,它有一個格式說明符的定義列表,每個格式說明符告訴它要使用的類型。 %d
是int
。 %f
是double
。 %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)
:
字符串"..."
的地址被壓入堆棧。
每個變量a
, b
, c
被壓入堆棧:
小於4個字節的整數值在壓入堆棧時會擴展為4個字節。
小於8個字節的浮點值在壓入堆棧時會擴展為8個字節。
程序計數器(或稱其為-指令指針)跳轉到內存中函數printf
的地址,然后從那里繼續執行。
對於傳遞給函數printf
的第一個參數所指向的字符串中的每個%
字符,該函數從堆棧中加載相應的參數,然后-根據%
字符后指定的類型-計算要打印的數據。
當調用printf("%f",c)
:
字符串"%f"
的地址被壓入堆棧。
變量c
值擴展為4個字節,並壓入堆棧。
程序計數器(或稱其為-指令指針)跳轉到內存中函數printf
的地址,然后從那里繼續執行。
函數printf
在第一個參數指向的字符串中看到%f
,並從堆棧中加載8個字節的數據。 如您可能理解的那樣,在好的情況下這會產生“垃圾數據”,而在壞的情況下會產生內存訪問沖突。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.