?前言?
這個系列作為【全網最強】的續作,在內容上不會只是走馬燈式的瀏覽知識點,而會對各個知識點進行精講精析,達到真正熟練的地步,雖然說博主學習C語言不過兩個多月,但也熟知重難點和易錯點,會和大家一起分享學習,關注博主🍻,在學習C語言的路上結伴前行吧!
🐱?💻作者概況: 就讀南京郵電大學努力學習的大一小伙
🐱?🐉聯系方式:2879377052(QQ小號)
🐱?👤資源推薦:C語言從入門到進階
🐱?🚀今日書籍分享: 《C陷阱與缺陷》 提取碼:CSDN
🐱?👓gitee碼云鏈接: Gitee倉庫 test
📚參考書籍:《C缺陷與陷阱》
[有問題歡迎留言討論,作者盡力幫忙]?
目錄
一、優先級和結合性
二、 算數運算子
三、關系運算子
四、邏輯運算子
五、位操作運算子
六、賦值運算子
七、條件運算子
八、求位元組數運算子
九、逗號運算子
十、強制型別轉換運算子
十一、詞法運算中的“大嘴法”
十二、優先級與結合性總結
十三、后記
一、優先級和結合性
優先級:優先級的高低決定運算的優先順序,如最常見的加和乘,3+2*5中乘法優先級高
結合性:C語言中各運算子的結合性分為兩種,即左結合性(自左至右)和右結合性(自右至左),結合性指的是編譯器解釋的順序,如賦值運算子的結合性為自右向左,a=b=c實際將c賦值給b,再將b賦值給a,從右往左解釋
(運算子也叫運算子)
二、 算數運算子
<第一組> + - *(乘) /(除) %(取余)
解釋:+ - * /詳細很好理解,解釋一下%,取余表示取余數,如30%11=8,而30/11=2,
注意點:
①除號兩邊資料型別不同時會發生型別轉,
舉例:如1/2=0,因為除號兩邊都是整形,所以結果也是整形,1除2等于0余1,1/2.0,除號一邊是整數,另一邊是浮點數,型別不同,int型轉化為double型,因此結果也是浮點數,所以答案是0.5,
自動型別轉化原則:將參與運算的運算元轉換成其中占用記憶體大的型別,防止資料丟失

②取余兩邊必須是整型,不能出現浮點數

<第二組> ++(自增) --(自減)
解釋:++等價于a=a+1 --等價于a=a-1
注意點 :
①前置++與后置++問題
前置++,先自增后使用;后置加加,先使用后加加(--同理)

程序分析:為b賦值時,++在a后后置加加,所以現將a的值賦值給b,所以b的值為5,為b賦值后a自增1變成6,為c賦值時,++在a前為前置加加,所以先將a自增再賦值給c,a自增后值為7,所以c的值為7,
為什么將上面的算數運算子分成一二組呢,因為這兩組的優先級和結合性不同(在最后總結)
三、關系運算子
<第一組> > < <= >= <=
<第二組> ==(相等) !=(不相等)
(為什么分成兩組呢,兩組的優先級不同,最后有總結)
注意點:
①大于等于中>與=之間不要留有空格,否則不會被識別為>=,其他同理
②注意:==與!=的優先級低于第一組的關系運算子
舉例:if(10>=5 == 4<=3)
>=和<=的優先級高于==,所以最后計算==
10>=5為真,整體的值為1;4<=3為假,整體的值為0,1==0為假,所以if陳述句不執行
③?易錯點:注意別把==寫成=,
#include<stdio.h>
int main()
{
for (int k = -1; k = 1; k++)
{
printf("*****\n");
}
return 0;
}
分析:寫成k=1,而不是k==1,則每次不是進行判斷而是將k賦值成1,1表示真,回圈還會繼續,所以程式的結果是死回圈,在設計程式時要特別小心這種錯誤,
int main()
{
int a = 1;
int b = 2;
int c = 3;
if (a <= b <= c)
{
printf("haha");
}
return 0;
}
④所有運算式都有值,這里if里的邏輯是:a<=b為真,所以a<=b的值為1,1<=c成立,所以if陳述句執行,注意這里并不是字面看來的判斷是不是a<=b<=c,
四、邏輯運算子
<第一組> && (邏輯與)
<第二組> ! (邏輯非)
<第三組> || (邏輯或)
(自上往下優先級降低)
解釋:
1.邏輯與:兩邊都為真整體才為真,如 if(1>0 && 8 >9)左邊為真,右邊為假,整體為假,
2.邏輯或:兩邊只要有真整體就位真,如上面的例子用 || if陳述句會執行
3.邏輯假:真變假,假變真,在vs編譯器下,真用1表示,假用0表示
如 !1=0 !6=0 !0=1
注意點:
①邏輯短路?:對于&&,只要一邊為0,就可以確定整體的值為0,其他的值就不會再運算,對于||,只要一邊為1,就可以確定整體的值為1,其他的值就不會再運算,
【總結】只在需要時對右邊求值
②注意別把&&寫成&,現在分析三種可能造成 &&寫成 &程式“沒錯的原因”
考慮以下代碼,其功能是查詢表中一個特定元素,
int i = 0;
while (i < tabsize && tab[i] != x)
{
i++;
}
現分析將&&替換成&仍然能"正常作業"的原因,
原因一:只要xy的值都限制在0~1,x&&y和x&y的結果始終相同,
原因二:陣列結尾之后的下一個元素,只要不改變他的值而僅僅是讀取,沒有什么大的危害;并且不同與&&的“邏輯短路”,&要求兩邊都要被求值,所以會查看tab陣列結尾的后一元素(即使不存在)
(&&替換成&問題摘錄于博主以前的博客,C經典書籍筆記——C陷阱與缺陷③(語意陷阱))
驗證優先級
上面提到優先級 && > ! > ||,我們設計程式來驗證
//驗證&&優先級高于!
int main()
{
int a = 0;
int b = 1;
if (!a && b)
{
printf("haha");
}
return 0;
}
//驗證!優先級高于||
int main()
{
int a = 0;
int b = 0;
if (!a || b)
{
printf("%d",a);
}
}
五、位操作運算子
<第一組> <<(左移) >>(右移)
<第二組> ~(位非) ^(位異或) &(位與) |(位或)
【優先級都不同,這里不重要,所以在最后總結】
解釋:位運算子是對二進制位的補碼進行操作的,這里涉及資料存盤的原碼反碼補碼,原反補不是這里的重點,之后作者也會發文總結,若有不大會的可以參考這篇博客 鏈接
功能:
①左移:左移位將第一運算元的每一位向左平移第二運算元指定的位數(如-1<<2表示將-1的二進制位每一位向左一兩位),右邊空位補0,左邊移出去的丟棄,
(先以-1左移1位為例圖解,-1的補碼為 11111111 11111111 11111111 11111111)

②右移:右移位將第一運算元的每一位向右平移第二運算元指定的位數,右邊丟棄,左邊空 位補值,左邊補值有兩種情況,
1.有符號數——符號位補符號值
2.無符號數——補0
(先以1右移1位為例圖解,1的補碼為 00000000 00000000 00000000 00000001)

③位非:按位取反

④位與:對應二進制有0出0,全1出1
⑤位或:對飲二進制位有1出1,全0出0
⑥位異或:對應二進制位上數字相同出0,不同出1
應用:
①用位運算子進行二進制有關的操作極其方便
例一:求一個整數存盤在記憶體中的二進制中1的個數
方法:1.利用%2和/2每次計算一個數字當前二進制位最后一位是0還是1
【缺陷】不能統計負數,局限性很大
2.利用 &1,&10,&100……判斷每一位二進制最后一位是1還是,100等用1<<2獲取
(不是100而是二進0制的100)
【缺陷】運算次數過多
3.最優解:a & (a-1),每進行一次就可以使原數字二進制少一個1,計數操作多少次使 原數字變成0,即可得出二進制中1的個數
【理解】每次a-1只會改變二進制中最后一位的1,使原來的 10 變成 01,其他不 變,&操作自然會使原二進制少一個1
//方法一:
int main()
{
int n = 0;
int count = 0;
scanf("%d",&n);
while (n)
{
count += n % 2;
n /= 2;
}
printf("二進制序列中1的個數為:%d",count);
return 0;
}
//方法二
int main()
{
int n = 0;
int count = 0;
scanf("%d",&n);
for (int i = 0; i < 32; i++)
{
if (n & 1 << i)
count++;
}
printf("二進制序列中1的個數為:%d",count);
return 0;
}
//方法三
int main()
{
int n = 0;
int count = 0;;
scanf("%d",&n);
while (n)
{
count++;
n = n & (n - 1);
}
printf("二進制序列中1的個數為:%d",count);
return 0;
}
②不使用變數使得兩數交換(代碼如下)
舉個例子就可以理解,可以這樣形象記憶a^b是一個寶盒,tmp^a相當于把a作為鑰匙打開,就獲得了b,但要強調的是這樣的代碼可讀性不佳,最好的還是用三個變數,
int main()
{
int a = 1;
int b = 10;
int tmp = 0;
tmp = a ^ b;
a = tmp ^ a;
b = tmp ^ b;
return 0;
}
六、賦值運算子
<簡單賦值符> =
<復合算術賦值符> += -= *= /= %=
<復合位運算賦值符> &= |= ^= >>= <<=
(三種賦值運算子的優先級都相同)
符合運算子如a*=b等價于a=a*b,只是縮寫罷了,注意理解,其他以此類推
注意點
賦值運算子的優先級是所有雙目運算子中最低的,使用時尤其要注意是否要加括號
練習題: 列印的結果是什么
#include<stdio.h>
int main()
{
int a = 3, b = 5;
int n = 2;
int m;
(m = a <= 3) && a + b < 8;
printf("%d\n", n += n -= n * n);
printf("%d\n", n += n -= n *= n);
printf("%d",m );
return 0;
}
七、條件運算子
<第一組> ?:
(唯一的三目運算子)
作用:
所有運算式都有一個值,包括條件運算子,如果第一個子運算式為真,則條件運算式的值為第二個子運算式的值,否則它就是第三個子運算式的值,
int main()
{
int a = 10;
int b = 1;
int max = 0;
max = a > b ? a : b;
return 0;
}
八、求位元組數運算子
想必大家對sizeof都很熟悉了,這里就不贅述了,
注意:sizeof不是函式,而是運算子
九、逗號運算子
作用
逗號運算式的值是最后一個運算式的值,注意使用時要加括號
int main()
{
int a = 0;
int b = 0;
int c = (a + 10, b = a + 10, b + 10);//最后把b+10的值附給c
printf("%d",c);
}
有時候要小心,不經意間就會錯,如下面的二維陣列實際只有兩個元素
#include <stdio.h>
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
int *p;
p = a[0];
printf( "%d", p[0]);
return 0;
}
十、強制型別轉換運算子
想必大家對()也都很熟悉了,這里就不贅述了,只是注意()里的是型別而不是變數
int main()
{
int a = 1;
int b = 2;
float tmp = 0.0;
tmp = (float)a / b;
printf("%f",tmp);
return 0;
}
十一、詞法運算中的“大嘴法”
思考
編譯器對連續輸入的符號是看成整體還是分別處理
如a/*b,是看成a/ (*b)還是把/*以注釋符看待
原則 :大嘴法(也叫貪心法)
一個符號應該盡可能包含更多的字符,即在輸入一個可能成為符號的字符后繼續讀入下一個字符,判斷兩個字符組成的字串能否組成一個符號,重復上述程序,直至不能組成一個可能的符號,
練習: a+++++b的含義什么 【原來的解釋有誤,現糾正】
分析:根據貪心法,++組合后再一次++最后以為((a++)++))+b,然而這個式子在語法上是錯誤的,因為a++的結果不能作為左值,因此編譯器實際不會接受a++后的++,也就會語法報錯,在編程實踐中的建議就是,盡量避免使用類似的結構
十二、優先級與結合性總結

關于優先級的記憶方法,博主之前做過總結,大家可以參考 C經典書籍筆記——C陷阱與缺陷②(語法陷阱之優先級)
十三、后記
【C語言知識精講②】出爐啦 【C語言知識精講②】static修飾區域變數,全域變數,函式區別
這四個沒有提到的運算子會在之后關于陣列,指標,結構體的專題里細講,這里先不提了,
還有哦提醒一下,define不是運算子,而是預處理指令,
指標運算子:*和&
分量運算子:. ->
下標運算子:[ ]
函式呼叫運算子:()
關于C語言運算子博主就不再總結了,因為之前看到過一篇不錯的博文,大家可以看看
詳解C語言運算子(史上最全的重點總結!全在這里!)
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/335217.html
標籤:其他
上一篇:我想在1024發的一篇博客。
