文章目錄
- 1.操作符的分類
- 2.?進制和進制轉換
- 3.原碼、反碼、補碼
- 4.移位操作符
- 4.1 左移操作符
- 4.2 右移操作符
- 5.位操作符:&、|、^、~
- 5.1 &:按位與
- 5.2 |:按位或
- 5.3 ^:按位異或
- 5.4 ~:按位取反
- 5.5 例題
- 例題1
- 例題2
- 例題3
- 例題4
1.操作符的分類
-
算術操作符:
+
、-
、*
、/
、%
-
移位操作符:
<<
,>>
-
位操作符:
&
,|
,^
-
賦值操作符:
=
、+=
、-=
、*=
、/=
、%=
、<<=
、>>=
、&=
、|=
、^=
-
單目操作符:
!
、++
、--
、&
、*
、+
、-
、~
、sizeof
、(類型)
-
關系操作符:
>
、>=
、<
、<=
、==
、!=
-
邏輯操作符:
&&
、||
-
條件操作符:
? :
-
逗號表達式:
,
-
下標引用:
[]
-
函數調用:
()
-
結構成員訪問:
.
、->
2.?進制和進制轉換
進制轉換的教程網上比比皆是,而且視頻看起來肯定比文字清晰。這里就不過多贅述了。
我們需要掌握一下幾個:
- 2進制
轉
10進制 - 10進制
轉
2進制 - 2進制
轉
8進制和16進制 - 8進制和16進制
轉
2進制 - 8進制
轉
16進制 - 16進制
轉
8進制
3.原碼、反碼、補碼
整數的2進制表示方法有三種:
-
原碼
-
反碼
-
補碼
有符號整數的三種表示方法均有符號位和數值位兩部分,2進制序列中,最高位的1位是被當做符號位,剩余的都是數值位。
符號位都是用0表示“正”,用1表示“負”。
正整數的原、反、補碼都相同。
原碼:直接將數值按照正負數的形式翻譯成二進制得到的就是原碼。
反碼:正數反碼和原碼一樣。
補碼:正數補碼和原碼一樣。
負整數的三種表示方法各不相同。
原碼:直接將數值按照正負數的形式翻譯成二進制得到的就是原碼。(注意符號位)
反碼:將原碼的符號位不變,其他位依次按位取反就可以得到反碼。
補碼:反碼+1就得到補碼。
4.移位操作符
只有整數才能運用移位操作符。
4.1 左移操作符
例1:
#include <stdio.h>
int main()
{int num = 10;int n = num<<1;//10補碼: 00000000 00000000 00000000 00001010//10<<1補碼:0 00000000 00000000 00000000 00010100//這里最前面的0被丟掉了printf("n= %d\n", n);printf("num= %d\n", num);return 0;
}
打印:
n= 20
num= 10
例2:
#include <stdio.h>
int main()
{int num = -1;int n = num<<1;//-1原碼: 10000000 00000000 00000000 00000001//-1反碼: 11111111 11111111 11111111 11111110//-1補碼: 11111111 11111111 11111111 11111111
//-1<<1補碼:0 11111111 11111111 11111111 11111110//這里最前面的0被丟掉了printf("n= %d\n", n);printf("num= %d\n", num);return 0;
}
打印:
n= -2
num= -1
4.2 右移操作符
右移運算分兩種:
-
邏輯右移:左邊用0填充,右邊丟棄。
-
算術右移:左邊用原該值的符號位填充(正數左邊補0,負數左邊補1),右邊丟棄。
右移是算術右移還是邏輯右移是取決于編譯器的。通常采用的都是算數右移。
例1:
#include <stdio.h>
int main()
{int num = -10;//-10原碼:10000000 00000000 00000000 00001010//-10反碼:11111111 11111111 11111111 11110101//-10補碼:11111111 11111111 11111111 11110110//邏輯右移:01111111 11111111 11111111 11111011 |0(這個0被丟棄了)//算術右移:11111111 11111111 11111111 11111011 |0(這個0被丟棄了)//這里采用算數右移//右移補碼:11111111 11111111 11111111 11111011//右移反碼:10000000 00000000 00000000 00000100//右移原碼:10000000 00000000 00000000 00000101--->-5int n = num>>1;printf("n= %d\n", n);printf("num= %d\n", num);return 0;
}
打印:
n= -5
num= -10
5.位操作符:&、|、^、~
雙目操作符:
- &:按位與
- |:按位或
- ^:按位異或
單目操作符:
- ~:按位取反
5.1 &:按位與
有0則0。
#include <stdio.h>
int main()
{int a = 6;// 6的補碼:00000000 00000000 00000000 00000110int b = -7;//-7的原碼:10000000 00000000 00000000 00000111//-7的反碼:11111111 11111111 11111111 11111000//-7的補碼:11111111 11111111 11111111 11111001int c = a & b;// 6的補碼:00000000 00000000 00000000 00000110//-7的補碼:11111111 11111111 11111111 11111001//a & b補碼::00000000 00000000 00000000 00000000-->0printf("n= %d\n", c);return 0;
}
打印:
n= 0
5.2 |:按位或
有1則1。
#include <stdio.h>
int main()
{int a = 6;// 6的補碼:00000000 00000000 00000000 00000110int b = -7;//-7的原碼:10000000 00000000 00000000 00000111//-7的反碼:11111111 11111111 11111111 11111000//-7的補碼:11111111 11111111 11111111 11111001int c = a | b;// 6的補碼:00000000 00000000 00000000 00000110//-7的補碼:11111111 11111111 11111111 11111001//a | b補碼:11111111 11111111 11111111 11111111//a | b反碼:10000000 00000000 00000000 00000000//a | b原碼:10000000 00000000 00000000 00000001-->-1printf("n= %d\n", c);return 0;
}
打印:
n= -1
5.3 ^:按位異或
相同為0,不同為1。
#include <stdio.h>
int main()
{int a = 6;// 6的補碼:00000000 00000000 00000000 00000110int b = -7;//-7的原碼:10000000 00000000 00000000 00000111//-7的反碼:11111111 11111111 11111111 11111000//-7的補碼:11111111 11111111 11111111 11111001int c = a ^ b;// 6的補碼:00000000 00000000 00000000 00000110//-7的補碼:11111111 11111111 11111111 11111001//a ^ b補碼:11111111 11111111 11111111 11111111//a ^ b反碼:10000000 00000000 00000000 00000000//a ^ b原碼:10000000 00000000 00000000 00000001-->-1printf("n= %d\n", c);return 0;
}
打印:
n= -1
5.4 ~:按位取反
按2進制位取反
#include <stdio.h>
int main()
{int a = 0;//0的原碼:00000000 00000000 00000000 00000000//~a補碼: 11111111 11111111 11111111 11111111//~a反碼: 10000000 00000000 00000000 00000000 //~a原碼: 10000000 00000000 00000000 00000001-->-1printf("n= %d\n", ~a);return 0;
}
打印:
n= -1
5.5 例題
例題1
不創建臨時變量(第三個變量),實現兩個整數的交換。
方法1:
int main() {int a = 3;int b = 5;printf("交換前:a=%d b=%d\n", a, b);a = a + b;b = a - b;a = a - b;printf("交換后:a=%d b=%d\n", a, b);return 0;
}
打印:
交換前:a=3 b=5
交換后:a=5 b=3
上面這個方法有點問題,如果a+b的和超過了一個整型數據的存儲大小,那么就計算不了了。
方法2:
int main() {int a = 3;int b = 5;printf("交換前:a=%d b=%d\n", a, b);a = a ^ b;b = a ^ b;a = a ^ b;printf("交換后:a=%d b=%d\n", a, b);return 0;
}
打印:
交換前:a=3 b=5
交換后:a=5 b=3
為什么呢?
因為異或操作符,相同兩個數異或為0。
3^3=0
a^a=0
0異或任何數都為數的本身 。
0^3=3
0^a=a
異或是支持交換率的。
例題2
編寫代碼實現:求一個整數存儲在內存中的二進制中1的個數。
int count_bit_onr(unsigned int n) {int count = 0;while (n) {if ((n % 2) == 1) {count++;}n = n / 2;}return count;
}int main() {int num = 0;scanf("%d", &num);int ret = count_bit_onr(num);printf("%d\n", ret);return 0;
}
輸入:
15
打印:
4
也可以這么寫:
int count_bit_onr(int n) {int i = 0;int count = 0;for (i = 0; i < 32; i++) {if ((n >> 1) & 1 == 1) {count++;}}return count;
}int main() {int num = 0;scanf("%d", &num);int ret = count_bit_onr(num);printf("%d\n", ret);return 0;
}
輸入-1會打印32
這個算法的原理:
-1補碼:11111111 11111111 11111111 11111111
1補碼: 00000000 00000000 00000000 00000001
-1&1= 00000000 00000000 00000000 00000001
不論-1的補碼前面多少個1,只要和1按位與后就只看最后一位是不是1。
然后運用移位操作符,將每一位都和1按位與,統計出一共多少個1。
其實還有更加巧妙地算法:
n=n&(n-1)
例如:n=11
n = 1011
n-1 = 1010
n&(n-1) = 1010
因為n=n&(n-1),所以現在新的n是1010
n = 1010
n-1 = 1001
n&(n-1) = 1000
因為n=n&(n-1),所以現在新的n是1000
n = 1000
n-1 = 0111
n&(n-1) = 0000
從這三次變化里面我們可以看到,我們執行了一次n=n&(n-1),那么n最右面那個1就會消失。把所有的1都去掉后就變成0了。
例題3
寫一個代碼,判斷n是否為2的次方數。
首先我們先看看2的次方數:
000010
000100
001000
010000
100000
可以看出2的次方數里面只有1個1。
if (n & (n - 1) == 0) {printf("yes")
}
例題4
編寫代碼,將13二進制序列的第五位修改為1,然后改回0。
第5位改成1,第5位就和1或|
,其他位都是0。
第5位改為0,第5位就和0與&
,其他位都是1。
int main() {int a = 13;//13原碼:00000000 00000000 00000000 00001101//13補碼:00000000 00000000 00000000 00001101//16補碼:00000000 00000000 00000000 00010000//16|13:00000000 00000000 00000000 00011101-->29int n = 5;a = a | (1 << (n - 1));printf("%d\n", a);a &= ~(a << (n - 1));printf("%d\n", a);return 0;
}
打印:
29
13