广告位联系
返回顶部
分享到

C语言各种符号的使用介绍(下篇)

C语言 来源:互联网 作者:酷站 发布时间:2022-08-10 11:55:39 人浏览
摘要

1、按位运算符 1.1 按位或( | )和按位与( ) 上期我们讲到过逻辑或和逻辑与,他们得到的结果是真假值,但我们一定要区分清楚,按位运算符 | 和 与逻辑运算符 || 是完全两个概念。 按位

1、按位运算符

1.1 按位或( | )和按位与( & )

上期我们讲到过逻辑或和逻辑与,他们得到的结果是真假值,但我们一定要区分清楚,按位运算符 "|" 和 "&" 与逻辑运算符 "||" "&&" 是完全两个概念。

按位,简明之意,按数值二进制位来进行运算,都是在数据补码的基础上进行。

按位或 "|" :两个数值的二进制补码对应位进行运算,对应位有 1 则为 1 ,否则为 0。

按位与 "&":两个数值的二进制补码对应位进行运算,对应位都为 1 则为 1, 否则为 0。

这里我们举例说明:

1 | 2 :

1 的二进制补码:0000 0000 ... 0000 0001

2 的二进制补码:0000 0000 ... 0000 0010

------按位或结果: 0000 0000... 0000 0011 -> 对应十进制:3

1 & 2:

1 的二进制补码:0000 0000 ... 0000 0001

2 的二进制补码:0000 0000 ... 0000 0010

------按位与结果: 0000 0000... 0000 0000 -> 对应十进制:0

其实有很多大学老师或者是书上都有可能把按位或,按位与,以及后面我们要讲的按位异或,他们会把每位二进制运算后的结果称为真或者假,其实这样的说法是不够严谨的,真假是逻辑判断,而按位运算得到的结果是数值,而且在C语言中0表示假,非0为真,所以我是不推荐这种说法。

1.2 按位异或( ^ )

按位或 "^" :两个数值的二进制补码对应位进行运算,相同为 0 , 不同为 1。

这里我们举例说明:

1 ^3:

1 的二进制补码:0000 0000 ... 0000 0001

3 的二进制补码:0000 0000 ... 0000 0011

---按位异或结果: 0000 0000... 0000 0010 -> 对应十进制:2

5 ^0:

5 的二进制补码:0000 0000 ... 0000 0101

0 的二进制补码:0000 0000 ... 0000 0000

---按位异或结果: 0000 0000... 0000 0101 -> 对应十进制:5

结论:任何数异或0都等于它本身

这里有一道笔试题:不创建临时变量,实现两个数的交换。

1

2

3

4

5

6

7

8

9

10

11

12

//很多小伙伴直接想出来的做法:

int main()

{

    int a = 10;

    int b = 20;

    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;

}

但是我们仔细研究下这段代码,他有没有什么隐藏的问题呢?

一个整型,占四个字节,也就是 32 个比特位,这里进行加法运算,就会产生进位,万一我们是两个很大的数相加呢?他们的和超过了整型最大存储范围,那么在计算机里面就会发生截断!为了避免发生这种现象,我们可以采取异或的方法来实现这道题:

最后还有一个很简单的按位取反操作符:~

用途:对一个数的二进制按位取反(包括它的符号位)

注意:以上的位运算符,他们的操作数必须是整数!

1.3 一个关于整型提升的问题

有这样一串代码,问:为什么一个char类型大小可以求出来是4字节?

无论任何位运算符,都是要计算机进行计算的,而计算机中CPU具有运算能力,但计算的数据都是放在内存中的。所以,做任何运算,都必须将数据从内存拿到CPU的寄存器中。而寄存器默认的操作数宽度是32位,可是,char类型数据只有1个字节,也就是8位,不满足32位怎么办,这就需要整型提升了!(详细整型提升大家可以查阅资料哦)

如果是一个有符号数的话:高位补符号位

如果是一个无符号数的话:高位补0

2、移位操作符

2.1 左移<< 右移>>操作符

<< 左移运算符是一个双目运算符,功能是把左边的运算数的各个二进制位向左移动指定位数。

>> 右移运算符是一个双目运算符,功能是把右边的运算数的各个二进制位向右移动指定位数。

注意:

<< 左移:最低位丢弃,最高位补零

>> 右移:

  1. 无符号数:最低位丢弃,最高位补零 [逻辑右移]
  2. 有符号数:最低位丢弃,最高位补符号位 [算数右移]

以上在补码中进行运算

警告:移位运算符,请不要移动负数位,这是标准未定义的!

左移我们好说,主要是右移我们需要细讲一下:

明显看到,这是在无符号数下进行右移,第一个小伙伴都不会感到惊讶, 可是第二个就有点不理解了,我们来解释下:

这里有一个问题,当 -1 准备放入变量 b 的时候我们需要看-1的类型吗?

答案是不需要!内存中放的都是二进制补码,本质上是把 -1 的补码放入变量 b 当中,第二,右移操作符属于计算,需要在CPU中进行,所以需要先把内存中 -1 的补码拿到CPU寄存器中运算,按照我们的规则,右移中,无符号数低位丢弃高位补零,所以 -1 右移完成之后就变成了 0111 1111 ... 1111 1111,接着我们以 %d 有符号整型打印,就会把他当作有符号数看待,最高位是 0 所以被认为是正数,转化成十进制也就是如上打印的值。

第二个我们来看下有符号数右移:

这个相信大家就很好理解了,第一个高位补符号位也就是补 0,低位丢弃,所以结果是 0,第二个高位补符号位也就是补 1,低位丢弃,值仍然不变,还是 -1。

注意:a>>1 并不会改变 a 变量的值,就好比如 a + 1。这样写才会改变:a = a >> 1;

