Why used printf the same variable addr twice result in different print values?

Environnment:

  • gcc version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.2))

The conditions under which this occurs are:

  • The printed variable is of float type.
  • There are type conversion errors in the printf.
  • The buffer is refreshed before the printf call.

Build command:

gcc floatTest.c -o floatTest -std=c99(arg c17 c11 c99 the result same as)

Code (also available on Godbolt):

#include <stdio.h>

int main()
{
    float test = 5.0;
    printf("first print   test data is %x test %pn", test, &test);
     //fflush(stdout);
    printf("second print  test data is %x test %pn", test, &test);
}

Output:

first print   test data is 37df33f4 test 0x7ffd37df3508
second print  test data is 37df33f4 test (nil)

I tried printing an integer type, and none of the mentioned issues occurred. The problem only arises when printing a floating-point type, and when the three conditions mentioned above are met, abnormal values are printed. I also tried using different compiler versions, but the results were consistently abnormal. Switching between different C standards yielded the same result. However, note that when I compiled and tested using Visual Studio 2022, the issue did not occur.

5

There is no particular reason why two identical, consecutive function calls with the same undefined behavior (function call argument types do not match the expected function parameter types after default argument promotions for variadic arguments of printf) should produce the same results.

For example, in some ABI (application binary interface) definitions, some function call arguments are passed in registers, with floating point arguments passed to the called function in completely different registers than integer arguments, so the printf call will be printing a junk register value.

One way to print a float in hexadecimal as though it was an integer, is to use a union type containing a float member and an unsigned integer type of the same size as the float. Let us assume that uint32_t is the same size as a float, then:

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

int main()
{
    union
    {
        float f;
        uint32_t u32;
    } test;
    test.f = 5.0;
    printf("first print   test data is %08"PRIx32" test %pn", test.u32, (void *)&test);
     //fflush(stdout);
    printf("second print  test data is %08"PRIx32" test %pn", test.u32, (void *)&test);
}

Example output:

first print   test data is 40a00000 test 0x7ffec4f1683c
second print  test data is 40a00000 test 0x7ffec4f1683c

4

Why used printf the same variable addr twice result in different print values?

From X86-64 ABI (that I guess that you are using) https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf section 3.2.3 Parameter passing we know that:

  • arguments of type float and double is in SSE class
  • arguments of pointer type are in INTEGER class
  • arguments of int type, that %x expects, are also in INTEGER class

Pointer types and int types use the same class. From page 20 of the document the class INTEGER types use the next available register of the sequence %rdi, %rsi, %rdx, %rcx, %r8 and %r9 is used and SSE class use different registers.

        movss   xmm0, dword ptr [rbp - 4]
        cvtss2sd        xmm0, xmm0
        lea     rdi, [rip + .L.str.2]
        lea     rsi, [rbp - 4]
        /* note - rdx not assigned */
        mov     al, 1
        call    printf@PLT

The format string passed to printf is a pointer. When printf starts, it reads the format string from %rdi register that points to the format string.

Then to output the %x printf reads the content of the next register in INTEGER class %rsi and prints it formatted as an hex. The content of this register is populated with the next argument in INTEGER class, so the first printed value 37df33f4 is actually the address of &test reinterpreted as an int. You can confirm this by adding for example printf("%xn", (int)(void*)&test); to your code.

Then the %p format specifier makes printf read a value from the next INTEGER class register %rdx. That register is not touched before the call to printf, so %rdx contains any garbage value that was there before the call to printf.

On the beginning of your program, the %rdx register happens to contain the value 0x7ffd37df3508. That value most probably comes from the 3rd argument to main. On POSIX systems, the C standard library calls the main with 3 arguments main(argv, argc, environ), and all these arguments happen to be in INTEGER class. The address to environ is placed in the %rdx register, and your main code does not override it, so the first printf call happens to print it. You can confirm it by printing extern char **environ; printf("%pn", (void*)environ); and comparing the result.

After the call to printf, the %rdx register happens to contain the value 0. That value is assigned somewhere inside printf source code. You would have to go with a disassembler to get the exact location where it is assigned.

Note that this answer is solely very specific to X86-64 ABI. Also the compiler can detect that the code is invalid and decide to spawn nasal demons instead of producing any sane output.

#include <stdio.h>
extern char **environ;
int main() {
    float test = 5.0;
    printf("first print   test data is %x test %pn", test, (void*)&test);
    printf("second print  test data is %x test %pn", test, (void*)&test);
    printf("&test = %xn", (int)(void*)&test);
    printf("environ = %pn", (void*)environ);
}
first print   test data is 978a72ec test 0x7ffd978a7418
second print  test data is 978a72ec test (nil)
&test = 978a72ec
environ = 0x7ffd978a7418

why this issue does not occur if the buffer is not flushed

Because fflush happens to assign some different value to %rdx register. You would have to go through a disassembler or debugger to find the location where this register happens to be assigned.

6

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật