C語言中的位運算


C語言中的位運算:

位運算,即對數據的二進制形式按位進行運算操作,c++中有多種位運算操作:

由於位運算是直接對內存中二進制數據進行操作,不需要進行轉化,因此效率很高,速度比+-*/等算數運算更快

C語言中 位運算速度 > +-速度 > */速度 > %速度

合理利用位運算操作可以一定程度上提高程序運行速度,從而避免TLE

壹.左移/右移:

<<  二進制左移(SHL)運算符:

將一個運算對象的各二進制位全部左移若干位,右邊補0,超出對應類型范圍時左邊的位自動丟棄。

例:

	printf("%d", 3 << 2);//結果為12

(3)10 = (11)2

*(3)10表示十進制數3,(11)2表示二進制數11

 向左移動兩位后,右邊補0,得到 (1100)2 =  (1*23+1*22+0*21+0*20)10 = 12

不難看出: (1100)2 = 十進制下  22*(1*21+1*20) = 22*(3)

以此類推: 3<<k  = 2k * 3 ;

同理:n<<k = n* 2^k

因此:我們可以借助 1<<k便捷的計算2的k次方

#由於位運算操做是直接針對內存中的二進制編碼進行簡單操做,所以位運算的運算效率要高於加減(+ -)操作,更快於乘除(*  /)操作,

  1<<k,遠遠快於需要進行k此操作的函數: pow(2,k); 可以減少計算耗時。

  在程序時間限制極為緊張時,也可以考慮把一些乘法操作改寫成加法操作與左移操作的組合,從而提高計算效率

  如: x*10 = x<<3+x<<1;

 

>>  二進制右移(SHR)運算符:

二進制右移運算符。將一個數的各二進制位全部右移若干位,正數左補0,負數左補1,右邊丟棄。

例:

	printf("%d", 5 >> 1);//結果為2
	printf("%d", -5 >> 1);//結果為-3

(5)10 = (00000101)2

向右移動並舍棄一位,左邊補0得 (00000010)2 = (2)10

(-5)10 在計算機中以二進制補碼的形式表示為:(11111011)

整體向右移一位,左邊補1得:(11111101) = (-3)10

類比左移運算不難看出: n>>k 相當於 n/2^k 向下取整    (注意:對負數向下取整相當於對其絕對值向上取整)

(不建議對負數進行位運算操作)

貳.按位 與 或 取反 異或 運算:

&  按位與(AND)運算符:

按位與操作,按二進制位進行"與"運算,每一位的運算規則於邏輯 “與” && 類似,只有在參與運算兩數同時為1時結果為1

即:1與1=1,1與0=0,0與1=0,0與0=0

( 二者位數不一致時向右對齊,較小數左端補0,如: 010 & 111 = 010 )

例:

	printf("%d", 7 & 5);//結果為5

(7)10=(111)2      (5)10=(101)2

第一位1&1=1,第二位1&0=0,第三位1&1=1

(111)   &    (101)2    =     (101) =  (5)10

 

#與運算遵循 交換律,結合律,對或運算的分配律

a&b = b&a   a&(b&c) = (a&b)&c    a&(b|c) = (a&b)|(a&c)

#不難看出,一切正數奇數的二進制最低位一定是1,偶數最低位一定是0

  所以可以通過n&1判斷n的奇偶性,結果為1則為奇,為0則為偶

  因為位運算比取余運算更高效,所以該表達式比n%2耗時更少

#利用&將數的二進制最低非0位變為0

	int a = 6;
	printf("%d\n", a & a - 1);

6=(110)6-1=(101)6&5 =(100)2=4

#利用&求數a的二進制中有多少個一 時間復雜度O(k) k為a中1的數量

	int a = 6, k = 0;
	while (a)
	{
		a = a & a - 1;
		k++;
	}
	printf("%d\n", k);

每次循環都會將a的最低為1位改為0,並且k++,當所有位全為0時循環結束,可算出a的二進制表示中有多少個1

|  按位或(OR)運算符:

按位或運算符,按二進制位進行"或"運算,每一位的運算規則於邏輯 “或” || 類似,只有在參與運算兩數同時為0時結果為0

即:0或0=0,1或0=1,0或1=1,1或1=1

( 二者位數不一致時向右對齊,較小數左端補0,如: 010 | 111 = 111 )

例:

	printf("%d", 6 | 9);//結果為15

(6)10=(110)2=(0110)2    (9)10=(1001)2

第一位1|0=1,第二位0|1=1,第三位1|0=1,第四位0|1=1

(110)2 | (1001)2 = (1111)2 = (15)10

#或運算遵循 交換律,結合律,對與運算的分配律

a|b = b|a   a|(b|c) = (a|b)|c    a|(b&c) = (a|b)&(a|c)

~  按位取反(NOT)運算符:

將二進制位取反,0變為1,1變為0 (對於原碼補碼的符號位同樣生效)

	printf("%d", ~3);//結果為-4

(3)10 = (00000011)2 按位取反得到 11111100 是二進制補碼形式 轉化為十進制得 -4

# ~x+1 = -1*x (利用補碼的性質)

^  按位異或(XOR)運算符:

異或運算符,按二進制位進行"異或"運算。運算規則:當參與運算兩數相同時結果為0,不相同時結果為1

即:0^0 = 0 , 1^1 = 0, 0^1 = 1, 1^0 = 1.

( 二者位數不一致時向右對齊,較小數左端補0,如: 010 ^ 111 = 101 )

例:

	printf("%d", 2 ^ 3);//結果為1

(2)10=(10)2 , (3)10=(11)2

第一位1^1=0,第二位1^0=1

(10)2 ^ (11)2 = (01)2 = (1)10

#異或遵循:

1、交換律: a^b=b^a

2、結合律: (a^b)^c = a^(b^c)

3、對於任何數x,都有x^x=0,x^0=x

4、自反性 a^b^b = a^0=a

#利用異或交換兩數(高效率)

  a ^= b;
  b ^= a;
  a ^= b;

第一步:a ^= b ---> a = (a^b)

第二步:b ^= a ---> b = b^(a^b) ---> b = (b^b)^a = a

第三步:a ^= b ---> a = (a^b)^a = (a^a)^b = b

叄.位運算賦值運算符:

類似於 +=,-=操做,可以將位運算符於等號組合,形成計算並賦值的位運算賦值運算符

<<= 左移且賦值運算符 C <<= n 等同於 C = C << n
>>= 右移且賦值運算符 C >>= n 等同於 C = C >> n
&= 按位與且賦值運算符 C &= n 等同於 C = C & n
^= 按位異或且賦值運算符 C ^= n 等同於 C = C ^ n
|= 按位或且賦值運算符 C |= n 等同於 C = C | n

肆.運算符優先級:

1.括號 ()
2.按位取反 ~
3.乘除取余 *  /  %
4.加減 + - 
5.左右移 << >> 
6.關系運算 < <= > >= 
7.相等 ==  != 
8.按位與
9.按位異或
10.按位或
11.邏輯與 && 
12.邏輯或 || 
13.三目運算符 ?: 
14.賦值  = += -= *= /= %=>>= <<= &= ^= |= 

#可以發現除了~以外,其他位運算符的運算優先級均較低,大致上優先級 算數運算 > 位運算 > 邏輯運算

  因此當出現位運算和算數運算(+-*/)相混合的表達式時,應當多加括號以避免運算順序錯誤

#不建議背誦此表,只需有大致印象即可,當不確定優先級時,可以通過多加括號解決

#   & ^ | 優先級低於<<,因此在c++中可能出現如下錯誤

	cout << 1 ^ 2 << endl;//編譯錯誤

因為 ^ 優先級低於<< ,所以編譯器會嘗試先執行輸出操作,后進行運算,顯然計算機是無法執行的,所以編譯時編譯器會報錯,加上括號即可

	cout << (1 ^ 2) << endl;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM