本篇內(nèi)容主要講解“c語言整型變量怎么用浮點型輸出”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學(xué)習(xí)“c語言整型變量怎么用浮點型輸出”吧!
成都一家集口碑和實力的網(wǎng)站建設(shè)服務(wù)商,擁有專業(yè)的企業(yè)建站團隊和靠譜的建站技術(shù),10余年企業(yè)及個人網(wǎng)站建設(shè)經(jīng)驗 ,為成都上1000家客戶提供網(wǎng)頁設(shè)計制作,網(wǎng)站開發(fā),企業(yè)網(wǎng)站制作建設(shè)等服務(wù),包括成都營銷型網(wǎng)站建設(shè),品牌網(wǎng)站建設(shè),同時也為不同行業(yè)的客戶提供網(wǎng)站設(shè)計制作、網(wǎng)站建設(shè)的服務(wù),包括成都電商型網(wǎng)站制作建設(shè),裝修行業(yè)網(wǎng)站制作建設(shè),傳統(tǒng)機械行業(yè)網(wǎng)站建設(shè),傳統(tǒng)農(nóng)業(yè)行業(yè)網(wǎng)站制作建設(shè)。在成都做網(wǎng)站,選網(wǎng)站制作建設(shè)服務(wù)商就選成都創(chuàng)新互聯(lián)。
關(guān)鍵詞:內(nèi)存存儲,類型強制轉(zhuǎn)換,反匯編
變量
變量來源于數(shù)學(xué),是計算機語言中能儲存計算結(jié)果或能表示值抽象概念。在諸如C語言等高級語言中,變量的使用屏蔽了數(shù)據(jù)的底層細節(jié),使得高級語言程序員不必像匯編程序員那樣關(guān)心數(shù)據(jù)與硬件之間的關(guān)系。為了探究C語言中變量在內(nèi)存中的存儲形式,可以借助反匯編查看匯編語言以及內(nèi)存數(shù)據(jù)。
變量在內(nèi)存中的存儲形式
在內(nèi)存中,無論哪種數(shù)據(jù)類型的數(shù)據(jù),都是以相應(yīng)長度的二進制碼存取。從內(nèi)存取數(shù)據(jù)是,如果不按照定義數(shù)據(jù)類型的方式取數(shù)據(jù),所取數(shù)據(jù)就會錯誤。
2.1 用反匯編查看變量內(nèi)存數(shù)據(jù)
(1) 實驗代碼如下。在賦值部分打點后調(diào)試,轉(zhuǎn)入反匯編。
(2) 在監(jiān)視窗口查看變量的內(nèi)存地址,并在內(nèi)存窗口中查看數(shù)據(jù)。
整形變量_4ByteData的數(shù)據(jù)在以內(nèi)存地址0x0023FA58起始的四個Byte中存放:【注意】 Intel處理器是小端機,數(shù)據(jù)高位在高地址,地位在地址。所存數(shù)據(jù):4Bytes的十六進制數(shù) 0x12345678 單精度浮點型變量fl的數(shù)據(jù)在內(nèi)存中的存儲: 雙精度浮點型變量df的數(shù)據(jù)在內(nèi)存中的存儲: 字符型變量ch的數(shù)據(jù)在內(nèi)存中存儲;
結(jié)論:
(1) 局部變量存儲在函數(shù)棧中,且該棧向低地址生長,所以先定義的局部變
量在較高內(nèi)存地址(比如_4ByteData在0x0023FA58,ch在0x0023FA33),并且局部變量之間并非緊密排布,而是由8個Byte 的cc數(shù)據(jù)隔開。變量周圍塞些CCCCCCCC,這可能是編譯器提供的一種保護機制,越界了好出斷言。通過網(wǎng)上查找資料,這是VC在Debug時給變量留出空間,用來檢查stack overflow。
用release調(diào)試,就不會有多余的cc。(但是注意這里char型變量與之前的局部變量之間仍有數(shù)據(jù),我猜測這里是應(yīng)為有變量對齊的緣故,char型變量也占了4byte,只不過多余的3byte由其他數(shù)據(jù)填充。) (2) 通過觀察不同類型的變量在內(nèi)存中的存儲情況,可以發(fā)現(xiàn):
在32位機器,VS2010 IDE中,一個int型數(shù)據(jù)占4B,float型變量占4B,double型變量占8B,char型占1B。
2.2 IEEE754 單精度數(shù)的格式
單精度浮點數(shù)占據(jù)4個字節(jié),4個字節(jié)的分配如下:
(a)第一位為符號位,0表示正,1表示負;
(b)第2~9位為階碼,采用移碼表示;
(c)第10~32位為尾數(shù),采用原碼表示。
給定32位串,如何轉(zhuǎn)換成十進制數(shù): 假設(shè)內(nèi)存中存在32位串:00 00 00 3f,因為INTELCPU采用little endian存儲方式,所以其真實的值為:
0x 3f 00 00 00。將其寫成二進制形式:
(1)第一步,化為二進制 0 01111110 0000000 00000000 00000000 (2)第二步 該浮點數(shù)為正數(shù),階碼 01111110,移碼表示(126-127) = -1 尾數(shù) 0000000 00000000 00000000 因為在IEEE754中,單精度浮點數(shù)有規(guī)格化處理,所以其真正尾數(shù)部分為 1.0000000 00000000 00000000,其中‘.’為小數(shù)點
(3) 第三步
根據(jù)公式寫出實際數(shù)值大小 0.10000000 00000000 0000000 化為二進制:0.5
2.3 IEEE754 雙精度數(shù)的格式
長實數(shù)也稱雙精度數(shù)符號位1位,階碼11位,尾數(shù)52位 給定32位串,如何轉(zhuǎn)換成十進制數(shù): 假設(shè)內(nèi)存中存在64位串:00 00 00 0000 00 e0 3f,因為INTEL CPU采用littleendian存儲方式,所以其真實的值為: 0x 3f e0 00 00 00 00 00 00。將其寫成二進制形式:
(1)第一步
0 01111111110 0000 00000000 00000000 00000000 00000000 0000000000000000
(2)第二步
該浮點數(shù)為正數(shù),階碼 01111111110,移碼表示(1022-1023) = -1 尾數(shù) 0 因為在IEEE754中,單精度浮點數(shù)有規(guī)格化處理,所以其真正尾數(shù)部分為 1.0,其中‘.’為小數(shù)點
(3) 第三步
根據(jù)公式寫出實際數(shù)值大小 0.10 化為二進制:0.5
格式化輸出
3.1 printf函數(shù)調(diào)用的一般形式
printf函數(shù)是一個標準庫函數(shù),它的函數(shù)原型在頭文件“stdio.h”中。但作為一個特例,不要求在使用 printf 函數(shù)之前必須包含stdio.h文件。printf函數(shù)調(diào)用的一般形式為:printf(“格式控制字符串”, 輸出表列)。 其中格式控制字符串用于指定輸出格式。格式控制串可由格式字符串和非格式字符串兩種組成。格式字符串是以%開頭的字符串,在%后面跟有各種格式字符,以說明輸出數(shù)據(jù)的類型、形式、長度、小數(shù)位數(shù)等。如: “%d”表示按十進制整型輸出; “%ld”表示按十進制長整型輸出; “%c”表示按字符型輸出等 非格式字符串原樣輸出,在顯示中起提示作用。輸出表列中給出了各個輸出項,要求格式字符串和各輸出項在數(shù)量和類型上應(yīng)該一一對應(yīng)。
3.2 類型不對應(yīng)下的格式輸出
_4ByteData = 0x12345678; //Hexadecimal:0x12345678 對應(yīng)Decimal:305419896
fl = 0.5; df = 0.5; ch = 65; printf("%d\n",_4ByteData); printf("%c\n",_4ByteData); printf("%d\n",ch); printf("%f\n",&_4ByteData); printf("%d\n",fl); printf("%d\n",df);
分析:
可以看到同樣是int型變量,printf("%d\n",_4ByteData);與
printf("%c\n",_4ByteData);其結(jié)果分別為305419896(0x12345678對應(yīng)的十進制數(shù)),而后者只取了4字節(jié)數(shù)據(jù)的最低一個字節(jié)0x78,所以打印出了AISII碼0x78對應(yīng)的字符’x’。
聯(lián)系C語言中指針的用法,我做出假設(shè):格式輸出函數(shù)printf()根據(jù)類型字符%以及變量名,就可以根據(jù)數(shù)據(jù)首地址+讀取長度的方式輸出數(shù)據(jù)。
進一步發(fā)現(xiàn):
但是對字符型變量ch使用類型字符%d輸出,得到的是其ASCII碼的十進制數(shù),如果按照上述假設(shè),會輸出以ch地址起始的4B的數(shù)據(jù)(這將是一個錯誤數(shù)據(jù))。
但實驗結(jié)果是正確輸出了ch字符的ACSII碼的十進制數(shù)。
再對浮點數(shù)做實驗:
用類型字符%d輸出單精度數(shù),雙精度數(shù),結(jié)果均為0。 由上文2.1可知單精度浮點變量fl(十進制0.5)在內(nèi)存中占4B,機器碼是
0x00 00 00 3f(小端機)。現(xiàn)對整數(shù)_4ByteData賦值0x3f000000,并使用類型字符%f對該整數(shù)輸出,查看結(jié)果:
結(jié)果仍是0。這說明即使內(nèi)存中數(shù)據(jù)存儲的內(nèi)容一樣,但是使用類型字符%f對整型變量輸出,其結(jié)果仍然不是浮點數(shù)! 綜上,原假設(shè)值得懷疑!
printf()函數(shù)類型不對應(yīng)下的格式輸出進一步研究
4.1用%d輸出float類型數(shù)據(jù)
float fl=0.5;如果用printf("%d",fl);輸出的是0。 但float型用%d輸出是否一定是0呢,答案肯定不都是0(如下圖)。
為什么 0.5 用%d輸出的是0?
分析如下:
首先來了解下printf的輸出格式,int 和 long int 都是32位的,用%d輸出;float 、double都是%f輸出,但 float 是32位的,double 是64位的,所以在參數(shù)傳遞的時候C語言統(tǒng)一將 float 類型數(shù)值傳換為double 類型再傳入 printf 函數(shù)。如果是32位整型則輸出格式為%lld。
下面來講一下 float fl=0.5f ;printf("%d",fl)輸出為0的情況:
%d只輸出低32位的數(shù)據(jù),并將這些32位二進制以十進制數(shù)輸出,編譯器首先將 0.5從float類型轉(zhuǎn)換為double類型,0.5在內(nèi)存中的存放方式是0x3f000000,轉(zhuǎn)換成double類型在內(nèi)存中的數(shù)據(jù)就是這個0x3fe0000000000000,這個內(nèi)存數(shù)據(jù)可以很明顯看出低32位全是0,而%d則只能截取到低32位,所以這個以%d輸出0.5的數(shù)值當(dāng)然是 0了。如大家不相信可以用%lld 輸出看看,這個%lld就很讀到低64位數(shù)據(jù),讀出的結(jié)果就是0x3fe0000000000000,在屏幕上看到一個很大的十進制數(shù)。(這里用%llx顯示十六進制數(shù)更直觀)
如果我一定要輸出0.5在內(nèi)存中的存放方法怎么辦呢?
可以用printf("%d",*(int *)&fl);這里做了一下處理,不是直接把fl傳進來,把fl所在地址里的內(nèi)容處理了一下,不管fl是什么類型,只對地址進行操作,利用(int *)&lf,將fl所在地址中的內(nèi)容0x3f000000直接當(dāng)成 int 類型傳給printf,int 的類型數(shù)據(jù)不會再轉(zhuǎn)成double類型了,所以輸出正常,這個只是針對浮點型數(shù)據(jù)只占低32位,如果輸出64位還得用%lld格式控制輸出。
如果用printf("%d",(int)fl),輸出行不行?
這個強制類型轉(zhuǎn)換只針對fl的數(shù)據(jù)類型進行轉(zhuǎn)換,0.5轉(zhuǎn)換 int 類型是0,而上面的*(int *)&a,是對內(nèi)存中的實際存儲數(shù)據(jù)進行操作,蔽開數(shù)據(jù)類型這一層面,只將這個數(shù)據(jù)0x3f000000直接轉(zhuǎn)成int類型輸出。而(int)fl,要先看fl的類型,C語言會根據(jù)所要數(shù)據(jù)類型,對內(nèi)存存儲的數(shù)據(jù)進行改變,以便可以用int類型正確解析內(nèi)存數(shù)據(jù)。 如果用printf("%d",(float)fl),輸出什么,輸出的是0,這個只是將fl的float類型還轉(zhuǎn)成float類型,還是要自動轉(zhuǎn)成doube類型,傳給printf函數(shù)。
為什么float非要轉(zhuǎn)成double類型呢?
因為printf格式控制浮點型輸出只有%f,所以統(tǒng)一按doube類型輸出,不像整型有32位的%d或%ld,64位的有%lld,這就將32位整型和64位整型用不同的格式控制分開了,而%f則沒有,所以printf輸出的浮點數(shù)其實是統(tǒng)一遍歷了64位內(nèi)存,如果float傳入printf沒有進行轉(zhuǎn)換,那么printf輸出高32位數(shù)據(jù)將不可預(yù)知,printf輸出結(jié)果也就不正確了,因此傳入printf的浮點數(shù)都會被編譯器隱含轉(zhuǎn)成double類型。
4.2 int類型%f格式輸出
如果定義了inta=0x3f000000;用printf("%f",a)輸出的結(jié)果是多少呢?
答案是0,至少我們看的屏幕上顯示的是0.000000,實際值可不是0啊,只是我們顯示的精度只能有15位小數(shù),而實際的數(shù)據(jù)可能很小很小,0.0000....000幾百個0后會有幾個有效數(shù)據(jù)。
我們分析一下:
首先C語言把a傳進printf,因為a是整型,所以不會自動轉(zhuǎn)成double型數(shù)據(jù),直接將0x3f0000000傳進printf,而%f尋的是64位內(nèi)存,也就是把0x000000003f000000這個內(nèi)存中的數(shù)據(jù)當(dāng)成浮點型輸出來,那浮點型的數(shù)據(jù)是多少呢,又是怎么存儲的呢?
64位浮點數(shù)的存放方式:
63位 62~52位 51~0位 1個符號位 11個階數(shù) 52個尾數(shù) 從0x000000003f000000來看: 00000000 1)符號位是0,表示正 2)階數(shù)是0,用移碼表示:0-1023 = -1023,
用指數(shù)表示:1.#*2^-1023,‘#’是代表尾數(shù)。
3)尾數(shù)就是,0x000003f000000 4)浮點二進制表示
1.000000000000000000000011 1111 000000000000000000000000*2(-1023),2-1023次方可想而知有多小!
這就是為什么我們的int型數(shù)據(jù)用%f輸出是0.000000的原因! 如果把0.5的雙精度數(shù)對應(yīng)的十六進制數(shù)賦給long long類型變量,則可以輸出正確的小數(shù):
總結(jié):
通過以上實驗,我驗證了原假設(shè)基本正確:
格式輸出函數(shù)printf()根據(jù)類型字符%以及變量名,就可以根據(jù)數(shù)據(jù)首地址+讀取長度的方式輸出數(shù)據(jù)。
但是,還要注意其中的一些細節(jié):
(1)用%d輸出float類型數(shù)據(jù)時,在參數(shù)傳遞的時候C語言統(tǒng)一將 float 類型數(shù)值傳換為 double 類型再傳入 printf 函數(shù)。而%d只截取低32位數(shù)據(jù),所以得到的數(shù)字不是相應(yīng)浮點數(shù)的二進制碼。
(2)int類型%f格式輸出,%f尋的是64位內(nèi)存,所以輸出的數(shù)據(jù)可能很小(比如2^-1023),那么結(jié)果是0.
到此,相信大家對“c語言整型變量怎么用浮點型輸出”有了更深的了解,不妨來實際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進入相關(guān)頻道進行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!