2.2 习题练习

学完了上期的逻辑操作符,和本期的移位操作符,我们来练练手:

请你设计一个宏可以指定数据第几个比特位更改为 1 ,并设计一个函数将各个比特位打印出来。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

//参考

#define SETBIT(a, num) ((a) |= (1 << (num - 1)) )

void PrintBit(int a)

{

    int num = 31;

    while (num >= 0)

    {

        if ((a & (1 << num)))

            printf("1");

        else

            printf("0");

        --num;

    }

    printf("\n");

}

int main()

{

    int a = 0;

    SETBIT(a, 5);

    PrintBit(a);

    return 0;

}

3、++和--的操作

3.1 基本操作

其实这节知识点理解起来是很简单的,只不过总有些学校喜欢出一些很拉跨的题目:

int i = 3; 问:(++i) + (++i) + (++i) 的值是多少?

我的建议是,看到这类题,直接空着,你也可以在下面添一句,“ 你礼貌吗?”

这种表达式,在任何编译器下算出来的结果是不一样的!

对于这种问题没必要去争论谁对谁错, 如果有人想跟你杠的话,那么你直接告诉他,你真的超级高水平。

好了,言归正传,我们来说一下 ++ 和 -- 的基本理解:

  1. 前置++ -- :先自增(减),再使用
  2. 后置++ -- : 先使用,再自增(减) 如果没有变量接收,那么直接自增。

例子:

基本使用就是这么多,接下来我们从汇编角度来深度理解一下:

3.2 从汇编角度深入理解a++

既然我们知道,后置++ 是先使用后++,如果我们单纯的就 ++ 一下呢,他这个值被使用到了哪里去了呢?

1

2

3

4

5

6

7

8

int main()

{

    int a = 0xDD;

    int b = a++; //有b接收,那么a的先使用是将a的值(内容),放到b中

    int c = 0xEE;

    c++; //没有接收方,那么"先使用",如何理解?

    return 0;

}

vs2019编译器反汇编:

结论:后置++ 完整的含义是先使用,在自增,如果没有变量接收,那么直接自增。

注意:在不同的编译器可能处理过程不同,不过这是一个基本的研究过程,比单纯的理论学习更严谨。


版权声明 : 本文内容来源于互联网或用户自行发布贡献,该文观点仅代表原作者本人。本站仅提供信息存储空间服务和不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权, 违法违规的内容, 请发送邮件至2530232025#qq.cn(#换@)举报,一经查实,本站将立刻删除。
原文链接 : https://blog.csdn.net/m0_61784621/article/details/125469242
相关文章
  • C++中类的六大默认成员函数的介绍

    C++中类的六大默认成员函数的介绍
    一、类的默认成员函数 二、构造函数Date(形参列表) 构造函数主要完成初始化对象,相当于C语言阶段写的Init函数。 默认构造函数:无参的构
  • C/C++实现遍历文件夹最全方法总结介绍

    C/C++实现遍历文件夹最全方法总结介绍
    一、filesystem(推荐) 在c++17中,引入了文件系统,使用起来非常方便 在VS中,可以直接在项目属性中调整: 只要是C++17即以上都可 然后头文件
  • C语言实现手写Map(数组+链表+红黑树)的代码

    C语言实现手写Map(数组+链表+红黑树)的代码
    要求 需要准备数组集合(List) 数据结构 需要准备单向链表(Linked) 数据结构 需要准备红黑树(Rbtree)数据结构 需要准备红黑树和链表适配策略
  • MySQL系列教程之使用C语言来连接数据库

    MySQL系列教程之使用C语言来连接数据库
    写在前面 知道了 Java中使用 JDBC编程 来连接数据库了,但是使用 C语言 来连接数据库却总是连接不上去~ 立即安排一波使用 C语言连接 MySQL数
  • 基于C语言实现简单学生成绩管理系统

    基于C语言实现简单学生成绩管理系统
    一、系统主要功能 1、密码登录 2、输入数据 3、查询成绩 4、修改成绩 5、输出所有学生成绩 6、退出系统 二、代码实现 1 2 3 4 5 6 7 8 9 10 11
  • C语言实现共享单车管理系统

    C语言实现共享单车管理系统
    1.功能模块图; 2.各个模块详细的功能描述。 1.登陆:登陆分为用户登陆,管理员登陆以及维修员登录,登陆后不同的用户所执行的操作
  • C++继承与菱形继承的介绍

    C++继承与菱形继承的介绍
    继承的概念和定义 继承机制是面向对象程序设计的一种实现代码复用的重要手段,它允许程序员在保持原有类特性的基础上进行拓展,增加
  • C/C++指针介绍与使用介绍

    C/C++指针介绍与使用介绍
    什么是指针 C/C++语言拥有在程序运行时获得变量的地址和操作地址的能力,这种用来操作地址的特殊类型变量被称作指针。 翻译翻译什么
  • C++进程的创建和进程ID标识介绍
    进程的ID 进程的ID,可称为PID。它是进程的唯一标识,类似于我们的身份证号是唯一标识,因为名字可能会和其他人相同,生日可能会与其他
  • C++分析如何用虚析构与纯虚析构处理内存泄漏

    C++分析如何用虚析构与纯虚析构处理内存泄漏
    一、问题引入 使用多态时,如果有一些子类的成员开辟在堆区,那么在父类执行完毕释放后,没有办法去释放子类的内存,这样会导致内存
  • 本站所有内容来源于互联网或用户自行发布,本站仅提供信息存储空间服务,不拥有版权,不承担法律责任。如有侵犯您的权益,请您联系站长处理!
  • Copyright © 2017-2022 F11.CN All Rights Reserved. F11站长开发者网 版权所有 | 苏ICP备2022031554号-1 | 51LA统计