簡體   English   中英

c 中出現意外的 printf 行為,每次運行時將浮點數打印為十六進制更改值

[英]Unexpected printf behaviour in c, printing float as hex changes value on each run

I wrote some c code to play around with float values in memory, but ended up getting some unexpected output from printf compiling with "gcc (GCC) 12.1.1 20220730" with -std=c11 option.

我不知道它為什么會這樣,並且想知道發生了什么,如果我做錯了什么,以及如何將其轉換為 printf 一個浮點值作為十六進制,如果可能的話,無需先將其轉換為另一種類型?

這是使用的代碼和 output 的不同運行。

代碼:

#include <stdio.h>

int main()
{
    float f3 = 1.1;
    float f4 = 1.0;
    unsigned char *t1 = &f3;

    unsigned *t2 = &f3;

    printf("P1: %x\n", t2[0]);
    printf("P2: %x %x %x %x\n", t1[0], t1[1], t1[2], t1[3]);
    printf("P3: %p\n", &f3);
    printf("P4: %lx, %lx, %llx\n", &f3, f3, f4);

    printf("T1: %f, %f, %lx, %lx\n", f3, f4, f4, f3);
    printf("T2: %x, %lx\n", f4, f3);
    printf("T3: %x, %lx\n", f3, f4);
    
    return 0;
}

主要問題似乎是將浮點數打印為十六進制:

printf("%x\n", f3);

Output 1:

P1: 3f8ccccd                            // as expected
P2: cd cc 8c 3f                         // as expected
P3: 0x7ffc667d4d40                      // as expected
P4: 7ffc667d4d40, 3ff19999a0000000, 0   // pointer value as expected, but second and third isn't. Values stay the same after each run

T1: 1.100000, 1.000000, 5556db09b2a0, 0 // first two values as expected, second and third isn't, Values do not stay the same after each run
T2: db09b2a0, 0                         // this value keeps changing after each run
T3: db09b2a0, 0                         // same as above, but should be different? Also changes after each run.

Output 2:

P1: 3f8ccccd
P2: cd cc 8c 3f
P3: 0x7ffef87ebb00
P4: 7ffef87ebb00, 3ff19999a0000000, 0

T1: 1.100000, 1.000000, 55fb6a1962a0, 0
T2: 6a1962a0, 0
T3: 6a1962a0, 0

Output 3:

P1: 3f8ccccd
P2: cd cc 8c 3f
P3: 0x7ffdb2026640
P4: 7ffdb2026640, 3ff19999a0000000, 0

T1: 1.100000, 1.000000, 564dd210c2a0, 0
T2: d210c2a0, 0
T3: d210c2a0, 0

主要問題似乎是將浮點數打印為十六進制:

 printf("%x\n", f3);

是的,因為%x格式說明符需要一個unsigned int作為參數,但是您傳入的float被提升為double

使用錯誤的格式說明符會觸發未定義的行為,在這種情況下表現為奇怪的 output。 至於幕后發生的事情,浮點值通常使用浮點寄存器傳遞給 function,而 integer 值通常被推入堆棧。

這也是無效的:

printf("P1: %x\n", t2[0]);

因為它會導致嚴格的混疊違規。 這基本上意味着您不能像訪問另一種類型一樣訪問一種類型的字節,除非目標類型是charunsigned char

打印浮點類型的字節表示的正確方法是讓unsigned char *指向第一個字節,然后遍歷字節並打印每個字節。

在所有“意外輸出”的情況下,您傳遞的參數的類型與格式說明符不匹配。 你告訴printf()期待一件事,但傳遞另一件事。 那總是未定義的行為,推測特定的 output 是如何產生的在很大程度上是沒有意義的。 此外,將float傳遞給printf()會將其提升為 double - 因此您嘗試檢查的表示形式將與原始float變量的表示形式不同。
printf()也在運行時處理這些類型 - 編譯器的類型檢查在這里無法幫助您,適用的是printf()的規則,而不是編譯器的 - 它是可變參數 function,就編譯器而言可以采用任意數量的任意類型的 arguments。 也就是說,許多編譯器會檢查 stdio 格式說明符,因為它們是標准庫中定義明確的部分——您可能需要啟用特定警告或更高的警告級別才能啟用編譯器的檢查。

通常要檢查代表浮點值的位,您需要獲取浮點的地址,將該地址轉換為相同寬度的 integer 指針,然后取消引用它。
然而,嚴格的別名規則使得(嚴格)未定義,因此雖然*(uint32_t*)&f1很可能會產生預期的 integer 值,但您不能依賴它。

一個定義明確的解決方案是將字節作為unsigned char訪問,或者將字節復制(例如使用memcpy() )到相同大小的 integer object 。

例如:

#include <stdio.h>
#include <inttypes.h>
#include <stdint.h>
#include <string.h>

int main()
{
    float f1 = 1.0f ;
    float f2 = 1.1f ;
    uint32_t b1 = 0u  ;
    uint32_t b2 = 0u  ;
    
    memcpy( &b1, &f1, sizeof(b1) ) ;
    memcpy( &b2, &f2, sizeof(b2) ) ;
        
    printf( "f1: %f @%p = %"PRIx32" (%02x %02x %02x %02x)\n", 
            f1, &f1, b1,
            ((unsigned char*)&f1)[0],
            ((unsigned char*)&f1)[1],
            ((unsigned char*)&f1)[2],
            ((unsigned char*)&f1)[3] ) ;
                                                                
    printf( "f2: %f @%p = %"PRIx32" (%02x %02x %02x %02x)\n", 
            f2, &f2, b2,
            ((unsigned char*)&f2)[0],
            ((unsigned char*)&f2)[1],
            ((unsigned char*)&f2)[2],
            ((unsigned char*)&f2)[3] ) ;
            
    return 0;
}

輸出:

f1: 1.000000 @0x7fffe3e06668 = 3f800000 (00 00 80 3f)
f2: 1.100000 @0x7fffe3e0666c = 3f8ccccd (cd cc 8c 3f)

(顯然地址會有所不同)。

您還可以通過聯合來實現類型雙關:

typedef union 
{
    float f ;
    uint32_t u ;

} uFloatIntPun ;

然后你可以例如:

uFloatIntPun pun ;
pun.f = f1 ;
printf( "%"PRIx32"\n", pun.u ) ;

當然,如果您只想檢查特定變量的位置和內部表示以及字節順序,那么在符號調試器中觀察它們要簡單得多且不易出錯。

暫無
暫無

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

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