目录
1. 操作符的分类
- 算术操作符:+、-、*、/、%
- 移位操作符:<< 、>>
- 位操作符:&、 | 、^
- 赋值操作符:= 、+=、-=、*=、/=、%=、<<=、>>=、&=、|=、^=
- 单目操作符:!、++、--、&、*、+、-、~、sizeof、(类型)
- 关系操作符:>、>=、<、<=、==、!=
- 逻辑操作符:&&、||
- 条件操作符:?、:
- 逗号表达式:,
- 下标引用:[ ]
- 函数调用:()
- 结构成员访问: . 、->
上述操作符中有一些与二进制有关系。
2. 移位操作符
<< 左移操作符
>> 右移操作符
注意:移位操作符的操作数只能是整数。
(移动的是二进制位的补码)
2.1 左移操作符
移位规则:左边抛弃、右边补0
#include<stdio.h> int main() { int num = 10; int n = num << 1; printf("num = %d\n", n); return 0; }
左移操作符演示:
2.2 右移操作符
移位规则:
右移运算分为两种:
- 逻辑右移:左边用0填充,右边丢弃。
- 算术右移:左边用原改值得符号位填充,右边丢弃。
注意:1、2两种用哪一个取决于编译器,但大部分是算术右移
#include<stdio.h> int main() { int num = 10; int n = num >> 1; printf("num = %d\n", n); return 0; }
逻辑右移1位演示:
算术右移1为演示:
警告:对于移位运算符,不要移动负数位,这个是标准未定义的。
例如:
int num = 10;
num >> -1;
3. 位操作符
位操作符有:
1、& 按位与 (两个都为1才为1,有一个0则为0)
2、| 按位或 (有1则为1,两个都为0才为0)
3、^ 按位异或 (相同为0,相异为1)
4、~ 按位取反 (0变1,1变0,包括符号位)(单目操作符)
注意:他们的操作数必须是整数,作用的对象都是二进制补码。
代码:
#include<stdio.h>
int main()
{
int num1 = -3;
int num2 = 5;
printf("num1 & num2 = %d\n", num1 & num2);
printf("num1 | num2 = %d\n", num1 | num2);
printf("num1 ^ num2 = %d\n", num1 ^ num2);
printf("~num1 = %d\n", ~ num1);
printf("~num2 = %d\n", ~ num2);
return 0;
}
练习1
题目:编写代码求一个整数存储在内存中的二进制中1的个数。
#include<stdio.h>
int main()
{
int n = 0;
scanf("%d", &n);
int count = 0;
for (int i = 0; i < 32; i++)
{
if (n & (1 << i))
count++;
}
printf("%d\n", count);
return 0;
}
优化后的代码:
#include<stdio.h>
int main()
{
int n = 0;
scanf("%d", &n);
int count = 0;
while (n)
{
count++;
n = n & (n - 1);
}
printf("%d\n", count);
return 0;
}
n = n & (n - 1);
这行代码的作用是将n的二进制表示中最右边的1变为0。这是一种常见的技巧,可以用来统计二进制表示中1的个数。
练习2
题目:将13二进制序列的第5位修改为1,然后再改为0。
- 13的2进制序列: 00000000000000000000000000001101
- 将第5位置为1后:00000000000000000000000000011101
- 将第5位再置为0:00000000000000000000000000001101
代码:
#include<stdio.h>
int main()
{
int a = 13;
a = a | (1 << 4);
printf("%d\n", a);
a = a ^ (1 << 4);
printf("%d\n", a);
return 0;
}
练习3
题目:不能创建临时变量(第三个变量),实现两个数的交换。
代码:
#include<stdio.h>
int main()
{
int a = 10;
int b = 20;
a = a ^ b;
b = a ^ b;//b = (a^b)^b
a = a ^ b;//(a^b)^a
printf("a = %d b = %d\n", a, b);
return 0;
}
a ^ a = 0
a ^0 = a
4. 单目操作符
单目操作符(只需要一个操作数来执行特定的操作)
有这些:
!、++、--、&、~、*、+、-、sizeof、(类型)
其中注意(类型)表示类型转换操作符:可以将一个变量或表达式的类型转换为另一种类型。
当需要将一个变量或表达式的类型转换为另一种类型时,可以使用类型转换操作符。
例子:
#include <stdio.h>
int main() {
int num1 = 10;
int num2 = 3;
float result;
// 将整数除法的结果转换为浮点数
result = (float) num1 / num2;
printf("Result: %.2f\n", result);
return 0;
}
5. 逗号表达式
exp1,exp2,exp3,...expn
- 逗号表达式,就是用逗号隔开的多个表达式
- 逗号表达式,从左向右依次执行。
- 整个表达式的结果是最后一个表达式的结果。
//代码1
int a = 1;
int b = 2;
int c = (a>b, a=b+10, a, b=a+1);//逗号表达式
c是13
//代码2
if (a =b + 1, c=a / 2, d > 0)
//代码3
a = get_val();
count_val(a);
while (a > 0)
{
//业务处理
a = get_val();
count_val(a);
}
如果使⽤逗号表达式,改写:
while (a = get_val(), count_val(a), a>0)
{
//业务处理
}
6. 下标引用操作符
操作数:一个数组名 + 一个索引值
int arr[10];//创建数组
arr[9] = 10;//实⽤下标引⽤操作符。
[ ]的两个操作数是arr和9。
7. 函数调用操作符
接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。
#include <stdio.h>
void test1()
{
printf("hehe\n");
}
void test2(const char *str)
{
printf("%s\n", str);
}
int main()
{
test1(); //这⾥的()就是作为函数调⽤操作符。
test2("hello bit.");//这⾥的()就是函数调⽤操作符。
return 0;
}
8. 结构成员访问操作符
8.1结构体成员的直接访问
结构体成员的直接访问是通过点操作符 . 访问的。
点操作符接受两个操作数。
如下所示:
#include <stdio.h>
struct Point
{
int x;
int y;
}p = {1,2};
int main()
{
printf("x: %d y: %d\n", p.x, p.y);
return 0;
}
使用方式:结构体变量.成员名
8.2结构体成员的间接访问
有时候我们得到的不是一个结构体变量,而是得到了一个指向结构体的指针。
如下所示:
#include <stdio.h>
struct Point
{
int x;
int y;
};
int main()
{
struct Point p = {3, 4};
struct Point *ptr = &p;
ptr->x = 10;
ptr->y = 20;
printf("x = %d y = %d\n", ptr->x, ptr->y);
return 0;
}
使用方式:结构体指针 -> 成员名
综合举例:
#include <stdio.h>
#include <string.h>
struct Stu
{
char name[15];//名字
int age; //年龄
};
void print_stu(struct Stu s)
{
printf("%s %d\n", s.name, s.age);
}
void set_stu(struct Stu* ps)
{
strcpy(ps->name, "李四");
ps->age = 28;
}
int main()
{
struct Stu s = { "张三", 20 };
print_stu(s);
set_stu(&s);
print_stu(s);
return 0;
}
9. 操作符的属性:优先级、结合性
C语⾔的操作符有2个重要的属性:优先级、结合性,这两个属性决定了表达式求值的计算顺序。
9.1 优先级
优先级指的是,如果⼀个表达式包含多个运算符,哪个运算符应该优先执行。各种运算符的优先级是不⼀样的。3 + 4 * 5;
表达式 3 + 4 * 5 中 既有加法运算符( + ),又有乘法运算符( * )。由于乘法的优先级高于加法,所以会先计算 4 * 5 ,而不是先计算 3 + 4
9.2 结合性
如果两个运算符优先级相同,优先级没办法确定先计算哪个了,这时候就看结合性了,则根据运算符是左结合,还是右结合,决定执行顺序。大部分运算符是左结合(从左到右执行),少数运算符是右 结合(从右到左执行),比如赋值运算符( = )。5 * 6 / 2;
上面示例中, * 和 / 的优先级相同,它们都是左结合运算符,所以从左到右执行,先计算 5 * 6 , 再计算 6 / 2
运算符的优先级顺序很多,下面是部分运算符的优先级顺序(按照优先级从高到低排列)
• 圆括号( () )• 自增运算符( ++ ),自减运算符( -- )• 单目运算符( + 和 - )• 乘法( * ),除法( / )• 加法( + ),减法( - )• 关系运算符( < 、 > 等)• 赋值运算符( = )由于圆括号的优先级最高,可以使用它改变其他运算符的优先级。
下面是所有操作符的优先级表
完