[英]How does the function printf works in C?
我在測試函數printf時遇到了一個問題:
首先我寫這樣的代碼:
int main(void)
{
char a = 'a';
printf("a = %f\n", a);
return 0;
}
輸出是
然后我寫代碼:
int main(void)
{
float b = 'a';
printf("b = %f\n", b);
return 0;
}
輸出是
然后我寫代碼:
int main(void)
{
char a = 'a';
float b = 'a';
printf("b = %f\n", b);
printf("a = %f\n", a);
return 0;
}
輸出是
所以我很困惑為什么在第一個程序中a
= 0.000000
而在第三個程序中a
= 97.000000
?
函數printf()
如何工作?
符號%f
, %d
工作?
更新:這個做一些更多的研究后,似乎之間的差別float
和int
內存表示是不負責的三個程序的行為的人。
我查看了第三個程序的目標代碼,我發現了奇怪行為的原因:浮點參數被發送到其他注冊表/堆棧而不是整數。 printf
依賴於它並在其他位置查找它們,而不是printf
的被調用者(即main
方法)放置參數。
這是第三個程序的相關反匯編(對於x86_64架構):
0000000100000f18 leaq 0x71(%rip), %rdi ## literal pool for: "b = %f\n"
0000000100000f1f movsd 0x61(%rip), %xmm0 ## b variable gets sent to xmm0
0000000100000f27 movl $0x0, -0x4(%rbp)
0000000100000f2e movb $0x61, -0x5(%rbp) ## a variable gets placed on the callee stack
0000000100000f32 movsd %xmm0, -0x10(%rbp)
0000000100000f37 movsd -0x10(%rbp), %xmm0
0000000100000f3c movb $0x1, %al
0000000100000f3e callq 0x100000f66 ## symbol stub for: _printf
0000000100000f43 leaq 0x4e(%rip), %rdi ## literal pool for: "a = %f\n"
0000000100000f4a movsbl -0x5(%rbp), %esi
0000000100000f4e movl %eax, -0x14(%rbp)
0000000100000f51 movb $0x0, %al
0000000100000f53 callq 0x100000f66 ## symbol stub for: _printf
並且printf
依賴於此,它假定被調用者在xmm0
/ xmm1
/ etc寄存器中放置了%f
參數,並且三個程序的行為如下所示:
printf
在xmm0寄存器中查找%f
參數,但是當我們在程序的開頭時,寄存器是干凈的並且main
已經放入a
eax
,因此xmm0
保持零值,這是什么printf
打印 main
正確地將b
放在xmm0
, printf
從那里取出,打印正確的值 b
, xmm0
寄存器將保持該值,並且因為printf
沒有弄亂寄存器,當它第二次被調用時,它再次從xmm0
取出,在第一次之后保持完整printf
電話。 所以關於調用者和被調用者的約定都是關於調用者發送整數和浮點數以及被調用者嘗試拾取它們的地方。
原始響應:在第一個程序中,您嘗試打印浮點數,但是傳遞一個int(char是一個較小的int)。 由於int和浮點數具有不同的二進制表示,因此int 97(對應於字符'a')對應於非常小的浮點數:1.36E-43,其打印為零。
這是97的二進制表示(編譯器在調用函數時將任何1字節的char擴展為4字節的參數)
00000000 00000000 00000000 01100001
IEEE 754是浮點/雙數的二進制表示的標准格式,您可以在這里使用在線轉換器,並且當它被解釋為int或float時,您可以看到相同的二進制數如何具有不同的值。
%f表示字符的Float%c
The 97 which you have got is the ASCII value for 'a'
根據最新的'C'標准,它是一種未定義的行為。 檢查c標准草案的7.21.6.1第9頁。
如果轉換規范無效,則行為未定義.282)如果任何參數不是相應轉換規范的正確類型,則行為未定義。
因此,當事物被認為具有未定義的行為時,任何事情都是可能的,並且行為可能因編譯器而異。 'C'讓你用斧頭剪你的腳趾,但這不是你應該做的。
%f
代表浮動。 您必須使用%c
作為字符。
如果你使用
printf("a = %c\n", a);
你會得到這個角色。
因此,如果您將第一個代碼更改為
int main(void)
{
char a = 'a';
printf("a = %c\n", a);
return 0;
}
你會得到輸出
a
之間的區別
printf("%f\n, 97.0);
和
printf("%c\n, 'a');
是printf函數根據您給出的%X
從堆棧中讀取其參數,並解釋它們(用於顯示)。
對於%c
printf需要一個char作為參數,因此它將讀取一個char(一個字節,但通常實際上是一個int,它依賴於實現)並顯示它(如果提供了int,它將顯示不太重要的字節)。
對於%f
printf,需要一個float(以字節為單位的sizeof(float)
,通常是gcc / Intel處理器上的4個字節)。
如果使用gcc進行編譯,請使用-Wall
選項,該選項會在%X
格式和參數類型不匹配時發出警告。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.