天天看點

printf 格式串和參數不比對的後果(你想知道的C語言 1.10)

Q: 如下代碼的輸出結果是多少?

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char *argv[])
{
	printf("%d\n", 5.0f);
	return 0;
}
           

A: %d本來對應的是整形,實際參數傳入浮點數5.0f.   5.0f會強制轉換成整形5嗎?

    對于此類問題, 最佳的分析手法是從原理和彙編出發, 不論什麼格式都可以分析出.

    首先, printf是如何處理格式串的, %d格式串會被printf解析出, 并從參數中取一個整數,注意,此整數是強制拿sizeof(int)位元組的資料,并沒有對參數做可适配轉換(比如浮點數去掉小數點變成整數之類的友好轉換)。

    printf 格式串的處理: printf 内部原理和實作 (你想知道的C語言 1.2)

    是以,其實5.0f是啥不重要,重要的是5.0f被存儲的資料二進制形式是什麼.

    按照浮點數的IEEE754标準的轉換算法,5.0f将被轉換成0x40A00000. 是以,上面輸出的整數應該就是1084227584.(注意此處的結論不一定是對的!)

    5.0f是被轉換成0x40A00000, 可以用如下代碼驗證:

/*
   Xi Chen([email protected])
   cxsjabcabc
*/
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

void dump_data(void *data, int bytes)
{
	int i;
	unsigned char *p;

	p = (unsigned char *)data;
	for (i = 0; i < bytes; ++i) {
		printf("%x ", p[i]);
	}
	printf("\n");
}

int main(int argc, char *argv[])
{
	float d = 5.0f;

	dump_data(&d, sizeof(d));

	return 0;
}
           

    https://github.com/cxsjabc/basic/blob/dev/c/_topics/printf/parse_data.c

Q: 實際的運作結果看起來不是上面的數字,而且每次執行還會變動?

A: 彙編代碼如下:

printf 格式串和參數不比對的後果(你想知道的C語言 1.10)

    可以看到參數5.0f被放到了xmm寄存器中, 此為儲存浮點數參數的寄存器. 但是printf内部實作會從堆棧強制找sizeof(int)位元組資料,這将是不确定的資訊.

    在不适用xmm寄存器,而是用堆棧傳遞參數的編譯器環境下,才會得到1084227584.

作者:     陳曦
環境:     MacOS 10.14.5
         Apple LLVM version 10.0.1 (clang-1001.0.46.4)
         Target: x86_64-apple-darwin18.6.0
 
轉載請注明出處