一文带你深入浅出C语言运算符、表达式和语句
目錄
🔋前言
🔋1.關于操作符(運算符)
🔋1.1操作符分類
🔋1.2算術操作符
🔋?1.2.1 加減乘除取模
🔋1.2.2指數運算?
🔋1.3移位操作符
🔋1.3.1?左移操作符
🔋1.3.2 右移操作符
🔋1.4位操作符
🔋1.4.1位操作符分類
🔋1.4.2實例+分析+總結
🔋(1)不能創建臨時變量(第三個變量),實現兩個數的交換
🔋(2)求一個整數存儲在內存中的二進制中1的個數
🔋(3)兩個int(32位)整數m和n的二進制表達中,有多少個位(bit)不同
🔋(4)獲取一個整數二進制序列中所有的偶數位和奇數位
🔋1.5 賦值操作符
🔋1.5.1 單獨賦值?
🔋1.5.2復合賦值
🔋1.5.3 左值、右值與數據對象
🔋1.6 單目操作符
🔋1.6.1 sizeof()
🔋1.6.2 自增++或自減--
🔋1.7 關系操作符
🔋1.8 邏輯操作符
🔋1.8.1 邏輯與和按位與
🔋1.8.2 邏輯或和按位或
🔋1.8.3 典題剖析
🔋1.9 條件操作符
🔋1.10 逗號操作符
🔋1.11 [ ] 下標引用操作符
🔋1.12 ( ) 函數調用操作符
🔋1.13 訪問一個結構的成員的操作符
🔋2. 操作符的屬性
🔋2.1 求值因素
🔋2.2 例題分析?
🔋2.2.1 例1
🔋2.2.2 例2
🔋2.2.3 例3
🔋2.2.4 例4
🔋2.2.5 例5
🔋2.2.6 總結
🔋3.深入理解——副作用與序列點(了解一下)
🔋4.?自增運算符計算路徑問題
🔋表達式匹配——貪心法
🔋5. 表達式和語句的那些事兒
🔋5.1 表達式?
🔋5.2 語句
🔋5.3 關系
🔋敬請期待更好的作品吧~
🔋前言
? ? ? ? 本文內容還挺豐富的,干貨滿滿了屬于是😉,這不寫著寫著就近萬字了嘛,主要分享一波C語言運算符、表達式和語句,重點在于運算符,這篇幅不可謂不“浩蕩”。
? ? ? ? 有一點C語言基礎就可以閱讀此文,希望我的分享心得能幫助到大家,同時由于作者水平實在有限,難免存在紕漏,大伙各取所需即可。
給你點贊,加油加油!
🔋1.關于操作符(運算符)
🔋1.1操作符分類
算術操作符
移位操作符
位操作符
賦值操作符
單目操作符
關系操作符
邏輯操作符
條件操作符
逗號表達式
下標引用、函數調用和結構成員
????????操作符(運算符)對象是操作數(運算對象),不只是單單的一個數,實際上對象也可以是表達式的值。
🔋1.2算術操作符
🔋?1.2.1 加減乘除取模
1. 除了 % 操作符之外,其他的幾個操作符可以作用于整數和浮點數。
2. 對于 / 操作符如果兩個操作數都為整數,執行整數除法。而只要有浮點數執行的就是浮點數除法。
????????浮點數除法的結果是浮點數,而整數除法的結果是整數。
????????在C語言中,整數除法結果的小數部分被直接舍棄而非四舍五入,這一過程被稱為截斷。
3. % 操作符的兩個操作數必須為整數。返回的是整除之后的余數。
🔋1.2.2指數運算?
????????C沒有指數運算符,不過標準庫里提供了pow()函數用于指數運算:
函數原型:
double pow( double x, double y );
所屬頭文件:
<math.h>
功能:求取x的y次方的值,注意參數和返回值都是雙精度浮點型。
比如pow(2.0, 3.0)得到的值是8.0。
要注意的取值:
🔋1.3移位操作符
????????移位操作符的操作數只能是整數,并且移位針對補碼操作。
🔋1.3.1?左移操作符
移位規則:
左邊拋棄、右邊補0,每移動一位相當于*2。
🔋1.3.2 右移操作符
移位規則:
右移運算分兩種:
1. 邏輯移位
左邊用0填充,右邊丟棄
2. 算術移位
左邊用原該值的符號位填充,右邊丟棄
到底使用哪一種取決于編譯器。
VS使用算術右移。
警告? : 對于移位運算符,不要移動負數位(如a << -1),這個是標準未定義的。
🔋1.4位操作符
🔋1.4.1位操作符分類
&? ? ? ? ? //按位與,兩個操作數二進制位都為1才為1,否則為0
?|? ? ? ? ? //按位或,兩個操作數二進制位有1就為1,否則為0
?^? ? ? ? ?//按位異或 ,兩個操作數二進制位相同為0,相異為1
注:他們的操作數必須是整數,而且操作對象是補碼!
?示例:
#include <stdio.h> int main() {int num1 = 1;int num2 = 2;num1 & num2;num1 | num2;num1 ^ num2;return 0; }分析:
🔋1.4.2實例+分析+總結
🔋(1)不能創建臨時變量(第三個變量),實現兩個數的交換
方法一:
int a = 3; int b = 5; a = a + b; b = a - b;//得到原來的a a = a - b;//得到原來的b方法二:
關于異或的小總結:3^3 = 0, 3^0 = 3
????????也就是說一個數異或它本身結果為0,一個數異或0結果為它本身。
3^5^3 = 5,說明異或運算具有交換律,即3^5^3 = 3^3^5
所以可以這樣:
int a = 4; int b = 7; a = a ^ b; b = a ^ b;//也就是a^b^b=b^b^a=0^a=a得到原來的a a = a ^ b;//也就是a^b^a=a^a^b=0^b=b得到原來的b🔋(2)求一個整數存儲在內存中的二進制中1的個數
方法一:
????????循環進行以下操作,直到n被縮減為0。
????????用該數據模2,檢測其是否能夠被2整除,若可以則該數據對應二進制比特位的最低位一定是0,否則是1。如果是1給計數加1,如果n不等于0,繼續模2除2。(類比一下十進制)
????????十進制數除10去掉一個十進制位,二進制數除2去掉一個二進制位。
int count_one_bit(int n) {int count = 0;while(n){if(n%2==1)count++;n = n/2;}return count;}????????這樣測不了負數,需要小小改動一下,把函數形參改為無符號整型unsigned int,當傳入負數時,會自動轉換類型,看成一個很大的正數。
缺陷:進行了大量的取模以及除法運算,取模和除法運算的效率本來就比較低。
方法二:
????????一個數&1,若該數二進制第0位為1則結果為1,為0則結果為0
????????那我們可以這樣來設計,循環地把目標整數的二進制位右移,讓每一個位上的數都&1,以此來判斷每一位是不是1,結果為1則計數+1。正數負數都可以。
????????實際上二進制位移位&1相當于取出每一位來判斷。
int count = 0; int a = 65; for(int n = 0; n < sizeof(int); n++) {if((a >> n)&1)count++; }方法三:
或者這樣(更巧妙高效):
int tmp = 0; scanf("%d", &tmp); int count = 0; while(tmp) {tmp = tmp&(tmp-1);count++; }????????這樣做每次與運算都會讓目標數的二進制位減少最低位的一個1。
????????為什么呢?
????????每次-1,即n-1,最靠后的1變為0,它后面的0全變為1,這時候從這一位開始往后的所有位都與n上對應位相反,比如
?????????這時候n&(n-1)的話,dist前面的不變,后面的全變為0,得到的結果相較于n,最靠后的1就被消掉了。
?????????此種方式,數據的二進制比特位中有幾個1,循環就循環幾次,而且中間采用了位運算,處理起來比較高效。
拓展:判斷一個數是不是2的n次方
2 ^ 0 = 1 -> 1
2 ^ 1 = 2 -> 10
2 ^ 2 = 4? -> 100
……
????????我們發現2的n次方的二進制位上只有一個1,那么只要一個數的二進制位上去掉一個1后為0就說明它的二進制位上只有一個1,也就是2的n次方。
????????if(n & (n - 1) == 0)條件成立說明是2的n次方。
🔋(3)兩個int(32位)整數m和n的二進制表達中,有多少個位(bit)不同
思路一:
????????把兩個數各個二進制位都比較一下,用到了前面講過的移位&1法
int count_diff_bit(int m, int n) {int count = 0;int i = 0;for (i = 0; i < 32; i++){if (((m >> i) & 1) != ((n >> i) & 1)){count++;}}return count; }思路二:
????????直接用異或比較每一個二進制位,相異為1,再統計結果的二進制位有幾個1,用n&(n-1)法
int count_diff_bit(int m, int n) {int count = 0;//^ 異或操作符//相同為0,相異為1int ret = m ^ n;//統計一下ret中二進制位有幾個1while (ret){ret = ret & (ret - 1);count++;}return count; }🔋(4)獲取一個整數二進制序列中所有的偶數位和奇數位
????????其實是取出每一位的變式題,取出每一位就一位一位地右移,那取出奇數位或偶數位就兩位兩位地移,注意一下起點終點即可。這里從高位開始取出,如圖
int main() {int i = 0;int num = 0;scanf("%d", &num);//獲取奇數位的數字for (i = 30; i >= 0; i -= 2){printf("%d ", (num >> i) & 1);}printf("\n");//獲取偶數位的數字for (i = 31; i >= 1; i -= 2){printf("%d ", (num >> i) & 1);}return 0; }🔋1.5 賦值操作符
🔋1.5.1 單獨賦值?
????????賦值操作符就是將右邊操作對象的值賦給左邊的。
int a, x, y = 3; a = 8; x = a; a = x = y+1;//連續賦值 //不推薦,因為可讀性較差且不易調試。🔋1.5.2復合賦值
int x = 10; x = x + 10; x += 10;//復合賦值,將兩個表達式復合在一起 //其他運算符一樣的道理,這樣寫更加簡潔🔋1.5.3 左值、右值與數據對象
????????2002 = val;
????????這樣的語句在C語言中式無意義的,因為2002被稱為右值,只是字面常量,而常量不可以被賦值,常量本身就是它的值。
????????實際上,賦值運算符的左側必須引用一個存儲位置,最簡單的方法就是使用變量名(變量名標識特定內存空間),還可以使用指針(指向一個存儲位置)。總的來說,C語言使用可修改的左值標記那些可賦值的實體。
🔋1.6 單目操作符
!?????????? 邏輯反操作?????? 對一個值邏輯取反,根據C語言非零為真零為假,比如!8=0
-?????????? 負值???????????????? 對一個數取負數
+?????????? 正值??????? 實際上對一個數沒什么影響,比如a = -10; b = +a;算出來b的值還是-10
&?????????? 取地址???????? 取出一個變量的地址
sizeof????? 操作數的類型長度(以字節為單位)
~?????????? 對一個數的二進制按位取反? 比如~65,65補碼(省略前面的24位0):01000001,按位取反得到10111110,再轉成原碼得到11000010即-66
--????????? 前置、后置--
++????????? 前置、后置++
*?????????? 間接訪問操作符(解引用操作符)
(類型)?????? 強制類型轉換
🔋1.6.1 sizeof()
????????sizeof運算符以字節為單位返回運算對象的大小,返回值時無符號整數類型,運算對象可以是具體的數據對象(如變量名),也可以是類型(如int),如果是類型就必須用圓括號()括起來。
()里的操作數可以直接是一個變量,如:
int a = 10; printf("%d\n", sizeof(a));也可以是一個常數,如:
?printf("%d\n", sizeof(10));會根據該常數對應類型來計算內存大小。
還可以是類型關鍵字,如:
?printf("%d\n", sizeof(int));????????但要注意的是,這里計算的肯定不可能是int的內存,int只是規定的關鍵字,不是變量,壓根沒有存在內存中,那這里是什么意思呢?就是計算int類型的變量在內存所占用空間大小。
????????等會,不是說計算變量的內存大小嗎,那為什么常數的也可以求?實際上是根據操作數的類型來計算該類型的變量所占內存空間大小,你放入一個常數比如10到sizeof()里,它會根據10對應數據類型int來計算int類型的變量大小。
?printf("%d\n", sizeof a);//這樣寫行不行?可以,結果不影響。
printf("%d\n", sizeof int);//這樣寫行不行?不行,會出問題。
看看sizeof()和數組以及函數傳參
#include <stdio.h> void test1(int arr[]) {printf("%d\n", sizeof(arr));//(2) }void test2(char ch[]) {printf("%d\n", sizeof(ch));//(4) }int main() {int arr[10] = {0};char ch[10] = {0};printf("%d\n", sizeof(arr));//(1)printf("%d\n", sizeof(ch));//(3)test1(arr);test2(ch);return 0; }問:
(1)、(2)兩個地方分別輸出多少?
(3)、(4)兩個地方分別輸出多少?
????????一個int四個字節,數組arr帶有10個int就是40個字節,一個char一個字節,ch數組大小就是10個字節。
????????函數傳參傳數組名實際上傳的是數組首元素地址,而函數形參看起來是數組,但編譯器會把它看成對應類型的指針,這時候再使用sizeof計算的就是指針大小,32位系統下指針大小4個字節,64位系統下指針大小8個字節。
🔋1.6.2 自增++或自減--
關于++和--,要分前置和后置的情況
后置的話先使用后自增
前置的話先自增后使用
這里講的使用是作為表達式去使用
在數組中,比如arr[r++] = 10;,就是arr[r] = 10;r++;,而arr[++r] = 10;就是r++;arr[r] = 10;。
在函數調用中,比如add(r++);就是add(r);r++;,而add(++r);就是r++;add(r);。
🔋1.7 關系操作符
>
>=
<
<=
!=?? 用于測試“不相等”
==????? 用于測試“相等”
關系表達式的值為1或0,當關系滿足時,表達式值為真即1,關系不滿足時,表達式值為假即0。
🔋1.8 邏輯操作符
&&???? 邏輯與? ?兩邊操作數(運算對象)都為真表達式值才為真
||????????? 邏輯或? ?兩邊操作數(運算對象)有一個為真表達式值即為真
注意區分:
🔋1.8.1 邏輯與和按位與
邏輯與:左右操作數都為真(非0),表達式的值才為真(為1),否則為假(為0)
按位與:左右操作數的二進制位補碼一一進行“全為1則為1,否則為0”的操作
🔋1.8.2 邏輯或和按位或
邏輯或:左右操作數有一個為真(非0),表達式的值就為真(為1),否則為假(為0)
按位或:左右操作數的二進制位補碼一一進行“有一個為1則為1,否則為0”的操作
????????其實感覺比較相像,可以這么看,邏輯與和邏輯或是對于數值本身進行邏輯真假的判斷,而按位與和按位或是對于數值的二進制位進行邏輯真假的判斷,邏輯真假即非0與0。
🔋1.8.3 典題剖析
?程序輸出的結果是什么?
#include <stdio.h>int main() {int i = 0;int a=0, b=2, c =3, d=4;i = a++ && ++b && d++;printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);return 0;}其實上面故意少提了一點:
????????對于邏輯與和邏輯或,都是左結合性,邏輯與表達式要是第一個表達式值為0則整個表達式的值就為0,后面的表達式可以不用計算,相似地,邏輯或表達式要是第一個表達式值為1則整個表達式的值就為1,后面的表達式可以不用計算。
????????再回到上面的例題,注意a++是后置++,所以(a++)&&…相當于a&&…再a++,而a的值為0,為假,則不管++b是否為真,a++&&++b都為假,則無論d++是否為真,整個邏輯與表達式的值都為假,后面的++b和d++表達式就不會再計算了,所以只有a自增了1,程序輸出結果就為1 2 3 4。
如果稍微修改一下這段代碼結果會怎樣呢?看看:
程序輸出的結果是什么?
#include <stdio.h>int main() {int i = 0;int a=1, b=2, c =3, d=4;i = a++ && ++b && d++;printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);return 0; }答案是2 3 3 5
????????因為這次a為非0,后面的++b和d++都被執行了。
那我們再來看看邏輯或表達式:
程序輸出的結果是什么?
#include <stdio.h>int main() {int i = 0;int a=1, b=2, c =3, d=4;i = a++ || ++b || d++;printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);return 0; }????????注意a為非0,不管++b是否為真,a++||++b都為真,則無論d++是否為真,整個邏輯或表達式都為真,所以后面的表達式不會再計算,只有a自增了1,輸出結果就是2 2 3 5。
我們再改改看看:
程序輸出的結果是什么?
#include <stdio.h>int main() {int i = 0;int a=0, b=2, c =3, d=4;i = a++||++b || d++;printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);return 0; }????????注意a為0,而++b非0,則a++||++b為真,所以無論d++是否為真,整個邏輯或表達式都為真,d++也就不會計算了,故輸出結果為1 3 3 4。
🔋1.9 條件操作符
exp1 ? exp2 : exp3????????如果exp1為真,則整個表達式的值為exp2的值,而exp3不執行;
????????如果exp1為假,則整個表達式的值為exp3的值,而exp2不執行。
????????要注意的就是不宜把表達式搞得太復雜,可以換成用if…else語句。
🔋1.10 逗號操作符
exp1, exp2, exp3, …expN????????逗號表達式,從左向右依次執行。整個表達式的結果是最后一個表達式的結果。
????????不過由于逗號的運算優先級是最低的,要使用逗號表達式要使用圓括號。
//代碼1 int a = 1; int b = 2; int c = (a>b, a=b+10, a, b=a+1);//代碼2 if (a =b + 1, c=a / 2, d > 0)//判斷條件也可以是逗號表達式????????不用圓括號括起來的話,很容易出錯!
比如:
????????int d = ++a, b++, c++, a++;實際上是先執行賦值表達式(d = ++a),而不是將后面的逗號表達式都執行完取最后一個的值,那是有圓括號的情況,因為逗號運算符優先級最低啊。
🔋1.11 [ ] 下標引用操作符
操作數:一個數組名 + 一個索引值
比如:
????????arr[9]的操作數是arr和9
騷操作:9[arr] 等價于 arr[9]
我直呼好家伙 Σ(っ °Д °;)っ
為什么可以這樣呢?
????????究其本質,arr[9]等價于*(arr + 9),實際上[ ]就是*( )操作數放在圓括號里相加,所以9[arr] 等價于 *(9+arr),而加法具有交換律,這兩種表示方式的意義都一樣。
🔋1.12 ( ) 函數調用操作符
????????接受一個或者多個操作數:第一個操作數是函數名,剩余的操作數就是傳遞給函數的參數。
#include <stdio.h>void test1(){printf("hehe\n");}void test2(const char *str){printf("%s\n", str);}int main(){test1();???????????test2("hello bit.");return 0;}????????所以說函數調用其實也是表達式語句,操作符為()。
🔋1.13 訪問一個結構的成員的操作符
. 結構體.成員名
-> 結構體指針->成員名
#include <stdio.h>struct Stu {char name[10];int age;char sex[5];double score; };int main() {struct Stu stu;struct Stu* pStu = &stu;stu.age = 20;//直接訪問結構成員pStu->age = 20;//間接訪問結構成員return 0;}🔋2. 操作符的屬性
🔋2.1 求值因素
復雜表達式的求值有三個影響的因素。
1. 操作符的優先級
2. 操作符的結合性
3. 是否控制求值順序。
????????兩個相鄰的操作符先執行哪個?取決于他們的優先級。當運算符共享一個運算對象時,優先級決定了求值順序。
????????如果兩者的優先級相同,則取決于他們的結合性。
????????但是大部分運算符都沒有規定同優先級下的求值順序。
????????實際上只有邏輯與,邏輯或,條件操作符和逗號操作符控制求值順序,其他操作符沒有控制求值順序,這就有可能產生一些問題代碼。
🔋2.2 例題分析?
🔋2.2.1 例1
a*b + c*d + e*f其實計算路徑不唯一,有兩種順序:
????????代碼1在計算的時候,由于*比+的優先級高,只能保證,*的計算是比+早,但是優先級并不能決定第三個*比第一個+早執行,因為算術操作符并沒有控制求值順序,所以就可能會有多條計算路徑。
????????結合性只適用于共享同一運算對象的運算符,例如,在表達式12/3*2中,/和*優先級相同,共享運算對象3,因此從左往右的結合性起作用,表達式簡化為4*2即8,但是如果從右往左計算的話會得到12/6即2,這種情況下計算的先后順序會影響最終結果。
????????對于如y = 6 * 12 + 5 * 20;這樣的語句,兩個*運算符并沒有共享同一個運算對象,因此左結合性不起作用,*優先級比+高,肯定先算乘法運算對吧,那到底先進行哪一個乘法呢?C標準未定義,實際上得根據不同硬件或編譯環境來確定,只不過該例中先算哪個對結果沒有影響而已。那萬一有影響呢?
🔋2.2.2 例2
int c = 2; int d = c + --c;????????自增自減與算術操作符復合的時候很容易產生歧義,因為求值順序不是唯一的,實際上這類是C標準未定義行為,結果有多種可能值,與編譯器有關(相當于甩鍋給編譯器)。
編譯器:我太難了……?_?
????????+左邊c的值是什么時候確定的呢?是--c后嗎?還是在--c之前呢?因為沒有統一的標準,所以都有可能。
????????若是--c后確定左邊c的值,則1+1=2,若是--c前確定左邊c的值,則2+1=3。
????????操作符的優先級只能決定自減--的運算在+的運算的前面,但是我們并沒有辦法得知,+操作符的左操作數的獲取在右操作數之前還是之后求值,所以結果是不可預測的,是有歧義的。
🔋2.2.3 例3
int main() {int i = 10;i = i-- - --i * ( i = -3 ) * i++ + ++i;printf("i = %d\n", i);return 0; }????????這個就純屬惡心人的,估計能夠到世界亂碼大賽的門檻了(笑🤣)。
????????有前輩測試過,發現在各個編譯器上的值都大不相同。
?????????這段代碼堪稱究極反面教材,提醒我們不要自作聰明玩一些花里胡哨的東西(惱),沒有什么實際意義,本身就存在歧義,可讀性就更別提了,無論是學習還是工作都要極力避免寫出過于復雜的表達式。
🔋2.2.4 例4
int fun() {static int count = 1;return ++count; }int main() {int answer;answer = fun() - fun() * fun();printf( "%d\n", answer);//輸出多少?return 0; }????????在VS下是先一個一個地進入fun()后再進行算術運算的,所以變成了2 - 3 * 4 = -12 -10。
????????還可以這樣,先分別調用后面兩個fun()進行乘法運算后再調用前面的fun()再進行減法運算,就變成了4 - 2 * 3=-6 -2。
????????我們只能通過操作符的優先級得知:先算乘法,再算減法。
????????然而函數的調用先后順序無法通過操作符的優先級確定
🔋2.2.5 例5
int main() {int i = 1;int ret = (++i) + (++i) + (++i);printf("%d\n", ret);printf("%d\n", i);return 0; }????????沒錯又是自增的鍋😅。
????????可以先執行前兩個++i再執行(++i)+(++i)得到2+3,再執行最后一個++i后執行2+3+(++i)得到9
????????還可以先把三個自增表達式執行完后再執行加法運算,即4+4+4=12。
????????這段代碼中的第一個 + 在執行的時候,第三個++是否執行,這個是不確定的,因為依靠操作符的優先級和結合性是無法決定第一個 + 和第 三個前置 ++ 的先后順序。
🔋2.2.6 總結
????????我們寫出的表達式如果不能通過操作符的屬性確定唯一的計算路徑,那這個表達式就是存在問題的。
????????為什么會有問題,因為計算順序對表達式的值有影響,一般都和++或--脫不了干系,因為自增自減會改變變量的值,導致不清楚什么時候確認變量的值。
????????所以我們盡量不要寫太復雜的表達式,尤其是帶有自增或自減操作符的。
????????遵循以下規則,不要濫用自增或自減運算符
? ? ? ? 1.如果一個變量出現在一個函數的多個參數中,不要對其使用自增或自減運算符。
? ? ? ? 2.如果一個變量多次出現在一個表達式中,不要對其使用自增或自減運算符。
🔋3.深入理解——副作用與序列點(了解一下)
🔋4.?自增運算符計算路徑問題
????????為什么計算路徑不唯一?編譯器識別表達式,是同時加載至寄存器還是分批加載完全不確定而導致的。
????????為什么不確定呢?因為對此沒有明確的規定,這也涉及到序列點的問題。
????????比如對于int b = (++i)+(++i)+(++i);,(++i)并不是一個完整表達式,而是作為整個表達式的子表達式,整個賦值表達式語句才是一個完整表達式,分號標記了序列點,也就是在執行下一條語句之前肯定會完成表達式中的三次自增,只是以怎樣的順序進行并沒有被明確規定,到底是先把三次自增都進行完(同時加載至寄存器)再進行加法運算,還是先把前兩個自增完(分批加載)后相加,再進行后面的自增,然后幾個數相加,或者其他順序都有可能。
????????具體實現細節因為標準未定義,相當于一股腦甩給了編譯器,讓它自己決定(編譯器:我**&%¥#@),不同編譯器下的過程可謂五花八門了。
🔋表達式匹配——貪心法
?????????比如a+++10,在讀到a+時,由于要讀盡可能多的字符,所以會繼續往后讀也就是a++,此時若再往后讀就無法組成有意義的符號了,所以a++是一個整體,原式相當于a++ + 10。
????????這個規則不保證表達式正確,只是“貪多”。
🔋5. 表達式和語句的那些事兒
🔋5.1 表達式?
????????表達式由運算符和運算對象組成。最簡單的表達式是一個單獨的運算對象。
下面是一些表達式:
4
-6
a*(b + c / d) / 20
q > 3
p = 5 * 2
????????運算對象可以是常量、變量或二者的結合。
????????每一個表達式都有對應值
????????賦值表達式的值與運算符左邊的值相同,也就是賦給的是什么表達式的值就是什么,如p = 6*7的值就是42。
????????關系表達式(如q>3)的值不是0就是1,關系成立就是1,不成立就是0。
🔋5.2 語句
????????語句是C程序的基本構建塊,一條語句相當于一條完整的計算機指令。在C語言中,大部分語句都以分號結尾。
????????比如legs = 4沒有分號就只是個表達式。
????????最簡單的語句是空語句:
?????????;(只有一個分號)
????????C把末尾加上一個分號的表達式都看作是一條語句(表達式語句)。
????????有些語句實際上不起作用(如3+4;),算不上真正的語句,語句應該是可以改變值或調用函數的。
????????不是所有的指令都是語句,如:
????????x = 6 + (y = 5);
????????y = 5是一條完整的指令,但它只是語句的一部分。
????????復合語句使用花括號括起來的一條或多條語句,也被稱為塊。
🔋5.3 關系
????????C的基本程序步驟由語句組成,而大多數語句都由表達式構成。
🔋敬請期待更好的作品吧~
感謝觀看,你的支持就是對我最大的鼓勵,閣下何不成人之美,點贊收藏關注走一波~
總結
以上是生活随笔為你收集整理的一文带你深入浅出C语言运算符、表达式和语句的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 而立之年——那些从一线城市退到二三线的程
- 下一篇: 3D打印成型技术:看得见摸得着的真实