- 零基礎學單片機C語言程序設計
- 趙建領 薛園園等編著
- 4629字
- 2018-12-31 21:41:36
3.8 C51的運算符
運算符是表示特定的算術或邏輯操作的符號,也稱為操作符。例如,“*”號表示了一個乘法運算符;“&&”號表示了一個邏輯與的運算符。在C51語言中,需要進行運算的各個量(常量或變量)通過運算符連接起來便構成一個表達式。本節首先介紹運算符,表達式將在下一節進行介紹。
C51語言中有算術運算符、關系運算符、邏輯運算符、位運算符這幾類運算符,還有些用于輔助完成復雜功能的特殊運算符,如“,”運算符、“?”運算符、地址操作運算符、聯合操作運算符、“sizeof”運算符、類型轉換運算符等。使用特殊運算符可以起到簡化程序的作用。下面對各種運算符的含義和用法分別進行介紹。
3.8.1 算術運算符
算術運算符是用來進行算術運算的操作符。C51語言中的算術運算符繼承了其他高級計算機語言的特點,用法也基本一致。C51語言中的算術運算符有如下所示的幾種。
?“-”運算符:進行減法或取負的運算。
?“+”運算符:進行加法運算。
?“*”運算符:進行乘法運算。
?“/”運算符:進行除法運算。
?“%”運算符:進行模運算。
?“--”運算符:進行自減(減1)運算。
?“++”運算符:進行自增(增1)運算。
1. 普通算術運算符
普通算術運算符是指加“+”、減“-”、乘“*”、除“/”以及取模“%”運算,示例如下。
3+23=26; 17-5=12; 4*5=20; 6/3=2; 43%2=1;
這些普通算術運算符的運算操作和其他高級語言的運算相類似,需要注意的是以下幾個運算符操作的不同之處。
?除法運算符“/”的運算結果是取除法結果的整數部分。例如,“10/4=2”,結果取商2.5的整數部分,值為2。
?取模運算符“%”的運算結果是取除法結果的余數部分。該運算符不能用于浮點型數據的運算操作。例如,“9%4=1”,結果取商9-4*2=1的余數部分,值為1。
?減法運算符“-”除進行減法運算外,還可以用來進行取負運算操作。例如,“-sz”是取變量sz的負操作。
普通算術運算符的使用程序示例如下。
#include <stdio.h> //頭文件 void main() //主函數 { int x,y,z; //定義整型變量 x=30;y=12; //賦初值 z=x+y; //加法運算 printf("x+y=%d\n",z); //輸出結果 z=x-y; //減法運算 printf("x-y=%d\n",z); //輸出結果 z=x*y; //乘法運算 printf("x*y=%d\n",z); //輸出結果 z=x/y; //除法運算 printf("x/y=%d\n",z); //輸出結果 z=x%y; //取模運算 printf("x%%y=%d\n",z); //輸出結果 z=-x; //取負運算 printf("-x =%d\n",z); //輸出結果 }
該程序可在Keil μ Vision3集成開發環境中運行,其執行結果如下。
x+y=43 x-y=18 x*y=360 x/y=2 x%y=6 -x=-30
2. 自增和自減運算
自增運算符“++”表示操作數加1,即x++等同于x=x+1;自減運算符“--”表示操作數減1,即x--等同于x=x-1。這兩個很常用的運算符是沿用了C語言的特點。
自增和自減運算符既可放在操作數之前,也可放在其后。例如x=x+1,可寫成++x,也可以寫成x++,但在表達式中這兩種用法是有區別的。自增或自減運算符放在操作數之前時,C51語言在引用操作數之前就先執行加1或減1操作;運算符在操作數之后時,C51語言就先引用操作數的值,而后再進行加1或減1操作,示例如下。
x=++m; //m先增加1,然后賦值給x x=m++; //m先賦值給x,然后再增加1
說明
在大多數的編譯環境中,采用自增和自減操作符所生成的程序代碼比等價的賦值語句所生成的代碼執行起來的要快得多,因此推薦采用自增和自減運算符。
自增和自減運算符的程序示例如下。
#include <stdio.h> //頭文件 void main() //主函數 { int x,y,z1,z2; //定義整型變量 x=10;y=21; //賦初值 z1=(x++)+(x++); printf("x=%d,z1=%d\n",x,z1); //輸出結果 z2=(++y)+(++y); printf("y=%d,z2=%d",y,z2); //輸出結果 }
這段程序可在Keil μ Vision3集成開發環境中運行,執行結果如下。
x=12,z1=21 y=23,z2=45
在該程序中,計算z1時,對于第一個括號先取x的值,然后x增1,此后x變為11;對于第二個括號同樣先取x的值11,然后再增1,此后x變為12,因此最后x為12,z1為10+11=21。計算z2時,對于第一個括號y值,首先增1,然后取y的值,此后y變為22;對于第二個括號,同樣y值先增1,然后取y的值,此后y變為23,因此最后y為23,z2為22+23=45。
說明
在C51程序中,一般按照從左向右的運算順序,關于運算符的運算優先級別的內容將在后面章節中作詳細的介紹。
3.8.2 邏輯運算符
邏輯運算符是進行邏輯運算的操作符。C51語言中的邏輯運算符如下所示。
?“!”運算符:進行邏輯非運算。
?“||”運算符:進行邏輯或運算。
?“&&”運算符:進行邏輯與運算。
邏輯運算符的操作對象可以是整型數據、浮點型數據以及字符型數據。如果邏輯運算符的操作結果是真,則運算結果為1;如果為假,則運算結果為0。邏輯運算符的邏輯真值如表3.6所示。
表3.6 邏輯運算符的真值表

邏輯運算符運算的程序示例如下。
#include <stdio.h> //頭文件 void main() //主函數 { int a,b,c,d,e; //定義整型變量,并存放邏輯運算結果 a=!0; //邏輯非運算 b=15&&22; //邏輯與運算 c=35&&0; //邏輯與運算 d=17.3||0; //邏輯或運算 e=17.3||2.6; //邏輯或運算 printf("a=%d,b=%d,c=%d,d=%d,e=%d\n",a,b,c,d,e); //輸出結果 }
該程序可在Keil μ Vision3集成開發環境中運行,執行結果如下。
a=1,b=1,c=0,d=1,e=1
說明
在C51語言中規定,非零的操作數都被視為是真,為零的操作數都被視為是假。
3.8.3 關系運算符
關系運算符主要用于比較操作數的大小關系,和一般的C語言相類似。常用的關系運算符如下所示。
?“>”運算符:判斷是否大于。
?“>=”運算符:判斷是否大于等于。
?“<”運算符:判斷是否小于。
?“<=”運算符:判斷是否小于等于。
?“==”運算符:判斷是否等于。
?“!=”運算符:判斷是否不等于。
關系運算符和邏輯運算符在程序運算中常常在一起聯合使用。如果關系運算符的操作結果是真,則運算結果為1;如果為假,則運算結果為0。關系運算符運算的程序示例如下。
#include <stdio.h> //頭文件 void main() //主函數 { int a,b,c,d; //定義整數變量,存儲結果 a=-2.3>=0; //比較運算 b=71==32; //比較運算 c=7!=0; //比較運算 d=-12<=0; //比較運算 printf("a=%d,b=%d,c=%d,d=%d\n",a,b,c,d); //輸出結果 }
該程序可在Keil μ Vision3集成開發環境中運行,執行結果如下。
a=0,b=0,c=1,d=1
說明
邏輯運算符和關系運算符的返回值都是True(真)和Flase(假)。在C51語言中規定,非0的值為True,0值為Flase。使用關系或邏輯運算符的表達式時,若表達式為True(真)則返回值為1;若表達式為Flase(假)則返回值為0。
3.8.4 位運算符
位運算符是對字節或字中的二進制位(bit)進行逐位邏輯處理或移位的運算符。C51語言中的位運算符如下所示。
?“&”運算符:進行邏輯與(AND)運算。
?“|”運算符:進行邏輯或(OR)運算。
?“^”運算符:進行邏輯異或(XOR)運算。
?“~”運算符:進行按位取補(NOT)運算。
?“>>”運算符:進行右移運算。
?“<<”運算符:進行左移運算。
位運算符的操作對象整型和字符型數據的字節或字,位操作不能用于float、double、long double、void或其他聚合類型。支持全部的位運算符,表明C51可以進行匯編語言所具有的位運算,因此C51語言既具有高級語言的特點,也具有低級語言的功能。
位運算中的AND、OR和NOT(1的補碼)的真值表與邏輯運算等價,唯一不同的是,位操作是首先將操作數分解為二進制,然后逐位進行運算的。下面分別介紹各個位運算符的用法。
1. 按位與運算符
按位與運算符是將兩個操作數按二進制展開,然后將對應位進行邏輯與運算。按位與運算符“&”是二目運算符,即要求有兩個源操作數。如果操作數對應的二進制位均為1,則邏輯與的結果為1;否則,則邏輯與的結果為0。
例如,將a=56和b=37進行按位與運算。將56展開為二進制是00111000,將37展開為二進制是00100101。按位與運算的結果為a&b=32(00100000)。
注意
這里需要區分位運算符“&”和關系邏輯運算符“&&”,關系邏輯運算符“&&”的結果不是0就是1。而位運算符“&”的結果通過逐位處理,結果可為0和1之外的其他值。例如a=56和b=37,a&&b=1,而a&b=32。
2. 按位或運算符
按位或運算符是將兩個操作數按二進制展開,然后將對應位進行邏輯或運算。按位或運算符“|”也是二目運算符,即要求有兩個源操作數。如果對應的二進制位均為0,則邏輯或的結果為0;否則,則邏輯或的結果為1。
例如,將a=56和b=37進行按位或運算。將56展開為二進制是00111000,將37展開為二進制是00100101。按位或運算的結果a|b=61(00111101)。
3. 按位非運算符
按位非運算符是將操作數按二進制展開,然后將每一位進行取反操作,即將0變為1,將1變為0。按位非運算符“~”是一目運算符,只需要一個源操作數。
例如,將a=56進行按位取反運算。56的二進制表示為00111000,結果~a=199(11000111)。
說明
按位非運算得到的結果和源操作數是逐位相反的,因此這兩者進行按位或運算的結果每個二進制位均為1,即對應十進制255。這樣可以通過255減去源操作數得到按位非運算的結果。例如56按位非的結果為255-56=199,和上面的結果相同。
4. 按位異或運算符
按位異或運算符是將兩個操作數按二進制展開,然后將對應位進行邏輯異或運算。按位異或運算符“^”也是二目運算符,需要兩個源操作數。如果對應的二進制位均為0或均為1,則邏輯或的結果為0;否則,只要對應位不相同,則邏輯異或的結果為1。異或運算的邏輯真值如表3.7所示。
表3.7 異或的真值表

