日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

一文带你深入浅出C语言运算符、表达式和语句

發布時間:2024/3/13 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 一文带你深入浅出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语言运算符、表达式和语句的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。