目录
运算符
移位操作符
补充原反补码相关知识:
正数的原、反、补码相同。(为了方便做减法)
无符号数没有原、反、补码。
补码 = 原码取反(反码) +1
反码:除符号位其他位取反
原码 = 补码取反 + 1 or 补码-1再取反
左移操作符
这里为了省功夫,我们用字符类型来演示:
char a = 4;
00000100//原码
01111100//补码
printf("%d",a<<1);//01111000补码
//打印:00001000 8//原码
左移本身并没有改变a的值。打印结果显示的是原码的值,而实际各种运算都是对补码进行操作。
右移操作符
一样的道理,右移操作符使值减少到了原来的一半,而左移是扩大2倍,原理参考2进制转换成10进制。
注意:不要移动负数位,这样的结果是未定义的。(a>>-1)
位操作符
按位与&
3 & 5//char
//原码
00000011
00000101
//补码
01111101
01111011
01111001 //3&5
//原码输出:00000111//7
按位或|
按位异或^
异或交换数字
常规做法
int a = 3;
int b = 5;
a = b + a - a;
b = b + a - b;
这种做法可能出现数字大的时候导致溢出,我们可以改进一下:
// 5^5 = 0
// 0^5 = 5
//3^5^5 = 3^0 = 3;
int a = 3;
int b = 5;
a = a ^ b;
b = a ^ b;
a = b ^ a;
计算二进制中1的个数
00000001//1
00010010 >>
00000011 >>
按位与在遇到1结果为1,遇到0时结果为0,这样能区分0和1,异或也是同理。
int n = 8;
unsigned int sz = sizeof(n);//非负整数
int bit_num = sz * 8;//计算比特位
int sum = 0;
int i = 0;
for (i = 0; i < bit_num; i++)
{
if (1 & (n>>i))
sum++;
}
printf("%d\n", sum);
// 01000
// 00001
结果输出1,记住移位并不会直接改变数字大小,所以得在判断时调整。
关系操作符
注意:
关系操作符不能比较两个结构体大小,但可以比较其成员变量的大小。
不能比较两个字符串大小,因为比较的是两个字符串首元素地址。
逻辑操作符
真返回1,假返回0
条件操作符
逗号表达式
int a = 2;
int b = 0;
int c = (a > b, a = b + 10, a, b = a + 1);
printf("%d %d %d\n", a,b,c);
这个表达式的值为多少呢?
答案是:10 11 11,大家可以自己算算。
a = get_val();
count_val(a);
while (a > 0)
{
a = get_val();
count_val(a);
}
这段代码重复的部分除了可以用do...while循环改写还可以用逗号表达式改写:
while (a = get_val(), count_val(a), a > 0)
{
//业务处理
}
将需要先处理的代码用逗号表达式放到while()里,是一种很巧妙的做法。
下标引用、函数调用和结构成员
下标引用:[]
函数调用:
void test1(int x, int y)
{}
void test2()
{}
int main()
{
test1(3, 4);//操作数(),3,4
test2();//操作数()
结构成员
struct book
{
char name[10];
int price;
};
int main()
{
struct book s1 = { "wei",30 };
struct book* s2 = &s1;//不能为空
printf("%s %d\n", s1.name, s1.price);
printf("%s %d", s2->name, s2->price);
隐式类型转换
char a = 5; //00000101
char b = 126;//01111110
char c = a + b;
//整型提升
00000000000000000000000000000101
00000000000000000000000001111110
00000000000000000000000010000011
当把计算的结果存到c中去时,会发生截断,因为char只能存8个字节。
//截断
10000011
输出时发生整型提升:
printf("%d\n", c);//以整形打印10进制
//11111111111111111111111110000011(补码)
//打印(原码):10000000000000000000000001111101
//-125
注:提升时有符号类型补符号位,无符号类型补0。
截断和提升都是对补码操作。
整形提升实例:
char a = 0xb6;
short b = 0xb600;
int c = 0xb6000000;
if(a==0xb6)
printf("a");
if(b==0xb600)
printf("b");
if(c==0xb6000000)
printf("c");
a:1011 0110->11111111111111111111111110110110
当用sizeof去对小于4字节的变量进行表达式计算时,自己也会为4字节。
算术转换
如果某个操作符的各个操作数属于不同类型,就会发生算数转换,算数转换按照如下优先级顺序转换:
当然,如果转换顺序不合理,将会出现精度丢失等情况。
float f = 3.14//float = double
int num = f;//隐式转换
操作符属性
大家可以自行查表记忆它们的特点和顺序,不需要死记硬背,用到时即可查阅。
问题表达式
int main()
{
int i = 10;
i = i-- - --i * ( i = -3 ) * i++ + ++i;
printf("i = %d\n", i);
return 0;
}
像这样不知道哪一步该执行,不同编译器的处理也是不同的,所以我们书写代码的时候尽量降低复杂性,提高可读性。