printf("%f\n",5); printf("%d\n",5.01); printf("%f\n", (float)5); printf("%f\n", 5.f);
輸出結果:程序員
看到結果,會感受很是奇怪。1處怎麼會輸出0呢?2又爲什麼會顯示這麼大的一個數呢?面試
解釋:安全
1,之因此沒輸出5,這是C語言設計的緣由。
2,之因此輸出0,這是計算機體系結構的問題。函數
具體來講:spa
printf函數不會進行任何類型轉換,它只是從內存中讀出你所提供的元素的值(按照%d,%f等控制字符提示的格式)。C語言設計中,int類型通常是32bit或者16bit,而float通常是64bit,而且有可能使用科學計數保存。這點就和huhugo88所說同樣,5在內存中爲00000000,00000101。並且5通常都在靜態區,程序的靜態存儲區默認是0,那麼當用%f來讀時,就會讀64bit,也就是會讀以前的不少位0,最後按照(有效數字)×(基數2)pow(指數)的方式來取數,天然結果是0.net
之因此Vc中不容許這種狀況,而有些編譯器就容許這麼輸出就是編譯器設置的問題。按理說,這樣訪問內存是屬於越界訪問,應該禁止。不過只是讀,傷害性不大而已。 對於單精度浮點數(32bit),很多c語言編譯系統以24位表示小數部分(包括1bit符號位),以8位表示指數部分。 ==========================printf("%d\n",5.01); 爲何輸出一個大數?在講這個題目以前,預備知識,講一下,printf函數,輸入參數是讀入緩衝區保存,再按照%?的格式從緩衝區中讀出數據,並據此格式解釋數據。設計
有了這個知識以後,在講程序員面試寶典上看到一個題:3d
#include "stdio.h" int main(int argc, char* argv[]) { printf("%d\n",5.01); return 0; }
輸出結果爲:188978561
而後開始研究爲何會是這個數?code
5.01是double類型,內存中佔8個字節,保存在緩衝區。而%d爲整型,佔4個字節,printf從緩衝區中讀入4字節,先讀到低32位的數據。也就是說printf輸出的應該是5.01以double類型保存數劇的低32位。爲了檢驗此結果是否正確,對比5.01在內存中的表示與輸出。blog
#include "stdio.h" int main(int argc, char* argv[]) { double d = 5.01; int *p = (int *)(&d); int rst = 1889785610; printf("1).%x\n",*p); printf("2).%x\n",*(p+1)); printf("3).%x\n",rst); return 0; }
輸出爲:
1).0x70a3d70a
2).0x40140a3d
3).0x70a3d70a
這也就證實了%d輸出了5.01的低32低。5.01的double類型,在內存的的表示爲0x40140a3d70a3d70a。
事情看似也就完成了。
我又想,若是輸入是浮點類型的5.01f,又會發生什麼呢?
#include "stdio.h" int main(int argc, char* argv[]) { float f = 5.01f; int *p = (int *)(&f); printf("1).0x%x\n",*p); printf("2).0x%x\n",5.01f); return 0; }
輸出:
1).0x40a051ec
2).0x80000000
咱們發現,此時輸出的並非浮點類型5.01f的內存的表示,這是爲何呢?
而後看到一個說法,是printf會把%f按double類型輸出,也就是說會把參數float型的轉成double型再輸出。
但如今並非%f,固然用%f顯示的是正確的結果。因而我猜想,printf是將所在float型讀入的數據都自動的轉化爲double型了,而後%f就按double處理,而咱們這是%d,因此顯示的爲float轉化爲double型後的低4字節。
驗證此想法:
#include "stdio.h" int main(int argc, char* argv[]) { double f = 5.01; int *p = (int *)(&f); printf("1).0x%x\n",*p); printf("2).0x%x\n",*(p+1)); printf("3).0x%x\n",5.01f); return 0; }
輸出:
1).0x70a3d70a
2).0x40140a3d
3).0x80000000
可是咱們發現結果並不同,因而我又猜測,也是許printf將float轉化爲double的方式與默認的方式不同
5.01d的默認的表示爲:0x40140a3d70a3d70a,在上面已經說明了
#include "stdio.h" int main(int argc, char* argv[]) { printf("0x%8x\n0x%8x\n",5.01f); return 0; }
輸出爲:
0x80000000
0x40140a3d
與是發現printf將5.01f->5.01d的表示是:0x40140a3d80000000
接着就是看這兩個值是否都是爲5.01了:
#include "stdio.h" int main(int argc, char* argv[]) { int d1[2], d2[2]; d1[0]=0x80000000; d1[1]=0x40140a3d; d2[0]=0x70a3d70a; d2[1]=0x40140a3d; double *p1 = (double *)d1; double *p2 = (double *)d2; printf("1).%f\n",*p1); printf("2).%f\n",*p2); return 0; }
輸出爲:
1).5.010000
2).5.010000
也就證實了0x40140a3d80000000,與0x40140a3d70a3d70a都是5.01d在機器中的表示。前者爲5.01f(0x40a051ec)由printf轉化爲double後的表示,後者爲5.01d的默認的表示。
總結:printf將輸入的浮點型參數全都自動轉化爲雙精度型,且與默認的雙精度的表示方法是不一樣的。最重要一點,printf不安全,類型不安全,要是類型不對了,也許咱們就掛了
本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/fengyunjh/archive/2011/03/07/6230164.aspx
=======================================================================================================================
經過以上解釋,咱們大體明白:
1. printf輸出float型時,會自動轉化成double型;
2. 因爲存儲時,都是先低位,再高位,同時通過轉化成double,前面會取不少0(越界訪問);
3. 5.01,打印時按照int來取,只取前四個字節。