?? 10_數據類型轉換.txt
字號:
數據類型轉換
各類整數之間的轉換
C語言中的數分8位、16位和32位三種。屬于8 位數的有:帶符號
字符char,無符號字符unsigned char 。屬于16位數的有:帶符號整
數int,無符號整數unsigned int(或簡寫為unsigned), 近指針。屬
于32位數的有:帶符號長整數long,無符號長整數 unsigned long,
遠指針。
IBM PC是16位機,基本運算是16位的運算,所以,當8位數和16
位數進行比較或其它運算時,都是首先把8 位數轉換成16位數。為了
便于按2的補碼法則進行運算,有符號8位數在轉換為16位時是在左邊
添加8個符號位,無符號8位數則是在左邊添加8個0。當由16位轉換成
8位時,無論什么情況一律只是簡單地裁取低8位,拋掉高8 位。沒有
char或usigned char常數。字符常數,像"C",是轉換為int以后存儲
的。當字符轉換為其它 16 位數(如近指針)時,是首先把字符轉換為
int,然后再進行轉換。
16位數與32位數之間的轉換也遵守同樣的規則。
注意,Turbo C中的輸入/輸出函數對其參數中的int和unsigned
int不加區分。例如,在printf函數中如果格式說明是%d 則對這兩種
類型的參數一律按2 的補碼(即按有符號數)進行解釋,然后以十進制
形式輸出。如果格式說明是%u、%o、%x、%X,則對這兩種類型的參數
一律按二進制 (即按無符號數) 進行解釋,然后以相應形式輸出。在
scanf函數中,僅當輸入的字符串中含有負號時,才按2的補碼對輸入
數進行解釋。
還應注意,對于常數,如果不加L,則Turbo C一般按int型處理。
例如,語句printf("%081x",-1L),則會輸出ffffffff。如果省略1,
則輸出常數的低字,即ffff。如果省略L,則仍會去找1個雙字,這個
雙字的就是int常數-1,高字內容是不確定的,輸出效果將是在4個亂
七八糟的字符之后再跟ffff。
在Turbo C的頭文件value.h中,相應于3 個帶符號數的最大值,
定義了3個符號常數: #define MAXSHORT 0X7FFF
#define MAXINT 0X7FFF
#define MAXLONG 0X7FFFFFFFL 在Turbo C Tools中,包括3對宏,分別把8位拆成高4位和低4位,
把16位拆成高8位和低8位,把32位拆成高16位和低16位。 uthinyb(char value) utlonyb(char value)
uthibyte(int value) utlobyte(int value)
uthiword(long value) utloword(long valueu)
在Turbo C Tools中,也包括相反的3 個宏,它們把兩個4位組成
一個8位,把兩個8位組成一個16位,把兩個16位組成一個32位。 utnybbyt(HiNyb,LoNyb)
utwdlong(HiWord,Loword)
utbyword(HiByte,LoByte)實數與整數之間的轉換
Turbo C中提供了兩種實數:float和 double。float 由32 位組
成,由高到低依次是:1個尾數符號位,8個偏碼表示的指數位(偏值=
127),23個尾數位。double由64位組成,由高到低依次是:1 個尾數
符號位,11個偏碼表示的指數位(偏值=1023), 52個尾數位。通過下
列公式,可以由尾數和指數計算出所代表的實數值: X=±1.尾數*2(指數-偏值) 下列幾種情況下,此公式不成立: 指數=000...0且尾數 =00...0,則X=X=±0
指數=000...0且尾數!=00...0,則X=±0.尾數*2(1-偏值)
指數=11....1且尾數 =00...0,則X=±∞
指數=11....1且尾數!=00...0,則X是一個無效數
在Turbo C的頭文件value.h中,相應于實數所能達到的最大最小
值,定義了如下幾個符號常數: #define MAXFLOAT 3.37E+38
#define MINFLOAT 8.43E-37
#define MAXDOUBLE 1.797693E+308
#define MINDOUBLE 2.225074E-308 實常數是按double格式存放的,如果想按float 格式存放,則必
須加后綴F,如:1.23E+4F。
當把實數強制轉換為整數時,采取的是“向零靠攏的算法",如: float值: 65432.6 -65432.6
轉換為long; 65432 -65432
轉換為unsigned: 65432 104
如果不希望“向零靠攏”,而希望“四舍五入”,則必須由程序員自
己處理。一種辦法是先加上(符號位/2),然后再進行類型轉換。應該
注意的是:如果被轉換的實數值超過了目標類型的表達范圍,則會產
生錯誤。例如上面的float值-65432,6轉換為unsigned int值時,由
于超過了目標范圍,所產生的104就是錯誤的。在65432。6轉換為
unsigned int的65432以后,可以用printf的%u格式輸出,如果用 %d
格式輸出,則會得到錯誤的結果。指針說明
指針是包含另一變量的地址變量。它的一般說明形式,如
int *fd, 其fd是一個指向整型變量的指針。比較復雜的指針說明,
如*(*pfpi)(),可按如下幾個原則來理解:以標識符為中心,一對方
括號一般表示數組,一對圓括號一般表示函數或強調某一優先順序,
方括號對和圓括號對為同一優先級,方括號和圓括號比*號優先級高。
以下幾例解釋了這些原則的應用。
int *fip(),因圓括號優先級高,幫fip 先與圓括號結合,說明
fip是一個函數,這個函數返回一個指向整數的指針。 int (*pfi)(),因兩對圓括號為同一優先級,故從左到右,pfi
是一個指針,這個指針指向一個函數,這個函數返回一個整數。 int *par[],因方括號比*號優先級高,故par是一個數組,這個
數組的每一個元素是指向整數的指針。
int (*ptr)[],因方括號與圓括號為同一優先級,故ptr 是一個
指針,這個指針指向一個數,這個數的每一個元素是一個整數。 int *(*pfpi)(),pfpi是一指針,這個指針指向一個函數,這個
函數返回一個提向整數的指針。指針與地址
指針在使用之前必須初始化,給指針賦地址的方法一般有如下幾
種:
第一種很容易理解,通過取地址操作符取變量(包括結構變量)的
地址,如:
char_c="a", *ptr_char;
ptr_char=&c; 第二種是數組,因為不帶方括號的數組名等效于數組中第一個元
素的地址,即數組名也可看作是一個指針,所以有兩種辦法。如: char myname[31], *ptr;
ptr=myname;
或 ptr=&myname[0];
第三種是動態分配的一塊內存,這時往往帶有類型強制轉換,但
應注意當內存不夠時,可能返回一個空(NULL)指針。如: struect complex {double real, image; };
struct complex *ptr;
ptr={ struct complex *)malloc(sizeof(struct complex)); 第四種是函數,與數組名一樣,函數名也可以當作一個地址,于
是可以把函數名賦給一個指向函數的指針。函數名后面跟一個帶圓括
號的參數表意味著計算函數的值,但僅有一個函數名則意味著指向函
數的指針。如:
double (*fx)();
double quad_poly(double);
fx=quad_poly;指針運算
常見的指針運算有:指針加或減一個數,指針增量,指針減量,
指針比較等等。假設P是某數組第1個元素的指針,則P+N 就是這個數
組中第n 個元素的地址,每個元素占多少存儲單元則由指針所指數組
的類型來確定。但有兩點要注意:
第一,上面這段話是C語言對指針運算的普遍規律,但具體到種C
編譯則有所不同,尤其是在80X86類型的機器上。Turbo C和
Microsoft C 6.0以前的版本把指針分為near、far、huge,
Microsoft C 6.0又增加了based。在這幾種指針中,只有huge嚴格遵
守上面的指針運算規則,詳見下一節。
第二,當指針應用于數組尤其是多維數組時,有時容易弄錯,下
表說明了數組法與指針法的區別。
│ 1維 │ 2維 │ 3維
━━━━━━━━━━┿━━━━━┿━━━━━━━┿━━━━━━━━━━
數組說明 │int x[] │int y[][] │int z[][][]
指針說明 │int *xptr │int *yptr[] │int *zptr[][]
數組法表示某元素地址│&x[i] │&y[i][j] │&z[i][j][k]
指針法表示某元素地址│ptr+i │*(yptr+i)+j │*(*(zptr+i)+j)+k
數組法存取某元素 │x[i] │y[i][j] │z[i][j][k]
指針法存取某元素 │*(ptr+i) │*(*(yptr+i)+j)│*(*(*(zptr+i)+j)+k)指針分類
在C 語言教科書上,指針就是指針,不存在分類的問題。我們經
常說“指向整數的指針”,“指向結構的指針”,“指向函數的指針”
等等,只是說指針指向不同的目標,而不是說指針本身有什么區別。
但是,在以80X86為基礎的微機上實現C 語言時,由于80X86的物理地
址空間不是線性連續的而是分段的,為了提高效率,就必須對指針加
以分類。各類指針的運算法則也不一樣。Turbo C 2.0及以前的版本,
Microsoft C 6.0以前的版本,指針都是分類三類,近(near),遠
(far),巨(huge)。Microsoft C 6.0版本中,出現了一種新的指外類
型,這就是基(based)指針。 基指針綜合實現了近和遠指針的優點,
它像近指針那么小那么快,又像遠指針那樣可以尋址缺省數據段以外
的目標。基指針這個名字就反映了這類指針上的實現方法:它是以程
序員指定的某一地址為段基址。如果在C 源程序中使用了基指針,編
譯程序一般先把所指定的段基址裝在ES寄存器內。在缺省的數據段內,
程序員一般不會使用基指針。但若同時要使用多個數據段,基指則有
其明顯的優點。
一、近(near)指針
近指針是用于不超過64K 字節的單個數據段或碼段。對于數據指
針,在微、小和中編譯模式下產生的數據指針是近指針,因為此時只
有一個不超過64K 字節的數據段。對于碼(即函數指針)指針,在微、
小和緊湊編譯模式下產生的碼指針是近指針,因為此時只一個不超過
64K字節的碼段。本章將只討論數據指針。
近指針是16位指針,它只含有地址的偏移量部分。為了形成32位
的完整地址,編譯程序一般是反近指針與程序的數據段的段地址組合
起來。因為在大部分情況下程序的數據段的段地址是裝在DS寄存器內,
因此一般沒有必要裝載這個寄存器。此外,當用匯編語言和C 語言混
合編程時,匯編語言總是假設DS含有數據目標的地址。
雖然近指針占用空間最小,執行速度最快,但它有一個嚴格的限
制,即只能64K字節以內的數據,且只能存取程序的數據段內的數據。
如果在小模式下編譯一個程序,而這個程序企圖增量一個近指針使之
超過第65536個字節,則這個近的指針就會復位到0。下面就是這樣一
個例子:
char _near *p=(char _near *)0xffff;
p++;
由于近指針的這個嚴重限制,所有在比較大或比較復雜的程序中,
都無法使用。二、遠(far)指針
遠指針不是讓編譯程序把程序數據段地址作為指針的段地址部分,
而是把指針的段地址與指針的偏移量直接存放在指針內。因此,遠指
針是由4 個字節構成。它可以指向內存中的任一目標,可以用于任一
編譯模式,盡管僅在緊湊、大和巨模式下遠指針才是缺省的數據指針。
因為遠指針的段地址在指針內,熟悉80X86 匯編語言的人都知道,這
意味著每次使用遠指針時都需要重新裝載段寄存器,這顯然會降低速
度。
應該注意:盡管遠指針可以尋址內存中的任一單元,但它所尋址
的目標也不能超過64K 字節。這是因為,遠指針在增量或減量之類的
算術運算時,也只是偏移量部分參與運算,而段地址保持不變。因此,
當遠指針增量或減量到超過64K字節段邊界時就出錯。例如: char far *fp=(char far *)0xb800ffff;
fp++; 在指針加1以后,fp將指向B800:0000,而不是所希望的
C800:0000。
此外,在進行指針比較時,far指針還會引起另外一些問題。far
指針是由偏移量和段地址這樣一對16位數來表示的,對于某一實際內
存地址,far指針不是唯一的,例如,far指針1234:0005、1230:0045、
1200:0345、1000:2345、0900:9345等都是代表實際地址12345,這樣
會引起許多麻煩。
第一,為了便于與“空”(NULL)指針(0000: 0000)進行比較,當
關系操作符“==”和“!=”用于對far 指針進行比較時,比較的是全
部32位。否則,如果只比較16位偏移量,那么任何偏移量為0 的指針
都將是“空”(NULL)指針,這顯然不符合一般使用要求。但在進行這
32位比較時,不是按20位實際地址來比較,而是把段地址和偏移量當
作一個32位無符號長整數來比較。對于上面這個例子,假設這些指針
分別叫作a、b、c、d、e,盡管這5個far 指針指向的都是同一內存單
元,但下列表達式運算的結果卻都為“假”,從而得出錯誤的結論:
if(a==b)....
if(b==c)....
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -