目录

1. 操作符的分类

2.移位操作符

2.1左移操作符

2.2右移操作符

3.位操作符

练习1

练习2

练习3

4. 单目操作符

5. 逗号表达式

6. 下标引用操作符

7. 函数调用操作符

8. 结构成员访问操作符

8.1结构体成员的直接访问

8.2结构体成员的间接访问

9.操作符的属性:优先级、结合性

9.1 优先级

9.2 结合性


1. 操作符的分类

  • 算术操作符:+、-、*、/、%
  • 移位操作符:<>
  • 位操作符:&、| 、^
  • 赋值操作符:= 、+=、-=、*=、/=、%=、<>=、&=、|=、^=
  • 单目操作符:!、++、–、&、*、+、-、~、sizeof、(类型)
  • 关系操作符:>、>=、<、<=、==、!=
  • 逻辑操作符:&&、||
  • 条件操作符:?、:
  • 逗号表达式:,
  • 下标引用:[ ]
  • 函数调用:()
  • 结构成员访问: . 、->

上述操作符中有一些与二进制有关系。

2.移位操作符

<<左移操作符

>>右移操作符

注意:移位操作符的操作数只能是整数。

(移动的是二进制位的补码)

2.1左移操作符

移位规则:左边抛弃、右边补0

#includeint main(){int num = 10;int n = num << 1;printf("num = %d\n", n);return 0;}

左移操作符演示:

2.2右移操作符

移位规则:

右移运算分为两种:

  1. 逻辑右移:左边用0填充,右边丢弃。
  2. 算术右移:左边用原改值得符号位填充,右边丢弃。

注意:1、2两种用哪一个取决于编译器,但大部分是算术右移

#includeint 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,包括符号位)(单目操作符)

注意:他们的操作数必须是整数,作用的对象都是二进制补码。

代码:

#includeint 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的个数。

#includeint 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;}

优化后的代码:

#includeint 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。

  1. 132进制序列: 00000000000000000000000000001101
  2. 将第5位置为1后:00000000000000000000000000011101
  3. 将第5位再置为000000000000000000000000000001101

代码:

#includeint main(){int a = 13;a = a | (1 << 4);printf("%d\n", a);a = a ^ (1 << 4);printf("%d\n", a);return 0;}

练习3

题目:不能创建临时变量(第三个变量),实现两个数的交换。

代码:

#includeint main(){int a = 10;int b = 20;a = a ^ b;b = a ^ b;//b = (a^b)^ba = a ^ b;//(a^b)^aprintf("a = %db = %d\n", a, b);return 0;}

a ^ a = 0

a ^0 = a


4. 单目操作符

单目操作符(只需要一个操作数来执行特定的操作)

有这些:

!、++、–、&、~、*、+、-、sizeof、(类型)

其中注意(类型)表示类型转换操作符:可以将一个变量或表达式的类型转换为另一种类型。

当需要将一个变量或表达式的类型转换为另一种类型时,可以使用类型转换操作符。

例子:

#include 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

  • 逗号表达式,就是用逗号隔开的多个表达式
  • 逗号表达式,从左向右依次执行。
  • 整个表达式的结果是最后一个表达式的结果
//代码1int a = 1;int b = 2;int c = (a>b, a=b+10, a, b=a+1);//逗号表达式c是13//代码2if (a =b + 1, c=a / 2, d > 0)//代码3a = 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 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 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 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 #include 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

运算符的优先级顺序很多,下面是部分运算符的优先级顺序(按照优先级从高到低排列

圆括号( () 自增运算符( ++ ),自减运算符( 单目运算符( + 乘法( * ),除法( / 加法( + ),减法( 关系运算符( < > 等) 赋值运算符( = 由于圆括号的优先级最高,可以使用它改变其他运算符的优先级。


下面是所有操作符的优先级表