html5 中文網(wǎng)站模板廣告設(shè)計(jì)網(wǎng)站
- 博客主頁(yè):算法歌者
- 本篇專欄:[C]
- 您的支持,是我的創(chuàng)作動(dòng)力。
文章目錄
- 0、總結(jié)
- 1、操作符的分類
- 2、二進(jìn)制和進(jìn)制轉(zhuǎn)換
- 2.1、2進(jìn)制轉(zhuǎn)10進(jìn)制
- 2.2、10進(jìn)制轉(zhuǎn)2進(jìn)制
- 2.3、2進(jìn)制轉(zhuǎn)8進(jìn)制和16進(jìn)制
- 3、原碼、反碼、補(bǔ)碼
- 4、移位操作符
- 4.1 左移操作符
- 4.2 右移操作符
- 4.3 警告:不要移動(dòng)負(fù)數(shù)位
- 5、位操作符
- 5.1 例1:觀察位操作符
- 5.2 例2:不能創(chuàng)建臨時(shí)變量(第三個(gè)變量),實(shí)現(xiàn)兩個(gè)數(shù)的交換。
- 5.3 例3:求一個(gè)整數(shù)存儲(chǔ)在內(nèi)存中的二進(jìn)制中1的個(gè)數(shù)。
- 5.4 例4:如何判斷一個(gè)數(shù)是否是2的次方數(shù)?
- 5.5 例5:二進(jìn)制位置0或者置1
- 6、逗號(hào)表達(dá)式
- 7、下標(biāo)訪問(wèn)[]、函數(shù)調(diào)用()
- 8、結(jié)構(gòu)成員訪問(wèn)操作符
- 9、操作符的屬性:優(yōu)先級(jí)、結(jié)合性
- 10、其他
- 10.1 整型提升
- 10.2 算術(shù)轉(zhuǎn)換
0、總結(jié)
1、操作符的分類
- 算術(shù)操作符:
+、-、*、/、%
- 移位操作符:
<<、>>
- 位操作符:
&、|、^、~
- 賦值操作符:
=、+=、-=、*=、/=、%=、<<=、>>=、&=、|=、^=
- 單目操作符:
!、++、--、&(取地址操作符)、*(解引用操作符)、+(正號(hào)操作符)、-(負(fù)號(hào)操作符)、~、sizeof、(類型)
- 關(guān)系操作符:
>、>=、<、<=、==、!=
- 邏輯操作符:
&&、||
- 條件操作符:
?:
- 逗號(hào)表達(dá)式:
,
- 下標(biāo)引用:
[]
- 函數(shù)調(diào)用:
()
- 結(jié)構(gòu)成員訪問(wèn):
.、->
操作符中一些操作符和二進(jìn)制有關(guān)系,需要先簡(jiǎn)單了解二進(jìn)制和進(jìn)制轉(zhuǎn)換的知識(shí)。
2、二進(jìn)制和進(jìn)制轉(zhuǎn)換
進(jìn)制,是數(shù)值的不同表示形式。例如,數(shù)值15的各種進(jìn)制的表示形式:
15的2進(jìn)制: 1111
15的8進(jìn)制: 17
15的10進(jìn)制:15
15的16進(jìn)制:F
觀察10進(jìn)制,得出規(guī)律如下:
- 10進(jìn)制中滿10進(jìn)1。
- 10進(jìn)制的數(shù)字每一位都是0~9的數(shù)字組成。
那么,二進(jìn)制也是一樣的:
- 2進(jìn)制中滿2進(jìn)1。
- 2進(jìn)制的數(shù)字每一位都是0~1的數(shù)字組成。
2.1、2進(jìn)制轉(zhuǎn)10進(jìn)制
在10進(jìn)制中,每一位是權(quán)重,10進(jìn)制的數(shù)字從右向左是個(gè)位、十位、百位…,分別每一位的權(quán)重是100,101,102…。
在十進(jìn)制中,如圖:
假設(shè)2進(jìn)制的1101,如何理解呢?如圖:
2.2、10進(jìn)制轉(zhuǎn)2進(jìn)制
從圖中可以知道:余數(shù)作用是求出是當(dāng)前數(shù)的個(gè)位。
2.3、2進(jìn)制轉(zhuǎn)8進(jìn)制和16進(jìn)制
原理:二進(jìn)制數(shù)可以按照3個(gè)位數(shù)(或更一般地,按照任意固定數(shù)量的位數(shù))轉(zhuǎn)換到其他進(jìn)制,這是基于進(jìn)制轉(zhuǎn)換的基本原理和方便性考慮的。
在8進(jìn)制中:8進(jìn)制的數(shù)字每一位是0 ~ 7的,0 ~ 7的數(shù)字各自寫成2進(jìn)制,最多有3個(gè)2進(jìn)制位就足夠了。所以2進(jìn)制轉(zhuǎn)8進(jìn)制數(shù)的時(shí)候,從2進(jìn)制序列中右邊低位開始向左每3個(gè)2進(jìn)制位會(huì)換算一個(gè)8進(jìn)制位,剩余不夠3個(gè)2進(jìn)制位的直接換算。
注意:0開頭的數(shù)字,會(huì)被當(dāng)做8進(jìn)制。
在16進(jìn)制中:16進(jìn)制的數(shù)字每一位是0 ~ 9、a ~ f的,0 ~ 9、a ~ f的數(shù)字各自寫成2進(jìn)制,最多有4個(gè)2進(jìn)制就足夠了。比如f的二進(jìn)制1111,所以在2進(jìn)制轉(zhuǎn)16進(jìn)制數(shù)的時(shí)候,從2進(jìn)制序列中右邊低位開始向左每4個(gè)2進(jìn)制位會(huì)換算一個(gè)16進(jìn)制位,剩余不夠4個(gè)二進(jìn)制位的直接換算。
注意:16進(jìn)制表示的時(shí)候前面加0x。
例如:2進(jìn)制的01101011,換成16進(jìn)制:0x6b。
進(jìn)制轉(zhuǎn)換總結(jié)如下:
- 二進(jìn)制轉(zhuǎn)八進(jìn)制,3位二進(jìn)制
- 二進(jìn)制轉(zhuǎn)十進(jìn)制,權(quán)和計(jì)算法
- 二進(jìn)制轉(zhuǎn)十六進(jìn)制,4位二進(jìn)制
- 十進(jìn)制轉(zhuǎn)十六進(jìn)制,先轉(zhuǎn)二進(jìn)制之后轉(zhuǎn)十六進(jìn)制
#include <stdio.h>
int main()
{printf("%d\n", 153); // 十進(jìn)制printf("%d\n", 0153); // 八進(jìn)制printf("%d\n", 0x153); // 十六進(jìn)制return 0;
}
輸出:
153
107
339
3、原碼、反碼、補(bǔ)碼
二進(jìn)制:
- 整數(shù)的2進(jìn)制表示方法:原碼、反碼、補(bǔ)碼。
- 有符號(hào)整數(shù)的2進(jìn)制三種表示方法均有符號(hào)位和數(shù)值位兩部分。
- 2進(jìn)制序列中,最高位的1位是被當(dāng)做符號(hào)位,剩余都是數(shù)值位。在符號(hào)位中,0表示“正”,1表示“負(fù)”。
- 原碼:直接將數(shù)值按照正負(fù)數(shù)的形式翻譯成二進(jìn)制得到的就是原碼。
- 反碼:將原碼的符號(hào)位不變,其他位依次按位取反就可以得到反碼。
- 補(bǔ)碼:反碼+1就得到補(bǔ)碼。
- 正整數(shù)的原、反、補(bǔ)碼都相同。
- 負(fù)整數(shù)的三種表示方法各不相同。
值得一提是:
- 反碼得到原碼也是可以使用:取反,+1的操作。
對(duì)整形來(lái)說(shuō):數(shù)據(jù)存放內(nèi)存中其實(shí)存放的是補(bǔ)碼,對(duì)于整數(shù)的計(jì)算統(tǒng)一都是用補(bǔ)碼來(lái)計(jì)算。
為什么呢?
在計(jì)算機(jī)系統(tǒng)中,數(shù)值一律用補(bǔ)碼來(lái)表示和存儲(chǔ)。原因在于,使用補(bǔ)碼,可以將符號(hào)位和數(shù)值域統(tǒng)一處理;
同時(shí),加法和減法也可以統(tǒng)一處理(CPU只有加法器),此外,補(bǔ)碼與原碼相互轉(zhuǎn)換,其運(yùn)算過(guò)程是相同的,不需要額外的硬件電路。
#include <stdio.h>
int main()
{int a = -10;// -10是存放在a中,a是整型變量,是4個(gè)字節(jié),32bit位// 10000000 00000000 00000000 00001010 - 原碼// 11111111 11111111 11111111 11110101 - 反碼// 11111111 11111111 11111111 11110110 - 補(bǔ)碼int b = 10;// 00000000 00000000 00000000 00001010 - 原碼// 00000000 00000000 00000000 00001010 - 反碼// 00000000 00000000 00000000 00001010 - 補(bǔ)碼return 0;
}
4、移位操作符
左移操作符:<<
右移操作符:>>
注意:移位操作符的操作數(shù)只能是整數(shù)。
4.1 左移操作符
移位規(guī)則:左邊拋棄、右邊補(bǔ)0。
#include <stdio.h>
int main()
{int num = 10;// 移位規(guī)則:左邊拋棄、右邊補(bǔ)0int n = num << 1;printf("n = %d\n", n);printf("num = %d\n", num);return 0;
}
輸出:
n = 20
num = 10
4.2 右移操作符
移位規(guī)則:首先右移運(yùn)算符分兩種:
- 1、邏輯右移:左邊用0填充,右邊丟棄。
- 2、算術(shù)右移:左邊用原該值的符號(hào)位填充,右邊丟棄。(最常見)
注意:右移采取邏輯右移還是算術(shù)右移取決于編譯器的實(shí)現(xiàn),常見的編譯器都是算術(shù)右移。
#include <stdio.h>
int main()
{int num = 10;// 移位規(guī)則:左邊用原該值的符號(hào)位填充,右邊丟棄。int n = num >> 1;printf("n = %d\n", n);printf("num = %d\n", num);return 0;
}
輸出:
n = 5
num = 10
4.3 警告:不要移動(dòng)負(fù)數(shù)位
對(duì)于移位運(yùn)算符,不要移動(dòng)負(fù)數(shù)位,這個(gè)是標(biāo)準(zhǔn)未定義的。
例如:
int num = 10;
num >> -1; //error
5、位操作符
位操作符有:
- &:按位與
- | :按位或
- ^:按位異或
- ~:按位取反
注意:他們的操作數(shù)必須是整數(shù)。
5.1 例1:觀察位操作符
#include <stdio.h>
int main()
{// 10000000 00000000 00000000 00000011 -3的原碼// 11111111 11111111 11111111 11111100// 11111111 11111111 11111111 11111101 -3的補(bǔ)碼int num1 = -3;// 00000000 00000000 00000000 00000101 5的補(bǔ)碼int num2 = 5;// 00000000 00000000 00000000 00000101 按位與,得出5printf("%d\n", num1 & num2);// 11111111 11111111 11111111 11111101 按位或,補(bǔ)碼// 10000000 00000000 00000000 00000011 計(jì)算,反碼+1,得出-3printf("%d\n", num1 | num2);// 11111111 11111111 11111111 11111000 按位異或,補(bǔ)碼// 10000000 00000000 00000000 00001000 計(jì)算,反碼+1,得出-8printf("%d\n", num1 ^ num2);// 00000000 00000000 00000000 00000000 0的補(bǔ)碼// 11111111 11111111 11111111 11111111 ~0// 10000000 00000000 00000000 00000001 計(jì)算,反碼+1,得出-1printf("%d\n", ~0);return 0;
}
輸出:
5
-3
-8
-1
5.2 例2:不能創(chuàng)建臨時(shí)變量(第三個(gè)變量),實(shí)現(xiàn)兩個(gè)數(shù)的交換。
#include <stdio.h>
int main()
{int a = 100;int b = 202;a = a ^ b;b = a ^ b;a = a ^ b;printf("a = %d b = %d\n", a, b);return 0;
}
輸出:
a = 202 b = 100
5.3 例3:求一個(gè)整數(shù)存儲(chǔ)在內(nèi)存中的二進(jìn)制中1的個(gè)數(shù)。
方法1:
// 方法1:
#include <stdio.h>
int main()
{int num = 0;scanf("%d", &num);int count = 0;while (num){if (num % 2 == 1)count++;num = num / 2;}printf("%d\n", count);return 0;
}
在負(fù)數(shù)中,方法1失效了,比如-1在內(nèi)存中1的個(gè)數(shù)有有32,而結(jié)果是0。
繼續(xù)優(yōu)化,寫方法2:
// 方法2:
#include <stdio.h>
int main()
{int num = 0;scanf("%d", &num);int count = 0;for (int i = 0; i < 32; i++){if (num & (1 << i))count++;}printf("%d\n", count);return 0;
}
方法2中是必須循環(huán)32次,再繼續(xù)優(yōu)化,寫方法3:
// 方法3:
#include <stdio.h>
int main()
{int num = 0;scanf("%d", &num);int count = 0;while (num){count++;num = num & (num - 1); }printf("%d\n", count);return 0;
}
在方法3中,每次循環(huán),都會(huì)將 num
的最低位的1清零,并將計(jì)數(shù)器 count
加1。
5.4 例4:如何判斷一個(gè)數(shù)是否是2的次方數(shù)?
if (n & (n - 1) == 0)
{n就是2的次方數(shù)
}
5.5 例5:二進(jìn)制位置0或者置1
編寫代碼將13二進(jìn)制序列的第5位修改為1,然后再改回0。
13的2進(jìn)制序列: 00000000 00000000 00000000 00001101
將第5位置為1后: 00000000 00000000 00000000 00011101
將第5位再置為0: 00000000 00000000 00000000 00001101
代碼如下:
#include <stdio.h>
int main()
{int a = 13;a = a | (1 << 4);printf("a = %d\n", a);a = a & ~(1 << 4);printf("a = %d\n", a);return 0;
}
6、逗號(hào)表達(dá)式
逗號(hào)表達(dá)式:就是用逗號(hào)隔開的多個(gè)表達(dá)式,從左向右依次執(zhí)行,整個(gè)表達(dá)式的結(jié)果是最后一個(gè)表達(dá)式的結(jié)果。
exp1, exp2, exp3, ... expN
7、下標(biāo)訪問(wèn)[]、函數(shù)調(diào)用()
下標(biāo)引用操作符:操作數(shù)為一個(gè)數(shù)組名 + 一個(gè)索引值。
int arr[10]; // 創(chuàng)建數(shù)組
arr[9] = 10; // 使用下標(biāo)引用操作符
[ ] 的兩個(gè)操作數(shù)是arr和9。
函數(shù)調(diào)用操作符:接受一個(gè)或者多個(gè)操作數(shù),第一個(gè)操作數(shù)是函數(shù)名,剩余的操作數(shù)就是傳遞給函數(shù)的參數(shù)。
8、結(jié)構(gòu)成員訪問(wèn)操作符
點(diǎn)操作符(.):直接訪問(wèn)結(jié)構(gòu)體成員,點(diǎn)操作符接受兩個(gè)操作數(shù)。
使用方式:結(jié)構(gòu)體變量.成員名。如下:
#include <stdio.h>
struct Point
{int x;int y;
}p = { 1,2 };int main()
{printf("x:%d, y:%d\n", p.x, p.y);return 0;
}
運(yùn)行:
x:1, y:2
箭頭操作符(->):間接訪問(wèn)結(jié)構(gòu)體成員。 有時(shí)候我們得到的不是?個(gè)結(jié)構(gòu)體變量,而是得到了一個(gè)指向結(jié)構(gòu)體的指針。
使用方式:結(jié)構(gòu)體指針->成員名。如下:
#include <stdio.h>
struct Point
{int x;int y;
}p = { 1,2 };int main()
{struct Point* ptr = &p;ptr->x = 10;ptr->y = 20;printf("x:%d, y:%d\n", ptr->x, ptr->y);return 0;
}
運(yùn)行:
x:10, y:20
9、操作符的屬性:優(yōu)先級(jí)、結(jié)合性
C語(yǔ)?的操作符有2個(gè)重要的屬性:優(yōu)先級(jí)、結(jié)合性,這兩個(gè)屬性決定了表達(dá)式求值的計(jì)算順序。
結(jié)合性:如果兩個(gè)運(yùn)算符優(yōu)先級(jí)相同,優(yōu)先級(jí)沒辦法確定先計(jì)算哪個(gè)了,這時(shí)候就看結(jié)合性了,則根據(jù)運(yùn)算符是左結(jié)合,還是右結(jié)合,決定執(zhí)行順序。大部分運(yùn)算符是左結(jié)合(從左到右執(zhí)行),少數(shù)運(yùn)算符是右結(jié)合(從右到左執(zhí)行),比如賦值運(yùn)算符( = )。
建議大概記住這些操作符的優(yōu)先級(jí)就行,其他操作符在使用的時(shí)候查看下面表格就可以了。
? 圓括號(hào)(()
)
? 自增運(yùn)算符(++
),自減運(yùn)算符(--
)
? 單?運(yùn)算符(+
和-
)
? 乘法(*
),除法(/
)
? 加法(+
),減法(-
)
? 關(guān)系運(yùn)算符(<
、>
等)
? 賦值運(yùn)算符(=
)
官方文檔:https://zh.cppreference.com/w/c/language/operator_precedence
10、其他
10.1 整型提升
C語(yǔ)言中整型算術(shù)運(yùn)算總是至少以缺省整型類型的精度來(lái)進(jìn)行的。
缺省:在計(jì)算機(jī)科學(xué)和相關(guān)領(lǐng)域中,它通常指的是“默認(rèn)”或“預(yù)設(shè)”的意思。
整型提升:為了獲得這個(gè)精度,表達(dá)式中的字符和短整型操作數(shù)在使用之前被轉(zhuǎn)換為普通整型。
整型提升的意義:
表達(dá)式的整型運(yùn)算要在CPU的相應(yīng)運(yùn)算器件內(nèi)執(zhí)行,CPU內(nèi)整型運(yùn)算器(ALU)的操作數(shù)的字節(jié)長(zhǎng)度一般就是int的字節(jié)長(zhǎng)度,同時(shí)也是CPU的通用寄存器的長(zhǎng)度。因此,即使兩個(gè)char類型的相加,在CPU執(zhí)行時(shí)實(shí)際上也要先轉(zhuǎn)換為CPU內(nèi)整型操作數(shù)的標(biāo)準(zhǔn)長(zhǎng)度。
通用CPU(general-purposeCPU)是難以直接實(shí)現(xiàn)兩個(gè)8比特字節(jié)直接相加運(yùn)算(雖然機(jī)器指令中可能有這種字節(jié)相加指令)。所以,表達(dá)式中各種長(zhǎng)度可能小于int長(zhǎng)度的整型值,都必須先轉(zhuǎn)換為int或unsigned int,然后才能送入CPU去執(zhí)行運(yùn)算。
char a, b, c;
...
a = b + c;
b和c的值被提升為普通整型,然后再執(zhí)行加法運(yùn)算。加法運(yùn)算完成之后,結(jié)果將被截?cái)?#xff0c;然后再存儲(chǔ)于a中。
如何進(jìn)行整體提升呢?
- 有符號(hào)整數(shù)提升是按照變量的數(shù)據(jù)類型的符號(hào)位來(lái)提升的。
- 無(wú)符號(hào)整數(shù)提升,高位補(bǔ)0。
// 負(fù)數(shù)的整形提升
char c1 = -1;
整型提升:
11111111 11111111 11111111 11111111
// 正數(shù)的整形提升
char c2 = 1;
整型提升:
00000000 00000000 00000000 00000001
10.2 算術(shù)轉(zhuǎn)換
如果某個(gè)操作符的各個(gè)操作數(shù)屬于不同的類型,那么除非其中一個(gè)操作數(shù)的轉(zhuǎn)換為另一個(gè)操作數(shù)的類型,否則操作就無(wú)法進(jìn)行。下面的層次體系稱為尋常算術(shù)轉(zhuǎn)換。
long double
double
float
unsigned long int
long int
unsigned int
int
如果某個(gè)操作數(shù)的類型在上面這個(gè)列表中排名靠后,那么首先要轉(zhuǎn)換為另外一個(gè)操作數(shù)的類型后執(zhí)行運(yùn)算。
完。