-
算术运算符
+ - * / %
注:不可除0也不可模0
两个整数相除得到的结果为整数。
-
移位操作符
左移<< 右移>>
不可移动负数位
对于CPU来说,计算移位操作,效率远远高于计算除法操作
移位操作是按照二进制进行移位:
左移一位相当于 *2,右移一位相当于 /2
#include <stdio.h>
#include <stdlib.h>
int main(){
int num = 4;
int num1= num << 1;
int num2 = num >> 1;
printf("num1=%d\n", num1);
printf("num2=%d\n", num2);
system("pause");
return 0;
}
左移移位规则:
左边丢弃,右边补零
右移移位规则:
1.逻辑右移
左边补零,右边丢弃
2.算术右移
数字符号位填充左边,右边丢弃
-
位操作符
& | ^
& 按位与:
全1为1,有0则0
注:逻辑与为&&
按照二进制进行按位与:
#include <stdio.h>
#include <stdlib.h>
int main(){
int num1= 2; //10
int num2 = 3; //11
//按位与后得到 10,即2
printf("%d & %d=%d\n", num1, num2,num1&num2);
system("pause");
return 0;
}
通过按位与操作,把数字中的某些位截取出来:
#include <stdio.h>
#include <stdlib.h>
int main(){
int num1= 33; //0010 0001
int num2 = 0xf; //按二进制表示为0000 1111
int num3 = 0xf0; //1111 0000
printf("%d 截取二进制最后一位的结果为:%d\n", num1, num1&num2);
printf("%d 截取二进制前两位的结果转换为十进制为:%d\n", num1, num1&num3);
system("pause");
return 0;
}
| 按位或:
全0为0,有1则1
注:逻辑或为||
按照二进制进行按位或:
#include <stdio.h>
#include <stdlib.h>
int main(){
int num1= 4; //100
int num2 = 2; //010
//按位或后得到110,即6
printf("%d | %d = %d\n", num1, num2,num1|num2);
system("pause");
return 0;
}
^ 按位异或:
同0异1
利用移位操作与位操作求取二进制数中1的个数:
#include <stdio.h>
#include <stdlib.h>
int main(){
//利用移位操作与位操作求取二进制数中1的个数
int num = 22;
int count=0;
for (int i = 0; i < 32; ++i){
//循环一直测试num中的每一位
//若num变量对应位为1,则按位与不为0
if (num&(1 << i)){
++count;
}
}
printf("%d的二进制中有%d个1\n", num, count);
system("pause");
return 0;
}
-
赋值操作符
=
可用于赋值,也可用于变量的初始化
复合赋值符:
+= -= *= /= %= >>= <<= &= |= ^=
-
单目操作符
! 逻辑反操作
- 负值
+ 正值
& 取地址
sizeof 操作数类型长度(字节)
~ 对数的二进制按位取反
-- / ++
* 间接访问操作符(解引用操作符)
(类型) 强制转换
按位取反与逻辑取反:
逻辑取反得到的值是一个表示真假的值,按位取反得到的是二进制每一位的反
#include <stdio.h>
#include <stdlib.h>
int main(){
unsigned int x = 0x0;
int x1 = ~x;
int x2 = !x;
printf("%x按位取反:%x\n", x,x1);
printf("%x逻辑取反:%x\n", x,x2);
system("pause");
return 0;
}
若不取表达式的返回值,++x和x++没有区别:
#include <stdio.h>
#include <stdlib.h>
int main(){
int x = 0;
int x1=++x;
printf("++x后的x为%d\n",x);
x = 0;
int x2=x++;
printf("x++后的x为%d\n", x);
system("pause");
return 0;
}
练习:
#include <stdio.h>
#include <stdlib.h>
int main(){
int i = 1;
int ret = (++i) + (++i) + (++i);
printf("ret=%d\n", ret);
system("pause");
return 0;
}
注:不同的系统 / 编译器的到的结果可能不同,因为C语言标准中没有规定这种表达式的求值顺序,这是未定义行为。
-
关系操作符
> >= < <= !=(不相等) ==(相等)
注:== 和 = 的区别
返回值为真或假
短路求值:
对于逻辑与来说,两个操作数任意一个为0,结果必然为0。若发现第一个操作数表达式的值已经为0,则不需要对第二个表达式进行求值。
对于逻辑或来说,两个操作数任意一个非0,结果必然为1。若发现第一个操作数表达式的值已经非0,则不需要对第二个表达式进行求值。
int main(){
int i = 0, a = 0, b = 2, c = 4;
i = a++ && ++b && c++;
//i = a++ || ++b || c++;
printf("a = %d\nb = %d\nc = %d\n\n", a, b, c);
system("pause");
return 0;
}
-
条件操作符
exp1 ? exp2 : exp3
若表达式1为真,则执行表达式2;否则,执行表达式3
-
逗号表达式
exp1, exp2, exp3, …expN
用逗号隔开的多个表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。
-
下标引用
[ ] 下标引用操作符
操作数:数组名 + 索引值
注:下标从0开始
-
函数调用
( ) 函数调用操作符
接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。
-
结构成员
. 结构体.成员名
-> 结构体指针->成员名
. 成员访问操作符,直接给结构体变量使用
->成员访问操作符,给指向一个结构体变量的指针来使用
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Student{
char name[1024];
int age;
};
int main(){
struct Student student;
struct Student*p= &student;
strcpy(student.name, "张三");
student.age = 20;
printf("%s\n", p->name);
printf("%d\n", p->age);
system("pause");
return 0;
}
-
表达式求值
隐式类型转换:
整型提升:
CPU从内存中读取数据的时候,都是以4个字节为单位。只是读取一个字节,反而不如一次读4个字节更高效。如果是short或其他比int短的变量,都会有类似的提升过程。
#include <stdio.h>
#include <stdlib.h>
int main(){
//字符类型的变量本质也是一个数字
//char ->int 相加后再强制转换为char
char a = 1;
char b = 2;
char c = a + b;
printf("c=%d\n", c);
system("pause");
return 0;
}
练习:
#include <stdio.h>
#include <stdlib.h>
int main(){
unsigned char a=255;
//本应为0111 1111
//整型提升为0000 0000 0000 1111 1111
//->0000 0000 0001 1111 1110
//->0000 0000 0000 1111 1111
//所以最终结果为1111 1111
unsigned char b = (a<<1)>>1;
printf("b=%x\n", b);
system("pause");
return 0;
}
算数转换:
如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行。
如果某个操作数的类型在下表中排名较低,那么首先要转换为另外一个操作数的类型后执行运算。
long double |
double |
float |
unsigned long int |
long int |
unsigned int |
int |
当作为函数参数传递时,char和short被转换位int,float转换位double