c语言运算符
按位或
按位或操作使用 | 符号表示。对于两个二进制数,如果对应位上至少有一个是1,则结果的那个位也是1;如果两个对应位都是0,则结果的那个位是0。
0101 (5 in decimal)
|
1010 (10 in decimal)
------
1111 (15 in decimal)
unsigned char a = 5;
unsigned char b = 10;
unsigned char c = a | b; // c 现在是 15
按位与
按位与操作使用 &
符号表示。对于两个二进制数,如果对应位上两个都是1,则结果的那个位也是1;如果至少有一个是0,则结果的那个位是0。
0101 (5 in decimal)
&
1010 (10 in decimal)
------
0000 (0 in decimal)
unsigned char a = 5;
unsigned char b = 10;
unsigned char c = a & b; // c 现在是 0
按位取反
按位取反操作使用 ~
符号表示。它对单个二进制数进行操作,将其每一位都反转(0变1,1变0)。
~
0101 (5 in decimal)
------
1010 (complement of 5, which is -6 in 2's complement representation)
unsigned char a = 5;
unsigned char b = ~a; // b 现在是 250 (对于8位无符号字符,因为255 - 5 = 250)
左移操作符
在C语言中,左移操作符(<<
)用于将一个整数的所有位向左移动指定的位数。左移操作实际上是将该数乘以2的指定次幂,因为向左移动一位等价于乘以2,向左移动两位等价于乘以4,依此类推。
当我们将一个数左移时,我们实际上是在丢弃该数的最右边的位(即最低位),并在最左边(即最高位)填充0。如果原始数是带符号的(如int
或char
,并且未明确指定为无符号),并且左移后最高位(符号位)变为1,则结果可能会是一个负数(在二进制补码表示法中)
#include <stdio.h>
int main() {
unsigned char a = 4; // 二进制表示为 0100
unsigned char b;
// 将a左移一位
b = a << 1; // 现在b是 8,二进制表示为 1000
printf("a (original): %u\n", a); // 输出: a (original): 4
printf("b (after left shift): %u\n", b); // 输出: b (after left shift): 8
return 0;
}
在这个例子中,a
的初始值是4,其二进制表示为0100
。当我们将a
左移一位时,得到的结果是8,其二进制表示为1000
。
需要注意的是,对于无符号整数类型(如unsigned char
、unsigned int
等),左移操作总是安全的,因为最高位填充的是0,不会改变数的符号。但是,对于带符号整数类型(如char
、short
、int
、long
等),如果左移操作导致符号位变为1,则结果可能是负数。此外,如果左移的位数超过了该整数类型的位数,那么结果将是未定义的(对于标准C语言)。
按位异或
^
是一个位运算符,被称为“按位异或
- 当两个相应的二进制位相异时,结果为1
- 当两个相应的二进制位相同时,结果为0
a = 0b10101010 (二进制表示,等于十进制的170)
b = 0b01010101 (二进制表示,等于十进制的85)
a^b结果:
10101010
^ 01010101
----------
11111111 (二进制表示,等于十进制的255)
#include <stdio.h>
int main() {
unsigned char a = 170; // 二进制为 10101010
unsigned char b = 85; // 二进制为 01010101
unsigned char result = a ^ b;
printf("Result of XOR operation: %u\n", result); // 输出应为 255
return 0;
}
例子
需求:
二进制位控制LED灯开关。假定有8盏LED灯的开关由状态字psw控制。Psw的8个二进制位分别控制1个LED灯,将位清0则对应的灯关闭,置1则打开。编一个程序功能是模拟LED灯光跑马灯闪烁效果。即第1盏灯亮1秒后关闭,切换到第2盏灯再亮1秒,一直循环下去。设置按任意键控制跑马灯效果的启停,输出psw的十六进制数形式来模拟循环一个完整周期的灯光控制效果。
提示:通过观察状态字psw值的变化来模拟控制效果。其设计基本思路是:
① 使用getch(函数控制启停,延时调用库函数leep(1000),参数为毫秒数,其头文件在windows.h中。
② 状态字psw为1字节无符号字符(unsigned char),从第0位到第7位共8个二进制位分别控制8个LED灯。每位单独是1其他位是0时的十六进制值依次是:0x1(0位)、0x2(1位)、0x4(2位)、0x8(3位)、0x10(4位)、0x20(5位)、0x40(6位)、0x80(7位)
③ 灯全灭psw-0,开关掩码mask依据上列十六进制值来设定。
④ 由于异或运算同一个数2次,又恢复了原来的值,也就是psw^=mask,灯开,再次做同样运算灯灭。也可以考虑用位左移运算实现开关控制。psw=1,打开0位对应的灯,psw<=1每次左移1位可切换开关的状态。
#include <stdio.h>
#include <windows.h> // Sleep()
#include <conio.h> // _kbhit() 和 _getch()
int main() {
unsigned char psw = 0x00; // 初始状态,所有灯关闭
unsigned char mask = 0x01; // 掩码,用于控制每一位的LED
int running = 1; // 控制跑马灯是否运行
while (running) {
// 模拟每个LED灯打开并关闭
for (int i = 0; i < 8; ++i) {
// 打开当前LED灯
psw |= mask; // 使用按位或运算设置当前位
printf("psw: 0x%X\n", psw); // 输出当前psw的值
Sleep(1000); // 等待1秒
// 关闭当前LED灯
psw &= ~mask; // 使用按位取反和按位与运算清除当前位
//也可以用按位异或运算符 psw=psw^mask;
printf("psw: 0x%X\n", psw); // 输出当前psw的值
Sleep(1000); // 等待1秒
// 将掩码左移一位,以控制下一个LED灯
mask <<= 1;
// 如果掩码超过了一个字节的界限,重置它回到最低位
if (mask == 0) {
mask = 0x01;
}
// 检查是否有按键按下以停止跑马灯
if (_kbhit()) {
int ch = _getch();
if (ch != '\0' && ch != EOF) {
running = 0; // 如果有任意键被按下,停止跑马灯
}
}
}
}
printf("跑马灯已停止。\n");
return 0;
}