【C语言】8.C语言操作符详解(1)

文章目录


1.操作符的分类

  1. 算术操作符: +-*/%

  2. 移位操作符: << ,>>

  3. 位操作符: &, |, ^

  4. 赋值操作符: =+=-=*=/=%=<<=>>=&=|=^=

  5. 单目操作符: ++--&*+-~sizeof(类型)

  6. 关系操作符: >>=<<===!=

  7. 逻辑操作符: &&||

  8. 条件操作符: ? :

  9. 逗号表达式: ,

  10. 下标引用: []

  11. 函数调用: ()

  12. 结构成员访问: .->


2.⼆进制和进制转换

进制转换的教程网上比比皆是,而且视频看起来肯定比文字清晰。这里就不过多赘述了。

我们需要掌握一下几个:

  1. 2进制10进制
  2. 10进制2进制
  3. 2进制8进制和16进制
  4. 8进制和16进制2进制
  5. 8进制16进制
  6. 16进制8进制

3.原码、反码、补码

整数的2进制表示方法有三种:

  1. 原码

  2. 反码

  3. 补码

有符号整数的三种表示方法均有符号位和数值位两部分,2进制序列中,最高位的1位是被当做符号位,剩余的都是数值位。

符号位都是用0表示"正",用1表示"负"。

正整数的原、反、补码都相同。

原码:直接将数值按照正负数的形式翻译成二进制得到的就是原码。

反码:正数反码和原码一样。

补码:正数补码和原码一样。

负整数的三种表示方法各不相同。

原码:直接将数值按照正负数的形式翻译成二进制得到的就是原码。(注意符号位)

反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。

补码:反码+1就得到补码。


4.移位操作符

只有整数才能运用移位操作符。

4.1 左移操作符

例1:

c 复制代码
#include <stdio.h>
int main()
{
    int num = 10;
    int n = num<<1;
    //10补码:  00000000 00000000 00000000 00001010
 //10<<1补码:0 00000000 00000000 00000000 00010100
    //这里最前面的0被丢掉了
    printf("n= %d\n", n);
    printf("num= %d\n", num);
    return 0;
}

打印:

c 复制代码
n= 20
num= 10

例2:

c 复制代码
#include <stdio.h>
int main()
{
    int num = -1;
    int n = num<<1;
    //-1原码: 10000000 00000000 00000000 00000001
    //-1反码: 11111111 11111111 11111111 11111110
    //-1补码: 11111111 11111111 11111111 11111111
//-1<<1补码:0 11111111 11111111 11111111 11111110
    //这里最前面的0被丢掉了
    printf("n= %d\n", n);
    printf("num= %d\n", num);
    return 0;
}

打印:

c 复制代码
n= -2
num= -1

4.2 右移操作符

右移运算分两种:

  1. 逻辑右移:左边用0填充,右边丢弃。

  2. 算术右移:左边用原该值的符号位填充(正数左边补0,负数左边补1),右边丢弃。

    右移是算术右移还是逻辑右移是取决于编译器的。通常采用的都是算数右移。

例1:

c 复制代码
#include <stdio.h>
int main()
{
    int num = -10;
    //-10原码:10000000 00000000 00000000 00001010
    //-10反码:11111111 11111111 11111111 11110101
    //-10补码:11111111 11111111 11111111 11110110
    //逻辑右移:01111111 11111111 11111111 11111011 |0(这个0被丢弃了)
    //算术右移:11111111 11111111 11111111 11111011 |0(这个0被丢弃了)
    //这里采用算数右移
    //右移补码:11111111 11111111 11111111 11111011
    //右移反码:10000000 00000000 00000000 00000100
    //右移原码:10000000 00000000 00000000 00000101--->-5
    int n = num>>1;
    printf("n= %d\n", n);
    printf("num= %d\n", num);
    return 0;
}

打印:

c 复制代码
n= -5
num= -10

5.位操作符:&、|、^、~

双目操作符:

  1. &:按位与
  2. |:按位或
  3. ^:按位异或

单目操作符:

  1. ~:按位取反

5.1 &:按位与

有0则0。

c 复制代码
#include <stdio.h>
int main()
{
    int a = 6;
    // 6的补码:00000000 00000000 00000000 00000110
    int b = -7;
    //-7的原码:10000000 00000000 00000000 00000111
    //-7的反码:11111111 11111111 11111111 11111000
    //-7的补码:11111111 11111111 11111111 11111001
    int c = a & b;
    // 6的补码:00000000 00000000 00000000 00000110
    //-7的补码:11111111 11111111 11111111 11111001
 //a & b补码::00000000 00000000 00000000 00000000-->0
    printf("n= %d\n", c);
    return 0;
}

打印:

c 复制代码
n= 0

5.2 |:按位或

有1则1。

c 复制代码
#include <stdio.h>
int main()
{
    int a = 6;
    // 6的补码:00000000 00000000 00000000 00000110
    int b = -7;
    //-7的原码:10000000 00000000 00000000 00000111
    //-7的反码:11111111 11111111 11111111 11111000
    //-7的补码:11111111 11111111 11111111 11111001
    int c = a | b;
    // 6的补码:00000000 00000000 00000000 00000110
    //-7的补码:11111111 11111111 11111111 11111001
   //a | b补码:11111111 11111111 11111111 11111111
   //a | b反码:10000000 00000000 00000000 00000000
   //a | b原码:10000000 00000000 00000000 00000001-->-1
    printf("n= %d\n", c);
    return 0;
}

打印:

c 复制代码
n= -1

5.3 ^:按位异或

相同为0,不同为1。

c 复制代码
#include <stdio.h>
int main()
{
    int a = 6;
    // 6的补码:00000000 00000000 00000000 00000110
    int b = -7;
    //-7的原码:10000000 00000000 00000000 00000111
    //-7的反码:11111111 11111111 11111111 11111000
    //-7的补码:11111111 11111111 11111111 11111001
    int c = a ^ b;
    // 6的补码:00000000 00000000 00000000 00000110
    //-7的补码:11111111 11111111 11111111 11111001
   //a ^ b补码:11111111 11111111 11111111 11111111
   //a ^ b反码:10000000 00000000 00000000 00000000
   //a ^ b原码:10000000 00000000 00000000 00000001-->-1
    printf("n= %d\n", c);
    return 0;
}

打印:

c 复制代码
n= -1

5.4 ~:按位取反

按2进制位取反

c 复制代码
#include <stdio.h>
int main()
{
    int a = 0;
    //0的原码:00000000 00000000 00000000 00000000
    //~a补码: 11111111 11111111 11111111 11111111
    //~a反码: 10000000 00000000 00000000 00000000 
    //~a原码: 10000000 00000000 00000000 00000001-->-1
    printf("n= %d\n", ~a);
    return 0;
}

打印:

c 复制代码
n= -1

5.5 例题

例题1

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

方法1:

c 复制代码
int main() {
	int a = 3;
	int b = 5;

	printf("交换前:a=%d b=%d\n", a, b);

	a = a + b;
	b = a - b;
	a = a - b;

	printf("交换后:a=%d b=%d\n", a, b);

	return 0;
}

打印:

c 复制代码
交换前:a=3 b=5
交换后:a=5 b=3

上面这个方法有点问题,如果a+b的和超过了一个整型数据的存储大小,那么就计算不了了。


方法2:

c 复制代码
int main() {
	int a = 3;
	int b = 5;

	printf("交换前:a=%d b=%d\n", a, b);

	a = a ^ b;
	b = a ^ b;
	a = a ^ b;

	printf("交换后:a=%d b=%d\n", a, b);

	return 0;
}

打印:

c 复制代码
交换前:a=3 b=5
交换后:a=5 b=3

为什么呢?

因为异或操作符,相同两个数异或为0。

3^3=0

a^a=0

0异或任何数都为数的本身 。

0^3=3

0^a=a

异或是支持交换率的。


例题2

编写代码实现:求一个整数存储在内存中的二进制中1的个数。

c 复制代码
int count_bit_onr(unsigned int n) {
	int count = 0;
	while (n) {
		if ((n % 2) == 1) {
			count++;
		}
		n = n / 2;
	}
	return count;
}

int main() {
	int num = 0;
	scanf("%d", &num);
	int ret = count_bit_onr(num);
	printf("%d\n", ret);

	return 0;
}

输入:

c 复制代码
15

打印:

c 复制代码
4

也可以这么写:

c 复制代码
int count_bit_onr(int n) {
	int i = 0;
	int count = 0;
	for (i = 0; i < 32; i++) {
		if ((n >> 1) & 1 == 1) {
			count++;
		}
	}
	return count;
}

int main() {
	int num = 0;
	scanf("%d", &num);
	int ret = count_bit_onr(num);
	printf("%d\n", ret);

	return 0;
}

输入-1会打印32

这个算法的原理:

c 复制代码
-1补码:11111111 11111111 11111111 11111111
1补码: 00000000 00000000 00000000 00000001
-1&1=  00000000 00000000 00000000 00000001

不论-1的补码前面多少个1,只要和1按位与后就只看最后一位是不是1。

然后运用移位操作符,将每一位都和1按位与,统计出一共多少个1。


其实还有更加巧妙地算法:

n=n&(n-1)

例如:n=11

c 复制代码
n       = 1011
n-1     = 1010
n&(n-1) = 1010

因为n=n&(n-1),所以现在新的n是1010

c 复制代码
n       = 1010
n-1     = 1001
n&(n-1) = 1000

因为n=n&(n-1),所以现在新的n是1000

c 复制代码
n       = 1000
n-1     = 0111
n&(n-1) = 0000

从这三次变化里面我们可以看到,我们执行了一次n=n&(n-1),那么n最右面那个1就会消失。把所有的1都去掉后就变成0了。


例题3

写一个代码,判断n是否为2的次方数。

首先我们先看看2的次方数:

c 复制代码
000010
000100
001000
010000
100000

可以看出2的次方数里面只有1个1。

c 复制代码
if (n & (n - 1) == 0) {
	printf("yes")
}

例题4

编写代码,将13二进制序列的第五位修改为1,然后改回0。

第5位改成1,第5位就和1或|,其他位都是0。

第5位改为0,第5位就和0与&,其他位都是1。

c 复制代码
int main() {
	int a = 13;
	//13原码:00000000 00000000 00000000 00001101
	//13补码:00000000 00000000 00000000 00001101
	//16补码:00000000 00000000 00000000 00010000
     //16|13:00000000 00000000 00000000 00011101-->29
	int n = 5;
	a = a | (1 << (n - 1));
	printf("%d\n", a);
	a &= ~(a << (n - 1));
	printf("%d\n", a);
    
	return 0;
}

打印:

c 复制代码
29
13
相关推荐
懒大王爱吃狼42 分钟前
Python教程:python枚举类定义和使用
开发语言·前端·javascript·python·python基础·python编程·python书籍
秃头佛爷2 小时前
Python学习大纲总结及注意事项
开发语言·python·学习
待磨的钝刨2 小时前
【格式化查看JSON文件】coco的json文件内容都在一行如何按照json格式查看
开发语言·javascript·json
XiaoLeisj4 小时前
【JavaEE初阶 — 多线程】单例模式 & 指令重排序问题
java·开发语言·java-ee
励志成为嵌入式工程师5 小时前
c语言简单编程练习9
c语言·开发语言·算法·vim
捕鲸叉5 小时前
创建线程时传递参数给线程
开发语言·c++·算法
A charmer5 小时前
【C++】vector 类深度解析:探索动态数组的奥秘
开发语言·c++·算法
Peter_chq5 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
记录成长java7 小时前
ServletContext,Cookie,HttpSession的使用
java·开发语言·servlet
前端青山7 小时前
Node.js-增强 API 安全性和性能优化
开发语言·前端·javascript·性能优化·前端框架·node.js