目錄一覽
- 引:
- 一、運算子分類
- 二、算術運算子
- 三、移位運算子
- 1.左移原理剖析
- 2.右移原理剖析
- (1)右移正數a:
- (2)右移負數a:
- 3.注:
- 四、位運算子
- 1.原理剖析
- 2.典例
- 3.變態面試題
- 五、賦值運算子
- 六、單目運算子
- 七、關系運算子
- 八、邏輯運算子
- 九、條件運算子
- 十、逗號運算式
- 十一、下標參考,函式呼叫和結構成員
- 1.下標參考
- 2.函式呼叫
- 3.結構成員
- 十二、運算式求值
- 1.隱式型別轉換
- 2.算術轉換
- 3.運算子屬性
- 4.一些問題運算式
引:
運算子這部分內容吶,在小邊眼里,是看似食之無味,棄之又非常非常可惜的一部分,而且其實是蠻有意思的,
這么講是因為我個人認為,學校老師把它打散散布在零星各處又不夠深入,而如果出題又可能“刁鉆”,所以今天拿出來好好總結一下,
正文開始@邊通書
一、運算子分類

二、算術運算子
+,-,* 都非常簡單,唯一要注意的兩點就是 乘 * 和 取模 %
除號 /

運行結果:

取模 %(整除之后求余數)

三、移位運算子
移位運算子移動的都是 記憶體中的 二進制位,
其實對于整數的二進制位有3種表示方法:原碼,反碼,補碼,整數在記憶體中存盤的都是補碼,

呀,怕小伙伴忘記先說一下,最高位也就是符號位,正數為0,負數為1奧,
左移運算子<<(相對簡單)
1.左移原理剖析
1.左移正數a:

運行結果:

2.左移負數c:

運行結果

2.右移原理剖析
右移運算子>> (其實也沒奪復雜啦)
(1)右移正數a:

運行結果:

(2)右移負數a:

運行結果:

可見,vs2013采用的是算術右移(即補符號位),或者說大多數編譯器都采用算術右移,
且,算術右移似乎更合適一些,你是負數,右移之后仍為負數,
3.注:
可愛同學寫的胡亂代碼,如下


四、位運算子
1.原理剖析

按位與&

按位或|

按位異或^

2.典例
那么這些位運算子到底有什么用呢?下面看:
典例1:計算某整數存盤在記憶體二進制位中有多少1?
1.方法一:

代碼:
#include<stdio.h>
int main()
{
int a = 15;
int count = 0;
int i = 0;
for (i = 0; i < 32; i++)
{
if (((a >> i) & 1) == 1)
{
count++;
}
}
printf("%d\n", count);
return 0;
}
運行結果,如我所愿:

3.變態面試題
一道變態的面試題:不創建臨時變數(第三個變數),實作兩個數交換,
我們最最簡單也是最常用的辦法:

方法一:加減法

運行結果:

此方法雖滿足不創建臨時變數的要求,但是有一定問題的:
如果a,b都很大且還在int范圍之內,buta = a + b很有可能溢位,依然不是最好的解決方案,
方法二:異或的方法
#include<stdio.h>
int main()
{
int a = 3;
int b = 5;
a = a^b;
b = a^b;
a = a^b;
printf("a = %d,b = %d\n", a, b);
return 0;
}
運行結果:

原理剖析:
異或這個運算子是很有意思的,它讓我想起了集合論與圖論里的對稱差,那個小三角符號,

然而這種方法實際用的比較少:原因:
1.代碼可讀性不好,乍一看都不知道什么意思
2.只適用于整形
3.且執行效率低
優點是滿足了面試官的需求哈哈哈哈,
五、賦值運算子
賦值運算子是一個很棒的運算子,可以讓你得到一個之前不滿意的值,
1.連續賦值

2.復合賦值符
a += 2;即為a = a + 2;
a >>= 1;即為a = a>>1;
a &= 1即為a = a & 1,
顯然前者更加簡潔,
類似的有這些:
+= ,-=,*=, /=,%=,<<=,>>=,&=,|=,^=
六、單目運算子
單目運算子:即為只有一個運算元的運算子

邏輯反!
一般使用場景如下:

正號+ 負號-
比較簡單,不多講
取地址& 解參考*

sizeof
1.作用剖析 與 strlen()簡單對比

運行結果:

2.sizeof是運算子,不是函式
面試官問你:“小伙子,sizeof是不是函式?”,你說,“是!”,面試官“孩子,回家吧,”哈哈哈哈哈
下面用代碼簡要說明一下:

3.sizeof 內部的運算式是不參與真實運算的
看看以下代碼,小伙伴們覺得結果是什么?
#include<stdio.h>
int main()
{
int a = 5;
short s = 10;
printf("%d\n", sizeof(s = a + 2));//?
printf("%d\n", s);//?
return 0;
}
運行結果:

原理剖析:

4.sizeof與陣列
讀以下代碼,給出你的結果:
#include <stdio.h>
void test1(int arr[])
{
printf("%d\n", sizeof(arr));//(3)
}
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));//(2)
test1(arr);
test2(ch);
return 0;
}
原理剖析:

運行結果:
32位平臺:

按位取反~
二進制位全部取反,連符號位也要取反!!!
1.上代碼感受一下

運行結果:

除錯視窗,看看記憶體中~a的真實存在:

2.應用場景

++,- -
1.前置++

運行結果:

2.后置++

運行結果:

前置- -,后置- -,都同理,不贅述,
3.不建議研究過于復雜的相關代碼

在vs下,運行結果為12;
然而在Linux平臺下,gcc編譯器算出結果為10,
那么這段代碼本身就是錯誤的,就沒有什么必要糾結,也不要寫這樣的代碼,
至于為什么出現這種情況,后文會講,
強制型別轉換(型別)

七、關系運算子
兩 同型別 的變數之間 比較大小,比較簡單,不做贅述
<,>,<=,>=,==,!=
八、邏輯運算子
關注的是真偽,
&&,||
1.邏輯與&&
360筆試題:邏輯運算子的短路效果
#include <stdio.h>
int main()
{
int i = 0,a=0,b=2,c =3,d=4;
i = a++ && ++b && d++;
//i = a++||++b||d++;
printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
return 0; }
程式運行結果為多少?

剖析:左為假,則右邊不再計算

舉一反三:若將a的初值賦為1吶?

運行結果:

2.邏輯或||
完全類似的題目:

運行結果:

舉一反三:若將a初值再改成0吶?

運行結果:

九、條件運算子
(exp1) ? (exp2) : (exp3)

十、逗號運算式
從左向右執行,整個運算式結果是最后一個運算式 的結果,

運行結果:

應用:

十一、下標參考,函式呼叫和結構成員
1.下標參考

進一步說明[ ] 是運算子:

運行結果:

2.函式呼叫

3.結構成員

上代碼加強理解:

運行結果:

注:

十二、運算式求值
運算子可以說是為運算式而服務的,也影響著運算式的結果,主要體現在以下兩方面:
1.運算式求值的順序 ~ 運算子的優先級和結合性
2.型別轉換 ~ 運算元在求值程序中可能需要轉化為其他型別
1.隱式型別轉換
隱式,即偷偷地,沒法實在地看到,
這里要提到整形提升的概念及意義,
1.整形提升是什么?
C的整型算術運算總是至少以預設整型型別的精度來進行的,
為了獲得這個精度,運算式中的字符和短整型運算元在使用之前被轉換為普通整型,這種轉換稱為整型提升,
整形提升規則,按照資料型別的符號位來提升,
2.整形提升的意義
小邊從來不喜歡貼大段文字,但在這里覺得很有必要,有助于理解為什么要整形提升,
運算式的整型運算要在CPU的相應運算器件內執行,CPU內整型運算器(ALU)的運算元的位元組長度
一般就是int的位元組長度,同時也是CPU的通用暫存器的長度,
因此,即使兩個char型別的相加,在CPU執行時實際上也要先轉換為CPU內整型運算元的標準長度,
通用CPU(general-purpose CPU)是難以直接實作兩個8位元位元組直接相加運算(雖然機器指令中可能有這種位元組相加指令),所以,運算式中各種長度可能小于int長度的整型值,都必須先轉換為int或unsigned int,然后才能送入CPU去執行運算,
這可能也是暫存器讀寫很快的原因,
上代碼感受:
看看你覺得結果是多少?
#include<stdio.h>
int main()
{
char a = 3;
char b = 127;
char c = a + b;
printf("%d\n", c);
return 0;
}
運行結果:

有了前面的鋪墊,看到這個結果也不會感到震驚,
原理剖析:

鞏固練習1:
#include<stdio.h>
int main()
{
char a = 0xb6;
short b = 0xb600;
int c = 0xb6000000;
if (a == 0xb6)
printf("a");
if (b == 0xb600)
printf("b");
if (c == 0xb6000000)
printf("c");
return 0;
}
運行結果:只列印c

原理剖析:

鞏固練習2:

運行結果:

2.算術轉換
尋常算數轉換:如果某個運算子的各個運算元屬于不同的型別,那么其中一個運算元的必須轉換為另一個運算元的型別,否則操作就無法進行,
代碼感受:

轉換規則:

3.運算子屬性

關于優先級,這里值提供部分,有個感覺就好,記不住就加括號:

4.一些問題運算式
那是不是掌握了運算子的優先級及結合性及是否控制求值順序,就一定能得到運算式的唯一結果了吶?也不是滴,下面來看一些問題運算式,
(1)代碼1:
a*b + c*d + e*f;
由于*比 +的優先級高,只能保證,*的計算是比 +早,但是優先級并
不能決定第三個*比第一個+早執行,變數之間的牽連,會導致結果可能不同,
(2)代碼2:

(3)代碼3:

不同編譯器下跑出結果不同:
求值應有唯一路徑,那么這樣代碼就沒什么意義,

(4)代碼4

(5)代碼5
很早小邊就拿出過這段代碼了

Linux平臺下,編譯結果

vs2013平臺下,編譯結果

除錯起來,轉到反匯編,簡單看看即可:

本文完
相信小伙伴們會識訓滿滿的,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/294021.html
標籤:其他
上一篇:女朋友七夕節鴿我只為偷偷在家準備秋招簡歷?我一怒之下手把手教她寫滿分簡歷
下一篇:基于Yolov4的人群檢測,人群距離估計、基于SORT的多目標跟蹤及逆透視映射一體的系統Yolov4_DeepSocial
