1、操作符
1. 算术操作符
+ - * / %
除了%操作符之外,其它的几个操作符可以作用于整数和浮点数
对于/操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法
%操作符的两个操作数必须为整数,返回值为整除之后的余数
2. 移位操作符
移动的是二进制,数据在内存中以补码的形式进行保存。
正整数的原码、反码和补码相同。
负整数的原码、反码和补码如下 :(先绝对,再减一, 然后取反)
<< 左移操作符
移位规则:左边抛弃,右边补0 ,左移操作相当于*2
>> 右移操作符
右移运算分两种:
1>逻辑移位:右边丢弃,左边用0填充
2>算术移位:右边丢弃,左边补符号位
3. 位操作符
& 按位与
| 按位或
^ 按位异或
Tips:操作数必须是整数
#include <stdio.h>
int main() {
int num1 = 3;
int num2 = 5;
int result1 = num1 & num2;
printf("num1 & num2 = %d\n",result1);
int result2 = num1 | num2;
printf("num1 | num2 = %d\n", result2);
int result3 = num1 ^ num2;
printf("num1 ^ num2 = %d\n", result3);
}
3的二进制:00000000 00000000 00000000 00000011
5的二进制:00000000 00000000 00000000 00000101
按位与 &:
按位或 |
按位异或
不能创建临时变量(第三个变量),实现两个数的交换。
#include <stdio.h>
int main() {
int a = 10;
int b = 3;
printf("交换之前:a=%d b=%d\n", a, b);
/*a = a - b;
b = a + b;
a = b - a;*/
// 使用^操作符
a = a ^ b;
b = a ^ b;
a = a ^ b;
printf("交换之后:a=%d b=%d", a, b);
}
练习:
编写代码实现:求一个整数存储在内存中的二进制1的个数。
// 方式1:利用10进制转二进制的方式
#include <stdio.h>
int main() {
int num = 10;
int count = 0;
while (num) {
count += num % 2;
num = num / 2;
}
printf("%d二进制中1的个数=%d\n", num, count);
}
// 方式2:利用&运算符来实现(右移与循环)
#include <stdio.h>
int main() {
int num = -1;
int source = num;
int count = 0;
for (int i = 0; i < 32;i++) {
if ((num >> i) & 1 == 1) {
count++;
}
}
printf("%d二进制中1的个数=%d\n", source, count);
}
//方法3:减一循环
#include <stdio.h>
int main() {
int num = -1;
int source = num;
int count = 0;
while (num) {
count++;
num = num & (num - 1);
}
printf("%d二进制中1的个数=%d\n", source, count);
}
4. 赋值操作符
赋值操作符可以让你对自己声明的变量重新赋值。
int weight = 80; // 体重
weight=58;
double salary = 10000;// 薪水
salary=25000; // 使用赋值操作符重新赋值
注意:赋值操作符可以连续使用,比如:
int x = 10;
int y = 20;
int z = 30;
x = y = z + 1; // 连续赋值
但是,不建议连续使用,这样代码的可读性比较差,应该这样写:
y=z+1;
x=y;
复合赋值运算符:
+=,-=,*=,/=,%=,>>=,<<=,&=,|=,^=
5. 单目操作符
就是在使用时只有一个操作数。
6. 关系操作符
<,>,<=,>=,==(等于),!=(不相等)
这些关系运算符比较简单,但是一定要注意相等的写法(==),不要写成=.
7. 逻辑操作符
&& 逻辑与
|| 逻辑或
区分逻辑与(或)和按位与(或)
8. 条件操作符(三目操作符)
exp1?exp2:exp3
f(a>5){
b=3
}else {
b=-3;
}
// 转换成条件表达式,应该如何写?
b=a>5?3:-3;
9. 逗号表达式
exp1,exp2,exp3,.....,expN
逗号表达式,就是用逗号隔开的多个表达式。逗号表达式,从左向右依次执行,整个表达式的结果是最后一个表达式的结果。
10. 下标引用、函数调用和结构成员
(1)下标引用操作符
int arr[10]={0};
arr[9]=20; // 数组名+下标通过下标引用操作符来操作数组成员
(2)函数调用操作符
通过函数名+参数的形式来调用函数。
int get_max(int a, int b) {
return a > b ? a : b;
}
// 函数调用操作符
int max = get_max(a, b);
(3)访问一个结构的成员
. 结构体变量.成员名
-> 结构体指针->成员名
// 创建一个结构体类型----struct Stu
struct Stu {
char name[20];
int age;
char id[20];
};
int main(){
// 使用struct Stu这个类型创建一个学生变量stu,并初始化
struct Stu stu = {"chatGpt",1,"c1001"};
// 访问结构体成员,通过.操作符
printf("%s\n",stu.name);
printf("%d\n",stu.age);
printf("%s\n",stu.id);
struct Stu* p = &stu;
// 访问结构体成员,通过->操作符
printf("%s\n",p->name);
printf("%d\n",p->age);
printf("%s\n",p->id);
return 0;
}
2、表达式求值
(1)隐式类型转换
C的整型算术运算总是至少以缺省整型类型的精度来进行的。为了获得这个精度,表达式中的字符和short操作数在使用之前被转换为普通整型,这种转换称为整型提升。
(2)算术转换
如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数转换为另一个操作数的类型,否则操作就无法进行。下面的层次体系称为寻常算术转换。
(3)操作符的属性
复杂表达式的求值有三个影响的因素。
1. 操作符的优先级
2. 操作符的结合性
3. 是否控制求值顺序。
两个相邻的操作符先执行哪个?
取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。
下面,我们来看看操作符的优先级,这是一张表: