簡體   English   中英

printf函數如何在C中運行?

[英]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工作?

更新:這個做一些更多的研究后,似乎之間的差別floatint內存表示是不負責的三個程序的行為的人。

我查看了第三個程序的目標代碼,我發現了奇怪行為的原因:浮點參數被發送到其他注冊表/堆棧而不是整數。 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參數,並且三個程序的行為如下所示:

  1. 在第一個程序中, printf在xmm0寄存器中查找%f參數,但是當我們在程序的開頭時,寄存器是干凈的並且main已經放入a eax ,因此xmm0保持零值,這是什么printf打印
  2. 在第二個程序main正確地將b放在xmm0printf從那里取出,打印正確的值
  3. 在第三個程序中,由於首先打印bxmm0寄存器將保持該值,並且因為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表示浮動的替換標記。

為了替換char,你需要%c

這是一個列表 ,告訴您每種類型的適當替換標記是什么。

%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.

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