標(biāo)題:
關(guān)于C語(yǔ)言算術(shù)表達(dá)式中“尋常算術(shù)轉(zhuǎn)換”的問(wèn)題
[打印本頁(yè)]
作者:
niuniu
時(shí)間:
2015-3-25 00:09
標(biāo)題:
關(guān)于C語(yǔ)言算術(shù)表達(dá)式中“尋常算術(shù)轉(zhuǎn)換”的問(wèn)題
在c語(yǔ)言算術(shù)表達(dá)式的編譯過(guò)程中,有一個(gè)“尋常算術(shù)轉(zhuǎn)換”的問(wèn)題,大多數(shù)情況下,教材中并沒(méi)有做具體的有意義的說(shuō)明,在實(shí)際應(yīng)用中,如果不注意這個(gè)問(wèn)題,可能會(huì)產(chǎn)生嚴(yán)重的后果!一下以實(shí)例做一個(gè)說(shuō)明,希望引起大家的注意。
1、C語(yǔ)言算術(shù)表達(dá)式“尋常算術(shù)轉(zhuǎn)換”
由于“歷史”(搞笑,C語(yǔ)言弄出來(lái)不過(guò)幾十年而已,好意思談歷史?)的原因,C語(yǔ)言算術(shù)表達(dá)式在編譯過(guò)程中會(huì)對(duì)參入運(yùn)算的對(duì)象做“尋常算術(shù)轉(zhuǎn)換”,按照ANSIC標(biāo)準(zhǔn)的說(shuō)法,大致的描述是這樣的:
當(dāng)執(zhí)行算術(shù)運(yùn)算時(shí),如果操作對(duì)象的類(lèi)型不同,就會(huì)發(fā)生數(shù)據(jù)類(lèi)型轉(zhuǎn)換。對(duì)于雙目運(yùn)算符而言,兩個(gè)運(yùn)算對(duì)象的類(lèi)型都轉(zhuǎn)換為精度高的那個(gè)對(duì)象的類(lèi)型。數(shù)據(jù)類(lèi)型轉(zhuǎn)換的一般規(guī)則是:浮點(diǎn)朝著精度更高、長(zhǎng)度更長(zhǎng)的方向轉(zhuǎn)換,整型(char,short,int)運(yùn)算對(duì)象如果轉(zhuǎn)換為signed int類(lèi)型不會(huì)丟失信息,就轉(zhuǎn)換為signed int類(lèi)型,否則轉(zhuǎn)換為 unsigned int類(lèi)型(詳細(xì)表述請(qǐng)參考ANSIC規(guī)范)。
嚴(yán)重建議你注意:整型運(yùn)算對(duì)象(char,short,int)的轉(zhuǎn)換規(guī)則!由于在很多系統(tǒng)中,long類(lèi)型與int類(lèi)型長(zhǎng)度不同,比如在C51中,long是32位的,而int是16位的,就有可能因“想當(dāng)然”而出現(xiàn)錯(cuò)誤!在C51編譯環(huán)境KEIL里,仿真運(yùn)行下面的例子,x==?
unsigned long x=0x00123456;
x |= (0xe8<<24);
... ...
想當(dāng)然的結(jié)論:x==0xe8123456
想當(dāng)然的"當(dāng)然":“x |= (0xe8<<24);”等價(jià)于“x =x | (0xe8<<24);”,按照“尋常算術(shù)轉(zhuǎn)換”規(guī)則,0xe8被轉(zhuǎn)換為unsigned long類(lèi)型了,所以x==0xe8123456
但實(shí)際上上述結(jié)論是錯(cuò)誤的。正確的結(jié)論是:x==0x00123456
原因分析:C編譯器首先考慮的是子算術(shù)表達(dá)式(子表達(dá)式也是表達(dá)式)“(0xe8<<24)”!雙目運(yùn)算符“<<”的兩個(gè)運(yùn)算
對(duì)象都是“沒(méi)有顯式類(lèi)型說(shuō)明的”常數(shù),于是按照“整型(char,short,int)運(yùn)算對(duì)象如果轉(zhuǎn)換為signed int類(lèi)型不會(huì)丟失信息,就轉(zhuǎn)換為signed int類(lèi)型,否則轉(zhuǎn)換為 unsigned int類(lèi)型”的規(guī)則,0xe8轉(zhuǎn)換為16位的0000 0000 11101000B,左移24位以后就變成0000 0000 0000 0000B了!
然后這個(gè)16位全0的數(shù)與32位的x做 “|”運(yùn)算, 16位全0轉(zhuǎn)換為24位全0而已!
假定希望把一個(gè)字節(jié)常量0xe8“裝配”到32位的x最高8位上,正確的做法應(yīng)該是怎么樣的呢?這樣寫(xiě)就行了:
unsigned long x=0x00123456;
x |= ((unsigned long)0xe8<<24);
或者,幾乎沒(méi)有可移植性的寫(xiě)法(顯然也就嚴(yán)重的不推薦!。。。
unsigned long x=0x00123456;
x |= (0xppppqqe8<<24); //0xpppp=0x0001~0xffff, 0xqq=0x00~0xff
說(shuō)明一下這個(gè)嚴(yán)重不推薦的寫(xiě)法以說(shuō)明問(wèn)題的本質(zhì):
0xppppqqe8的形式,最小的數(shù)是0x000100e8,最大的數(shù)是0xffffffe8,不管咋樣,在C51里都必須用32位表示才行,所以<<24不會(huì)變成0了!
以下是從前日志里一個(gè)實(shí)際的例子,用于2M字節(jié)Flash芯片的讀訪問(wèn),其中的變量address是unsigned long類(lèi)型的,它的低端三個(gè)字節(jié)用于存放字節(jié)地址,最高端字節(jié)存放“連續(xù)讀”命令0xe8。
void Flash_Read(ulong address, char* buf, uint nbytes)
{
uchar i;
AT45161_CLK = 1;
AT45161_CS(0);
address |= ((ulong)0xE8<<24); //注意ulong強(qiáng)制類(lèi)型轉(zhuǎn)換!
for(i=0; i<4; i++) //4字節(jié)命令/地址
Flash_Byte_Write((address>>(24-8*i))&0xff);
for(i=0; i<4; i++) Flash_Byte_Write(0xff); //4字節(jié)填充位
while(nbytes--) Flash_Byte_Read(buf++);
AT45161_CS(1);
}
參考文獻(xiàn):C專(zhuān)家編程(Expert C Programming) 【美】Peter Van Der Linden 人郵出版社
歡迎光臨 (http://www.torrancerestoration.com/bbs/)
Powered by Discuz! X3.1