例如,將a=56和b=37進行按位異或運算。將56展開為二進制是00111000,將37展開為二進制是00100101。按位異或運算的結果a|b=29(00011101)。
5. 移位運算符
移位運算符是將源操作數按二進制展開,然后將對應位按要求進行移動。C51中移位運算符有兩種,分別將操作數的各位按要求向左或向右移動,其使用格式如下。
?左移語句形式是:變量名(或操作數)<<左移位數
?右移語句形式是:變量名(或操作數)>>右移位數
在C51程序中使用移位運算符需要注意如下幾點。
?C51語言中的移位不是循環移位,當某位從一端移出時,另一端移入0。從一端移出的位并不送回到另一端去,移去的位永遠丟失了,同時在另一端補0。
?右移運算,對無符號位數左端補0,稱為“邏輯右移”;如果為負數,即符號位為1,則左端補1,這種補1保持負號的方法稱為“算術右移”。
?左移運算有時可用來做乘法,右移運算有時可用來做除法。例如,x=7,將其進行移位操作,結果如表3.8所示。
表3.8 移位操作進行乘和除

從表中可以看出,每左移一位,結果乘2,每右移一位相當于被2除。注意表中x<<2后,原x的信息已經丟失了,因為一位“1”已經從一端移出。因此,需要注意這種用法的范圍。
6. 程序示例
前面具體介紹了各個位運算符,這里舉例來演示位運算符在程序中的具體應用。位運算符的程序示例如下。
#include <stdio.h> //頭文件 void main() //主函數 { int a,b,c; //定義變量 a=23; //變量賦值 b=217; c=a&b; //按位與運算 printf("a&b=%d\n",c); //輸出結果 c=a|b; //按位或運算 printf("a|b=%d\n",c); //輸出結果 c=a^b; //按位異或運算 printf("a^b=%d\n",c); //輸出結果 c=~a; //按位取反運算 printf("~a=%d\n",c); //輸出結果 c=a<<3; //左移兩位 printf("a<<3=%d\n",c); //輸出結果 c=b>>2; //右移三位 printf("b>>2=%d\n",c); //輸出結果 }
該程序可在Keil μ Vision3集成開發環境中運行,執行結果如下。
a&b=17 a|b=223 a^b=206 ~a=-24 a<<3=184 b>>2=54
讀者可以參考前面介紹的內容,分析一下程序運行的結果和預期的是否一致。讀者同樣可以嘗試不同數進行運算,從而熟練掌握位算符的操作。
3.8.5 “,”運算符
“,”運算符是把幾個表達式串在一起,并用括號括起來,按照順序從左向右計算的運算符。“,”運算符左側表達式的值不作為返回值,只有最右側表達式的值作為整個表達式的返回值。程序示例如下。
#include <stdio.h> //頭文件 void main() //主函數 { int a,b,c; //定義變量 a=37; //賦值 b=179; c=(a++,++b,b+a); //執行","運算符,為c賦值 printf("c=%d\n",c); //輸出結果 }
該程序可在Keil μ Vision3集成開發環境中運行,執行結果如下。
c=218
本例中首先執行a++,a自增1,然后執行++b,b自增1,最后執行b+a,并將結果賦給變量z。
3.8.6 “?”運算符
“?”運算符首先計算表達式1的值,然后根據表達式1的真假接著計算其余表達式的值,并輸出結果。“?”運算符是三目操作符,其一般形式如下。
EXP1?EXE2:EXP3;
其中,EXP1、EXP2和EXP3是表達式。“?”運算符作用是在計算表達式EXP1的值后,如果其值為真,則計算表達式EXP2的值,并將其結果作為整個表達式的結果;如果表達式EXP1的值為假,則計算表達式EXP3的值,并將結果作為整個表達式的結果。“?”運算符的程序示例如下。
#include <stdio.h> //頭文件 void main() //主函數 { int x,y; //定義變量 x=17; //賦值 y=x>5?10:20; //使用"?"運算符,為y賦值 printf("y=%d\n",y); //輸出結果 }
該程序可在Keil μ Vision3集成開發環境中運行,執行結果如下。
y=10
本例中,首先判斷x>5,其值為真,所以執行第一個表達式將10賦給y。若用if-else語句改寫,有下面的等價程序。
#include <stdio.h> //頭文件 void main() //主函數 { int x,y; //定義變量 x=17; //賦值 if (x>5) //使用if語句,為y賦值 { y=10; } else { y=20; } printf("y=%d\n",y); //輸出結果 }
這段程序同樣可以在Keil μ Vision3集成開發環境中運行,執行結果和前面相同。比較兩程序,從中可以看出使用“?”運算符,可以大大簡化程序。
3.8.7 “sizeof”運算符
“sizeof”運算符返回變量所占的字節或類型長度字節。“sizeof”運算符是單目操作符。在C51語言中,“sizeof”運算符類似于C語言中length函數。使用“sizeof”運算符的程序示例如下。
#include <stdio.h> //頭文件 void main() //主函數 { char ch[]="hello everyone!"; //定義字符串數組 int i,j; //定義整型變量 i=sizeof(ch); //獲取字符串數組的長度 j=sizeof(float); //獲取float類型數據的長度 printf("i=%d\nj=%d\n",i,j); //輸出結果 }
該程序可在Keil μ Vision3集成開發環境中運行,執行結果如下。
i=16 j=4
這段程序首先定義并初始化字符串數組,然后獲取該字符串所占的長度,包括最后的空字符。程序中還使用sizeof運算符來獲取float數據類型的長度。
3.8.8 地址操作運算符
地址操作運算符用來對變量的地址進行操作。在C51語言中,地址操作運算符主要有兩種:“*”和“&”。其中,“*”運算符是單目操作符,其返回位于某個地址內存儲的變量值;“&”運算符也是一個單目操作符,其返回操作數的地址。“*”運算符和“&”運算符是相對應的。程序示例如下。
#include <stdio.h> //頭文件 main() //主函數 { char ch1,ch2; //定義字符型變量 char *p; //定義指針型變量 ch1='A'; //為字符型變量ch1賦值 p=&ch1; //將變量ch1的地址賦給p ch2=*p; //地址p所指的單元值賦給ch2 printf("ch2=%c\n",ch2); //輸出ch2 }
該程序可在Keil μ Vision3集成開發環境中運行,執行結果如下。
ch2=A
本例中,字符型變量ch1賦值為字符'A',然后將其的地址賦給指針型變量p,最后將地址p中的數值賦給變量ch2,這樣變量ch2便有了和變量ch1同樣的內容,因此ch2輸出為字符'A'。
3.8.9 聯合操作運算符
聯合操作運算符主要用來簡化一些特殊的賦值語句,這類賦值語句的一般形式如下。
<變量1>=<變量1><操作符><表達式>
利用聯合操作運算符可以簡化為如下形式。
<變量1><操作符>=<表達式>
聯合操作運算符適合于所有的雙目操作符。C51語言中常用的聯合操作運算符示例如下。
?a+=b,相當于a=a+b。
?a*=b,相當于a=a*b。
?a&=b,相當于a=a&b。
?a|=b,相當于a=a|b。
?a/=x+y-z,相當于a=a/(x+y-z)。
3.8.10 類型轉換運算符
類型轉換運算符用于強制使某一表達式的結果變為特定數據類型。類型轉換運算符的一般形式如下所示。
(類型)表達式
其中,“(類型)”中的類型必須是C51中的一種數據類型。類型轉換運算符的使用示例如下。
(float)x/2 //將x/2的結果轉換為浮點型
在C51語言中,“/”運算的結果將取其整數,為確保表達式x/2具有準確的結果,所以使用類型轉換運算符強制運算結果轉換為浮點型數據。類型轉換運算符的程序示例如下。
#include <stdio.h> //頭文件 void main() //主函數 { int i; //定義整型循環變量 for(i=0;i<7;i++) //for循環語句 { printf("%d/3=%f\n",i,(float)i/3); //循環輸出i/3的數值 } }
該程序可在Keil μ Vision3集成開發環境中運行,執行結果如下。
0/3=0.000000 1/3=0.333333 2/3=0.666667 3/3=1.000000 4/3=1.333333 5/3=1.666667 6/3=2.000000
本例中,將從0到6的整數進行除法運算,由于“/”運算符只取商的整數部分,為了準確的結果,使用類型轉換運算符,強制將運算結果轉換為浮點型,這樣將得到準確的計算結果。
3.8.11 運算符優先級和結合性
在C51語言中,當一個表達式中有多個運算符參與運算時,要按照運算符的優先級別進行運算。在復雜的表達式中,除了要判斷運算優先級,還要考慮結合性(或者關聯性)。
1. 算術運算符的優先級
算術運算符的優先級由高到低依次為自增自減(++、--)和取負(-)、乘法除法(*、/)和取模(%)、加和減(+、-)。
這里需要強調的是,在C51程序編譯時對同級運算符一般按從左到右的順序進行計算,由于括號的優先級最高,所以括號會改變計算順序。
2. 關系運算符和邏輯運算符的優先級
關系運算符和邏輯運算符的相對優先級最高的是!,其次是>、<、>=和<=,然后是= =和!=,后面是&&,最后是||。關系運算符和邏輯運算符的優先級符合如下幾點(表3.9)。
?關系和邏輯運算符的優先級比算術運算符低,像表達式10>1+12和表達式10>(1+12)計算的結果是一樣的。
?在關系或邏輯表達式中可以使用括號來修改原計算的優先級順序。
3. 運算符的結合性
在一個復雜的表達式中,常常有許多運算符和變量,除了要判斷優先級,還要考慮結合性。C51語言中具有“左結合性”和“右結合性”的概念。左結合性即變量(或常量)與左邊的運算符結合。右結合性即變量(或常量)與右邊的運算符結合。示例如下。
-6+23;
表3.9 運算符的優先級和結合性

其中,運算符“-”和“+”相對于運算的操作數來說是“左”結合的,所以實際參與計算的是“-6”和“+23”,即相當于(-6)+(23)運算的結果為17。
運算符優先級和結合性的順序如表3.9所示,表中優先級從上往下逐漸降低,同一行優先級從左往右逐漸降低。
從表中可以看出,凡是單目運算符都是“右結合”的,凡是雙目運算符和三目運算符都是“左結合”的。這個規律,可以方便運算符的結合性的記憶。
為了程序移植、防止歧義和閱讀的方便,在實際程序設計中,如果代碼行中的運算符比較多,應當多用括號確定表達式的操作順序,避免使用默認的優先級。示例如下。
(num<<4)|a //用括號把需要先運算的括起來 (a|c)&&(b&c) //用括號把需要先運算的括起來
4. 程序示例
關于運算符優先級和結合性的程序示例如下。
#include<stdio.h> //頭文件 void main() //主函數 { int a=5; //定義整型變量 a%=9-2; //聯合操作 printf("a=%d\n",a); //輸出結果 a+=a/=a*=2; //復雜的運算 printf("a=%d\n",a); //輸出結果 }
該程序可在Keil μ Vision3集成開發環境中運行,執行結果如下。
a=5 a=0
本例中,由于“%”和“=”運算符的優先級別低于“--”運算符,a%=9-2即a%=7,等價于a=a%7即a=5%7=5。表達式a+=a/=a*=2,開始時a=5,表達式賦值是從左至右進行的,表達a*=2使得a=10,此表達式的值也為10。接著a/=a*=2。相當于a/=10,也就是a=a/10,因此,a=0。最后,表達式a+=a/=a*=2相當于a+=0,也就是a=a+0=0,所以最終a的值為0。
- Advanced Splunk
- Spring 5企業級開發實戰
- 零基礎搭建量化投資系統:以Python為工具
- 區塊鏈:以太坊DApp開發實戰
- 用Python實現深度學習框架
- Visual Basic程序設計實驗指導(第二版)
- Learning Apache Cassandra
- Deep Learning with R Cookbook
- Flink技術內幕:架構設計與實現原理
- 人人都能開發RPA機器人:UiPath從入門到實戰
- Java EE輕量級解決方案:S2SH
- 計算機應用基礎(Windows 7+Office 2010)
- Getting Started with the Lazarus IDE
- Raspberry Pi開發實戰
- Programming MapReduce with Scalding