有關運算符優先級的規則稍微有點復雜。在大多數情況下,這些規則確實是你所需要的,然而,有人也指出其中的一些規則本來是可以設計得更好的。
讓我們快速地回顧一些有關內容:“運算符優先級”是這樣一些規則的集合——這些規則規定了“運算符”(例如+,-,等等)的優先性,即哪一種運算符先參加運算。在數學中,表達式“2×3+4×5”和“(2×3)+(4×5)”是等價的,因為乘法運算在加法運算之前進行,也就是說乘法的優先級比加法高。
在c中,有16級以上的運算符優先級。盡管這么多的規則有時使c程序不易閱讀,但也使C程序寫起來容易多了。雖然這不是唯一的一種折衷方法,但這就是C所采用的方法。表16.1總結了運算符的優先級。
?????????? 表16.1 運算符優先級總結(從高到低)
----------------------------------------------------------------------------------
? 優先級??????????? 運算符
----------------------------------------------------------------------------------
? 1??????????????? x[y](下標)
?????????????????? x(y)(函數調用)
?????????????????? x.y(訪問成員)
????????????????? x->y(訪問成員指針)
?????????????????? x++(后綴自增)
?????????????????? x--(后綴自減)--
??? 2???????????? ++x(自增)
?????????????????? --x(自減)
????????????????? &x(取地址)
?????????????????? *x(指針引用)
????????????????? +x(同x,和數學中相同)
????????????????? -x(數學求負)
????????????????? !x(邏輯非)
????????????????? ~x(按位求反)
???????????????? ? sizeof x和sizeof(x_t)(字節數大小)
?? 3???????????????(x_t)y(強制類型轉換)
?? 4??????????????x*y(乘法)
?????????????????? x/y(除法)
?????????????????? x%y(求余)
?? 5????????????? x+y(加法)
?????????????????? x-y(減法)
?? 6????????????? x<<y(按位左移)
?????????????????? x>>y(按位右移)
??? 7????????????? x<y,x>y,x<=y,x>=y(關系比較)
??? 8???????????? x==y,x!=y(相等比較)
??? 9????????????? x&y(按位與)
??? 10???????????? x^y(按位異或)? .
??? 11???????????? x | y(按位或)
??? 12??????????? x&&y(邏輯與)
??? 13???????????? x||y(邏輯或)
??? 14???????????? x?y:z(條件)
????????????????? x=y,x*=y,x/=y,x+=y,x-=y,<<=,>>=,&=,^=,|=(賦值,右結合性)
??? 16??? x,y(逗號)
--------------------------------------------------------------------------------------
優先級最高的是后綴表達式,即運算符跟在一個表達式后面;其次是前綴或單目表達式,即運算符位于一個表達式的前面;再次是強制類型轉換表達式。
注意:關于運算符優先級,最重要的是知道*p++和*(p++)是等價的。也就是說,在*p++中,++運算符作用在指針上,而不是作用在指針所指向的對象上。象“*p++=*q++;這樣的代碼在C中是隨處可見的,其中的優先級和“(*(p++))=(*(q++))”中的是相同的。這個表達式的含義是“q+1,但仍用q原來的值找到q所指向的對象;p加1,但仍用p原來的值;把q所指向的對象賦給p所指向的對象”,整個表達式的值就是原來q所指向的對象。在C中你會經常看到這樣的代碼,并且你會有許多機會去寫這樣的代碼。對于其它運算符,如果你記不住其優先級,可以查閱有關資料,但是,一個好的c程序員應該連想都不用想就能明白*p++的含義。
最初的C編譯程序是為這樣一種計算機編寫的——它的某些指令對象*p++和*p++=*q++這樣的代碼的處理效率高得令人難以置信,因此,很多C代碼就寫成這種形式了。進一步地,因為象這樣的C代碼實在太多了,所以新機型的設計者會保證提供能非常高效地處理這些C代碼的指令。
再下一級的優先級是乘法、除法和求余(也叫取模),再往后是加法和減法。與數學中的表達式相同,“2*3+4*5”和“(2*3)+(4*5)”是等價的。
再下一級是移位運算。
再往后兩級分別是關系比較(例如x<y)和相等比較(x==y和x!=y)。
再往后三級分別是按位與、按位異或和按位或。
注意:關于運算符優先級,再次重要(即在知道*p++和x=y=z的含義之后)的是要知道x&y==z和(x&y)==z是不一樣的。因為按位操作的運算符的優先級低于比較運算符,所以x&y==z和x&(y==z)是等價的。這兩個表達式的含義都是“先看y和z是否相等(相等為1,不等為0),然后讓比較結果和x進行按位與運算”,這與“先讓x和y進行按位與運算,再比較其結果是否等于z”相差甚遠。有人可能會爭辯,按位與運算符的優先級應該高于比較運算符,但為時已晚,因為相應的標準是早在二十年前被定義的。如果你想把按位與的結果與別的東西進行比較,你就需要使用括號。
再往后兩級是邏輯運算符,例如x&&y和x||y。注意,邏輯與(AND)運算符的優先級高于邏輯或(OR)運算符,這與人們講話的方式是一致的。例如,請看下面的代碼:
??? if(have_ticket&&have_reservation
??? ||have_money && standby_ok){
??? goto_airport();
??? }
這段代碼的含義可以這樣來描述:“如果你有機票并且預定了航班,或者你有錢并且可以買到備用票,那么你就可以出發去機場了。”如果你用括號改變優先級,你就會得到一種截然不同的條件:
??? /* not a recommended algorithm!*/
??? if(have_ticket
??? &&(have_reservation || have_money)
??? &&standby_ok){
??? goto airport ();
??? }
這段代碼的含義可以這樣來描述:“如果你有機票,并且你預定好了航班或者有錢,并且可以買到備用票,那么你就可以出發去機場了。”
再下一級是條件表達式,例如x?y:z。這是一個if-then-else結構的表達式,而不是一條語句。條件表達式有時可以使程序簡潔,有時也會造成語意的模糊。條件表達式具有右結合性,也就是說
??? a?b:c?d:e
等價于
? a?b:(c?d:e)
這一點與else—if結構很相似。
再下一級是賦值運算。所有的賦值運算符都具有相同的優先級。與C的其它雙目運算符不同,賦值運算具有“右結合性”,即它是從右向左進行的,而不是從左向右進行的。x+y+z等價于(x+y)+z,x*y+z等價于(x*y)+z,而x=y=z等價于x=(y=z)。
注意:關于運算符優先級,次重要(即在知道*p++的含義之后)的是要知道x=y=z的含義。因為賦值運算具有右結合性,所以這個表達式等價于x=(y=z),其含義是“將z的值賦給y,然后再將該值賦給x”。象a=b=c=d=O;
這樣的代碼是很常見的,按從右向左的順序,它把。賦給d,再賦給c,再賦給b,最后賦給a。
c中優先級最低的是逗號運算符。它連接兩個表達式,先計算第一個表達式的值,扔掉后,再計算第二個表達式的值。只有當第一個表達式具有副作用時,例如賦值或函數調用,使用逗號運算符才有意義。逗號和賦值運算符經常在for循環語句中搭配使用:
??? for(i=0,count=O;i<MAX;++i){
?????? if(interestmg(a[i])){
?????? ++count:
?????? }
??? }
假設你的程序運行在short類型為兩字節長的計算機上,并且把值258(十進制)存放到地址s3000H處的一個short類型中。因為short類型的長度為兩字節,所以該值的一個字節存放在3000H處,另一個字節存放在3001H處。258(十進制)即0102H,所以該值的一個字節的內容為1,另一個字節的內容為2。那么,究竟內容為1和2的字節分別是哪一個呢?
其答案因機器的不同而不同。在高位優先的計算機上,高位字節就是低地址字節(“高位字節”指的是其值變化后使整個字的值變化最大的那個字節,例如,在值0102H中,01H就是高位字節,而02H是低位字節)。在高位優先的計算機上,字節中的內容如下所示:
??? 地址??? 2FFEH??? 2FFFH??? 3000H??? 3001H??? 3002H??? 3003H
?? 值????? 01H??? ? 02H
這種圖示方式很直觀——地址就象是尺子上的刻度值,低地址在左,高地址在右。在低位優先的計算機上,字節中的內容如下所示:
??? 地址??? 3003H??? 3002H??? 3001H??? 3000H??? 2FFFH??? 2FFEH
??? 值????? 01H????? 02H
這種圖示方式同樣很直觀——低位字節存放在低地址中。
不幸的是,有些計算機采用高位優先的存儲方式,而另一些計算機卻采用低位優先的存儲方式。例如,IBM兼容機和Macintosh機對高位字節和低位字節的處理方法就不同。
為什么這種區別會產生影響呢?試想一下,如果用fwrite()直接把一個short類型的值按兩字節存到文件或網絡上,不考慮格式和是否可讀,而只是存為緊湊的二進制形式,會引起什么后果呢?如果在高位優先的計算機上存入這個值,而在低位優先的計算機上讀出該值(或者反過來),那么存入的是0102H(258),讀出的就是0201H(513)。
解決這個問題的辦法是選擇一種存儲(和讀取)方式,并且自始至終使用這種方式,而不是按存入內存的方式來存儲short或int類型的值。例如,有些標準指定了“網絡字節順序(network byte order)”,它是一種高位優先順序(即高位字節存放在低地址中)。例如,如果s是一個short類型值而a是一個由兩個char類型組成的數組,那么下面這段代碼
??? a[0]=(s>>4)& Oxf;
??? a[1]=s&0xf;
將把s的值按網絡字節順序存入a的兩個字節中。不管程序是運行在高位優先或低位優先的計算機上,s的值都會存成這種形式。
你可能會注意到,筆者一直沒有提到哪種計算機是高位優先或低位優先的計算機。這樣做是有目的的——如果可移植性是重要的,你就應該按這兩種類型的計算機都能接受的方式編寫程序;如果效率是重要的,通常你仍然要按這兩種類型的計算機都能接受的方式編寫程序。
例如,在高位優先的計算機上可以用一種更好的方法去實現上例中的那段代碼,即使你使用了上例中的代碼,一個好的編譯程序仍然會利用那種更好的實現來產生機器代碼。
注意:“big-endian"和"little-endian"這兩個名稱來源于Jonathan Swift所寫的《格列佛游記>>(Gulliver's Travels)一書。在格列佛第三次出海時,他遇到了這樣一群人,他們對煮熟了的雞蛋的吃法爭論不休:有的要先吃大頭,有的要先吃小頭。
“網絡字節順序”只適用于int,short和long類型。char類型的值按定義只有一字節長,因此字節順序與它無關。對于float和double類型的值,沒有一種標準的存儲方式。
???????????????? 表14.9運算符優先級
----------------------------------------------------------------
? 運算符?????????????????????????????? ? 結合性???
----------------------------------------------------------------
? () [] ->?????????????????????????????? 自左至右
? ! ~ ++ -- -(類型轉換) * &????????????? 自右至左
? sizeof? * / %????????????????????????? 自左至右
? + -??????????????????????????????????? 自左至右
? <<? >>???????????????????????????????? 自左至右
? << =? >>=????????????????????????????? 自左至右
? ==? !=?????????????????????????????????自左至右
? &????????????????????????????????????? 自左至右
? ^????????????????????????????????????? 自左至右
? |????????????????????????????????????? 自左至右
? &&???????????????????????????????????? 自左至右
? ||???????????????????????????????????? 自左至右
? ?:???????????????????????????????????? 自右至左
? =? +=? -=????????????????????????????? 自右至左
? ,????????????????????????????????????? 自左至右
------------------------------------------------------------------
注意,運算符“!=”的優先級高于“=”(實際上,幾乎所有的運算符的優先級都高于“=”)。下面兩行語句說明了運算符優先級的差異是怎樣給程序員帶來麻煩的:
? ? while(ch=getch()!=27)printf(”Got a character\n”);
? ? while((ch=geteh())!=27)printf("Got a character\n");??? ’
顯然,上述語句的目的是從鍵盤上接收一個字符,并與十進制值27(Escape鍵)進行比較。不幸的是,在第一條語句中,getch()與Escape鍵進行了比較,其比較結果(TRUE或FALSE)而不是從鍵盤上輸入的字符被賦給了ch。這是因為運算符“!=”的優先級高于“=”。
在第二條語句中,表達式"ch=geteh()”的外邊加上了括號。因為括號的優先級最高,所以來自鍵盤的字符先被賦給ch,然后再與Escape鍵進行比較,并把比較結果(TRUE或FALSE)返回給while語句,這才是程序真正的目的(當while的條件為TRUE時,打印相應的句子)。需要進一步提出的是,與27比較的并不是ch,而是表達式"ch—getch()”的結果。在這個例子中,這一點可能不會造成什么影響,但括號確實可以改變代碼的組織方式和運行方式。當一個語句中有多個用括號括起來的表達式時,代碼的執行順序是從最里層的括號到最外層,同層的括號則從左到右執行。
注意,每個運算符在單獨情況下的結合性(自左至右,或自右至左)都是不會改變的,但優先級的順序可以改變。
??? x=15/7;
如果x是一個整數,x的值將為2。然而,如果用取模運算符代替除法運算符"/",得到的結果就不同了:
??? X=15%7;
這個表達式的結果為15除以7的余數,等于1。這就是說,15除以7得2余1。
取模運算符通常用來判斷一個數是否被另一個數整除。例如,如果你要打印字母表中序號為3的倍數的字母,你可以使用下面這段代碼:
??? int x;
??? for(x=1; x<=26; x++)
??? if((x%3)==0)
? ? printf("%c"; x+64);??
上例將輸出字符串"cfilorux",即字母表中序號為3的倍數的所有字母。
例如,請看一個使用后綴自增運算符的例子:
??? int x, y;
??? x=1;???
??? y=(x++* 5);
上例使用了后綴自增運算符,在求得表達式的值之后,x的值才增加1,因此,y的值為1乘以5,等于5。在求得表達式的值之后,x自增為2。
現在看一個使用前綴自增運算符的例子:
??? int x, y;
??? x=1;
??? y=(++x*5);
這個例子和前一個相同,只不過使用了前綴自增運算符,而不是后綴自增運算符,因此,x的值先增加1,變為2,然后才求得表達式的值。這樣,y的值為2乘以5,等于10。
此外,為了進一步優化代碼,目前流行的大多數C編譯程序常常會改變表達式的求值順序。因此,你應該用括號明確地指定運算符的優先級。例如,請看下述表達式:
??? a=b+c/d/function—call() * 5
上述表達式的求值順序非常模糊,你很可能得不到所要的結果,因此,你最好明確地指定運算符的優先級:
??? a=b+(((c/d)/function—call())* 5)
這樣,就能確保表達式被正確求值,而且編譯程序不會為了優化代碼而重新安排運算符的優先級了。
??? int X,y;
??? x = 1;??? /*? 1 iS an rvalue,? x is an lvalue? */
??? y=(x+1);??? /*? (x+1)is an rvalue;y is an lvalue? */
在前面已經介紹過,一條賦值語句必須有一個左值和一個右值,因此,下述語句無法通過編譯,因為它缺少一個右值:
??? int x;
??? x=void_function_call();? /* the{unction void—function—call()
?????????????????????????????? returns nothing */
如果上例中的函數返回一個整數,那么它可以被看作一個右值,因為它的返回值可以存儲到左值x中。
請參見:
1、什么是左值(lvaule)????
2、數組可以是左值嗎??
??? int x[5],y[5];
??? x=y;
不過,你可以通過for循環來遍歷數組中的每個元素,并分別對它們賦值,例如:
??? int i;
??? int x[5];
??? int y[5];
??? ......
??? for(i=0; i<5,i++)
??? x[i]=y[i];
??? ......
此外,你可能想一次拷貝整個數組,這可以通過象memcpy()這樣的函數來實現,例如:
??? memcpy(x,y,sizeof(y));
與數組不同,結構(structure)可以作為左值。你可以把一個結構變量賦給另一個同類型的結構變量,例如:???
??? typedef struct t_name
??? {
??? charlast_name[25];???
? ? char first_name[15];
??? char middle-init [2];
? ? } NAME
? ? ...
? ? NAME my_name, your_name;
? ? ...
? ? your_name = my_name;
? ? ...
在上例中,結構變量my_name的全部內容被拷貝到結構變量your_name中,其作用和下述語句是相同的:
??? memcpy(your_name,my_name,sizeof(your_name);
請參見:???
1、什么是左值(lvaule)?
2、什么是右值(rvaule)?
int x;
int *p_int;
x=1;
p_int=5;
變量x是一個整數,它對應于內存中的一個可存儲位置,因此,在語句“x=1”中,x就是一個左值。 注意,在第二個賦值語句“*p_int=5"中,通過“*”修飾符訪問p_int所指向的內存區域;因此,p_int是一個左值。
相反,下面的幾個例子就不是左值:
#define CONST_VAL 10
int x
/* example 1 * /
l=x;
/ * example 2 * /
CONST_VAL = 5;
在上述兩條語句中,語句的左側都是一個常量,其值不能改變,因為常量不表示內存中可
存儲的位置。因此,這兩條賦值語句中沒有左值,編譯程序會指出它們是錯誤的。
請參見:???
1、 數組(array)可以是左值嗎???? .
2、 什么是右值(rvaule)?
讓我們快速地回顧一些有關內容:“運算符優先級”是這樣一些規則的集合——這些規則規定了“運算符”(例如+,-,等等)的優先性,即哪一種運算符先參加運算。在數學中,表達式“2×3+4×5”和“(2×3)+(4×5)”是等價的,因為乘法運算在加法運算之前進行,也就是說乘法的優先級比加法高。
在c中,有16級以上的運算符優先級。盡管這么多的規則有時使c程序不易閱讀,但也使C程序寫起來容易多了。雖然這不是唯一的一種折衷方法,但這就是C所采用的方法。表16.1總結了運算符的優先級。
?????????? 表16.1 運算符優先級總結(從高到低)
----------------------------------------------------------------------------------
? 優先級??????????? 運算符
----------------------------------------------------------------------------------
? 1??????????????? x[y](下標)
?????????????????? x(y)(函數調用)
?????????????????? x.y(訪問成員)
????????????????? x->y(訪問成員指針)
?????????????????? x++(后綴自增)
?????????????????? x--(后綴自減)--
??? 2???????????? ++x(自增)
?????????????????? --x(自減)
????????????????? &x(取地址)
?????????????????? *x(指針引用)
????????????????? +x(同x,和數學中相同)
????????????????? -x(數學求負)
????????????????? !x(邏輯非)
????????????????? ~x(按位求反)
???????????????? ? sizeof x和sizeof(x_t)(字節數大小)
?? 3???????????????(x_t)y(強制類型轉換)
?? 4??????????????x*y(乘法)
?????????????????? x/y(除法)
?????????????????? x%y(求余)
?? 5????????????? x+y(加法)
?????????????????? x-y(減法)
?? 6????????????? x<<y(按位左移)
?????????????????? x>>y(按位右移)
??? 7????????????? x<y,x>y,x<=y,x>=y(關系比較)
??? 8???????????? x==y,x!=y(相等比較)
??? 9????????????? x&y(按位與)
??? 10???????????? x^y(按位異或)? .
??? 11???????????? x | y(按位或)
??? 12??????????? x&&y(邏輯與)
??? 13???????????? x||y(邏輯或)
??? 14???????????? x?y:z(條件)
????????????????? x=y,x*=y,x/=y,x+=y,x-=y,<<=,>>=,&=,^=,|=(賦值,右結合性)
??? 16??? x,y(逗號)
--------------------------------------------------------------------------------------
優先級最高的是后綴表達式,即運算符跟在一個表達式后面;其次是前綴或單目表達式,即運算符位于一個表達式的前面;再次是強制類型轉換表達式。
注意:關于運算符優先級,最重要的是知道*p++和*(p++)是等價的。也就是說,在*p++中,++運算符作用在指針上,而不是作用在指針所指向的對象上。象“*p++=*q++;這樣的代碼在C中是隨處可見的,其中的優先級和“(*(p++))=(*(q++))”中的是相同的。這個表達式的含義是“q+1,但仍用q原來的值找到q所指向的對象;p加1,但仍用p原來的值;把q所指向的對象賦給p所指向的對象”,整個表達式的值就是原來q所指向的對象。在C中你會經常看到這樣的代碼,并且你會有許多機會去寫這樣的代碼。對于其它運算符,如果你記不住其優先級,可以查閱有關資料,但是,一個好的c程序員應該連想都不用想就能明白*p++的含義。
最初的C編譯程序是為這樣一種計算機編寫的——它的某些指令對象*p++和*p++=*q++這樣的代碼的處理效率高得令人難以置信,因此,很多C代碼就寫成這種形式了。進一步地,因為象這樣的C代碼實在太多了,所以新機型的設計者會保證提供能非常高效地處理這些C代碼的指令。
再下一級的優先級是乘法、除法和求余(也叫取模),再往后是加法和減法。與數學中的表達式相同,“2*3+4*5”和“(2*3)+(4*5)”是等價的。
再下一級是移位運算。
再往后兩級分別是關系比較(例如x<y)和相等比較(x==y和x!=y)。
再往后三級分別是按位與、按位異或和按位或。
注意:關于運算符優先級,再次重要(即在知道*p++和x=y=z的含義之后)的是要知道x&y==z和(x&y)==z是不一樣的。因為按位操作的運算符的優先級低于比較運算符,所以x&y==z和x&(y==z)是等價的。這兩個表達式的含義都是“先看y和z是否相等(相等為1,不等為0),然后讓比較結果和x進行按位與運算”,這與“先讓x和y進行按位與運算,再比較其結果是否等于z”相差甚遠。有人可能會爭辯,按位與運算符的優先級應該高于比較運算符,但為時已晚,因為相應的標準是早在二十年前被定義的。如果你想把按位與的結果與別的東西進行比較,你就需要使用括號。
再往后兩級是邏輯運算符,例如x&&y和x||y。注意,邏輯與(AND)運算符的優先級高于邏輯或(OR)運算符,這與人們講話的方式是一致的。例如,請看下面的代碼:
??? if(have_ticket&&have_reservation
??? ||have_money && standby_ok){
??? goto_airport();
??? }
這段代碼的含義可以這樣來描述:“如果你有機票并且預定了航班,或者你有錢并且可以買到備用票,那么你就可以出發去機場了。”如果你用括號改變優先級,你就會得到一種截然不同的條件:
??? /* not a recommended algorithm!*/
??? if(have_ticket
??? &&(have_reservation || have_money)
??? &&standby_ok){
??? goto airport ();
??? }
這段代碼的含義可以這樣來描述:“如果你有機票,并且你預定好了航班或者有錢,并且可以買到備用票,那么你就可以出發去機場了。”
再下一級是條件表達式,例如x?y:z。這是一個if-then-else結構的表達式,而不是一條語句。條件表達式有時可以使程序簡潔,有時也會造成語意的模糊。條件表達式具有右結合性,也就是說
??? a?b:c?d:e
等價于
? a?b:(c?d:e)
這一點與else—if結構很相似。
再下一級是賦值運算。所有的賦值運算符都具有相同的優先級。與C的其它雙目運算符不同,賦值運算具有“右結合性”,即它是從右向左進行的,而不是從左向右進行的。x+y+z等價于(x+y)+z,x*y+z等價于(x*y)+z,而x=y=z等價于x=(y=z)。
注意:關于運算符優先級,次重要(即在知道*p++的含義之后)的是要知道x=y=z的含義。因為賦值運算具有右結合性,所以這個表達式等價于x=(y=z),其含義是“將z的值賦給y,然后再將該值賦給x”。象a=b=c=d=O;
這樣的代碼是很常見的,按從右向左的順序,它把。賦給d,再賦給c,再賦給b,最后賦給a。
c中優先級最低的是逗號運算符。它連接兩個表達式,先計算第一個表達式的值,扔掉后,再計算第二個表達式的值。只有當第一個表達式具有副作用時,例如賦值或函數調用,使用逗號運算符才有意義。逗號和賦值運算符經常在for循環語句中搭配使用:
??? for(i=0,count=O;i<MAX;++i){
?????? if(interestmg(a[i])){
?????? ++count:
?????? }
??? }
高位優先(big—endian)與低位優先(little-endian)的計算機有什么區別?
高位優先與低位優先的區別僅僅在于一個字的哪一端是高位字節。換句話說,兩者的區別在于你是喜歡從左向右數,還是喜歡從右向左數。但是,哪種方式都不見得比另一種方式更好。一個可移植的C程序必須能同時適用于這兩種類型的計算機。假設你的程序運行在short類型為兩字節長的計算機上,并且把值258(十進制)存放到地址s3000H處的一個short類型中。因為short類型的長度為兩字節,所以該值的一個字節存放在3000H處,另一個字節存放在3001H處。258(十進制)即0102H,所以該值的一個字節的內容為1,另一個字節的內容為2。那么,究竟內容為1和2的字節分別是哪一個呢?
其答案因機器的不同而不同。在高位優先的計算機上,高位字節就是低地址字節(“高位字節”指的是其值變化后使整個字的值變化最大的那個字節,例如,在值0102H中,01H就是高位字節,而02H是低位字節)。在高位優先的計算機上,字節中的內容如下所示:
??? 地址??? 2FFEH??? 2FFFH??? 3000H??? 3001H??? 3002H??? 3003H
?? 值????? 01H??? ? 02H
這種圖示方式很直觀——地址就象是尺子上的刻度值,低地址在左,高地址在右。在低位優先的計算機上,字節中的內容如下所示:
??? 地址??? 3003H??? 3002H??? 3001H??? 3000H??? 2FFFH??? 2FFEH
??? 值????? 01H????? 02H
這種圖示方式同樣很直觀——低位字節存放在低地址中。
不幸的是,有些計算機采用高位優先的存儲方式,而另一些計算機卻采用低位優先的存儲方式。例如,IBM兼容機和Macintosh機對高位字節和低位字節的處理方法就不同。
為什么這種區別會產生影響呢?試想一下,如果用fwrite()直接把一個short類型的值按兩字節存到文件或網絡上,不考慮格式和是否可讀,而只是存為緊湊的二進制形式,會引起什么后果呢?如果在高位優先的計算機上存入這個值,而在低位優先的計算機上讀出該值(或者反過來),那么存入的是0102H(258),讀出的就是0201H(513)。
解決這個問題的辦法是選擇一種存儲(和讀取)方式,并且自始至終使用這種方式,而不是按存入內存的方式來存儲short或int類型的值。例如,有些標準指定了“網絡字節順序(network byte order)”,它是一種高位優先順序(即高位字節存放在低地址中)。例如,如果s是一個short類型值而a是一個由兩個char類型組成的數組,那么下面這段代碼
??? a[0]=(s>>4)& Oxf;
??? a[1]=s&0xf;
將把s的值按網絡字節順序存入a的兩個字節中。不管程序是運行在高位優先或低位優先的計算機上,s的值都會存成這種形式。
你可能會注意到,筆者一直沒有提到哪種計算機是高位優先或低位優先的計算機。這樣做是有目的的——如果可移植性是重要的,你就應該按這兩種類型的計算機都能接受的方式編寫程序;如果效率是重要的,通常你仍然要按這兩種類型的計算機都能接受的方式編寫程序。
例如,在高位優先的計算機上可以用一種更好的方法去實現上例中的那段代碼,即使你使用了上例中的代碼,一個好的編譯程序仍然會利用那種更好的實現來產生機器代碼。
注意:“big-endian"和"little-endian"這兩個名稱來源于Jonathan Swift所寫的《格列佛游記>>(Gulliver's Travels)一書。在格列佛第三次出海時,他遇到了這樣一群人,他們對煮熟了的雞蛋的吃法爭論不休:有的要先吃大頭,有的要先吃小頭。
“網絡字節順序”只適用于int,short和long類型。char類型的值按定義只有一字節長,因此字節順序與它無關。對于float和double類型的值,沒有一種標準的存儲方式。
C語言運算符的優先級總能起作用嗎(從左至右,從右至左)?
如果你是指“一個運算符的結合性會從自右至左變為自左至右嗎?反過來會嗎?”,那么答案是否定的。如果你是指“一個優先級較低的運算符會先于一個優先級較高的運算符被執行嗎?”,那么答案是肯定的。表14.9按優先級從高到低的順序列出了所有的運算符及其結合性:???????????????? 表14.9運算符優先級
----------------------------------------------------------------
? 運算符?????????????????????????????? ? 結合性???
----------------------------------------------------------------
? () [] ->?????????????????????????????? 自左至右
? ! ~ ++ -- -(類型轉換) * &????????????? 自右至左
? sizeof? * / %????????????????????????? 自左至右
? + -??????????????????????????????????? 自左至右
? <<? >>???????????????????????????????? 自左至右
? << =? >>=????????????????????????????? 自左至右
? ==? !=?????????????????????????????????自左至右
? &????????????????????????????????????? 自左至右
? ^????????????????????????????????????? 自左至右
? |????????????????????????????????????? 自左至右
? &&???????????????????????????????????? 自左至右
? ||???????????????????????????????????? 自左至右
? ?:???????????????????????????????????? 自右至左
? =? +=? -=????????????????????????????? 自右至左
? ,????????????????????????????????????? 自左至右
------------------------------------------------------------------
注意,運算符“!=”的優先級高于“=”(實際上,幾乎所有的運算符的優先級都高于“=”)。下面兩行語句說明了運算符優先級的差異是怎樣給程序員帶來麻煩的:
? ? while(ch=getch()!=27)printf(”Got a character\n”);
? ? while((ch=geteh())!=27)printf("Got a character\n");??? ’
顯然,上述語句的目的是從鍵盤上接收一個字符,并與十進制值27(Escape鍵)進行比較。不幸的是,在第一條語句中,getch()與Escape鍵進行了比較,其比較結果(TRUE或FALSE)而不是從鍵盤上輸入的字符被賦給了ch。這是因為運算符“!=”的優先級高于“=”。
在第二條語句中,表達式"ch=geteh()”的外邊加上了括號。因為括號的優先級最高,所以來自鍵盤的字符先被賦給ch,然后再與Escape鍵進行比較,并把比較結果(TRUE或FALSE)返回給while語句,這才是程序真正的目的(當while的條件為TRUE時,打印相應的句子)。需要進一步提出的是,與27比較的并不是ch,而是表達式"ch—getch()”的結果。在這個例子中,這一點可能不會造成什么影響,但括號確實可以改變代碼的組織方式和運行方式。當一個語句中有多個用括號括起來的表達式時,代碼的執行順序是從最里層的括號到最外層,同層的括號則從左到右執行。
注意,每個運算符在單獨情況下的結合性(自左至右,或自右至左)都是不會改變的,但優先級的順序可以改變。
C語言取模運算符(modulus operator)“%”的作用是什么
取模運算符“%”的作用是求兩個數相除的余數。例如,請看下面這段代碼:??? x=15/7;
如果x是一個整數,x的值將為2。然而,如果用取模運算符代替除法運算符"/",得到的結果就不同了:
??? X=15%7;
這個表達式的結果為15除以7的余數,等于1。這就是說,15除以7得2余1。
取模運算符通常用來判斷一個數是否被另一個數整除。例如,如果你要打印字母表中序號為3的倍數的字母,你可以使用下面這段代碼:
??? int x;
??? for(x=1; x<=26; x++)
??? if((x%3)==0)
? ? printf("%c"; x+64);??
上例將輸出字符串"cfilorux",即字母表中序號為3的倍數的所有字母。
++var和var++有什么區別(C語言自增自減詳解)
“++”運算符被稱為自增運算符。如果“++”運算符出現在變量的前面(++var),那么在表達式使用變量之前,變量的值將增加1。如果“++”運算符出現在變量之后(var++),那么先對表達式求值,然后變量的值才增加1。對自減運算符(--)來說,情況完全相同。如果運算符出現在變量的前面,則相應的運算被稱為前綴運算;反之,則稱為后綴運算。例如,請看一個使用后綴自增運算符的例子:
??? int x, y;
??? x=1;???
??? y=(x++* 5);
上例使用了后綴自增運算符,在求得表達式的值之后,x的值才增加1,因此,y的值為1乘以5,等于5。在求得表達式的值之后,x自增為2。
現在看一個使用前綴自增運算符的例子:
??? int x, y;
??? x=1;
??? y=(++x*5);
這個例子和前一個相同,只不過使用了前綴自增運算符,而不是后綴自增運算符,因此,x的值先增加1,變為2,然后才求得表達式的值。這樣,y的值為2乘以5,等于10。
C語言運算符的優先級總能保證是“自左至右”或“自右至左”的順序嗎
對這個問題的簡單回答是:這兩種順序都無法保證。C語言并不總是自左至右或自右至左求值,一般說來,它首先求函數值,其次求復雜表達式的值,最后求簡單表達式的值。此外,為了進一步優化代碼,目前流行的大多數C編譯程序常常會改變表達式的求值順序。因此,你應該用括號明確地指定運算符的優先級。例如,請看下述表達式:
??? a=b+c/d/function—call() * 5
上述表達式的求值順序非常模糊,你很可能得不到所要的結果,因此,你最好明確地指定運算符的優先級:
??? a=b+(((c/d)/function—call())* 5)
這樣,就能確保表達式被正確求值,而且編譯程序不會為了優化代碼而重新安排運算符的優先級了。
C語言右值(rvaule)是什么
在1.9中,左值被定義為可被賦值的表達式,你也可以認為左值是出現在賦值語句左邊的表達式。這樣,右值就可以被定義為能賦值的表達式,它出現在賦值語句的右邊。與左值不同,右值可以是常量或表達式:例如:?????? int X,y;
??? x = 1;??? /*? 1 iS an rvalue,? x is an lvalue? */
??? y=(x+1);??? /*? (x+1)is an rvalue;y is an lvalue? */
在前面已經介紹過,一條賦值語句必須有一個左值和一個右值,因此,下述語句無法通過編譯,因為它缺少一個右值:
??? int x;
??? x=void_function_call();? /* the{unction void—function—call()
?????????????????????????????? returns nothing */
如果上例中的函數返回一個整數,那么它可以被看作一個右值,因為它的返回值可以存儲到左值x中。
請參見:
1、什么是左值(lvaule)????
2、數組可以是左值嗎??
C語言數組(array)可以是左值嗎
在1.9中,左值被定義為可被賦值的表達式。那么,數組是可被賦值的表達式嗎?不是,因為數組是由若干獨立的數組元素組成的,這些元素不能作為一個整體被賦值。下述語句是非法的:??? int x[5],y[5];
??? x=y;
不過,你可以通過for循環來遍歷數組中的每個元素,并分別對它們賦值,例如:
??? int i;
??? int x[5];
??? int y[5];
??? ......
??? for(i=0; i<5,i++)
??? x[i]=y[i];
??? ......
此外,你可能想一次拷貝整個數組,這可以通過象memcpy()這樣的函數來實現,例如:
??? memcpy(x,y,sizeof(y));
與數組不同,結構(structure)可以作為左值。你可以把一個結構變量賦給另一個同類型的結構變量,例如:???
??? typedef struct t_name
??? {
??? charlast_name[25];???
? ? char first_name[15];
??? char middle-init [2];
? ? } NAME
? ? ...
? ? NAME my_name, your_name;
? ? ...
? ? your_name = my_name;
? ? ...
在上例中,結構變量my_name的全部內容被拷貝到結構變量your_name中,其作用和下述語句是相同的:
??? memcpy(your_name,my_name,sizeof(your_name);
請參見:???
1、什么是左值(lvaule)?
2、什么是右值(rvaule)?
什么是C語言左值(lvaule)
左值是指可以被賦值的表達式。左值位于賦值語句的左側,與其相對的右值(rvaule,見 1.11)則位于賦值語句的右側。每條賦值語句都必須有一個左值和一個右值。左值必須是內存中一個可存儲的變量,而不能是一個常量。下面給出了一些左值的例子:int x;
int *p_int;
x=1;
p_int=5;
變量x是一個整數,它對應于內存中的一個可存儲位置,因此,在語句“x=1”中,x就是一個左值。 注意,在第二個賦值語句“*p_int=5"中,通過“*”修飾符訪問p_int所指向的內存區域;因此,p_int是一個左值。
相反,下面的幾個例子就不是左值:
#define CONST_VAL 10
int x
/* example 1 * /
l=x;
/ * example 2 * /
CONST_VAL = 5;
在上述兩條語句中,語句的左側都是一個常量,其值不能改變,因為常量不表示內存中可
存儲的位置。因此,這兩條賦值語句中沒有左值,編譯程序會指出它們是錯誤的。
請參見:???
1、 數組(array)可以是左值嗎???? .
2、 什么是右值(rvaule)?