elsaflower 发表于 2025-6-16 22:24

符号的深度理解

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    https://edit.wpgdadawant.com/uploads/news_file/blog/2022/7019/tinymce/1.png其实有很多大学老师或者是书上都有可能把按位或,按位与,以及后面我们要讲的按位异或,他们会把每位二进制运算后的结果称为真或者假,其实这样的说法是不够严谨的,真假是逻辑判断,而按位运算得到的结果是数值,而且在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都等于它本身
这里有一道笔试题:不创建临时变量,实现两个数的交换。//很多小伙伴直接想出来的做法:

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 个比特位,这里进行加法运算,就会产生进位,万一我们是两个很大的数相加呢?他们的和超过了整型最大存储范围,那么在计算机里面就会发生截断!为了避免发生这种现象,我们可以采取异或的方法来实现这道题: https://edit.wpgdadawant.com/uploads/news_file/blog/2022/7019/tinymce/2.png
最后还有一个很简单的按位取反操作符:~

用途:对一个数的二进制按位取反(包括它的符号位)注意:以上的位运算符, 他们的操作数必须是整数!
1.3 一个关于整型提升的问题有这样一串代码,问:为什么一个char类型大小可以求出来是4字节? https://edit.wpgdadawant.com/uploads/news_file/blog/2022/7019/tinymce/3.png无论任何位运算符,都是要计算机进行计算的,而计算机中CPU具有运算能力,但计算的数据都是放在内存中的。所以,做任何运算,都必须将数据从内存拿到CPU的寄存器中。而寄存器默认的操作数宽度是32位,可是,char类型数据只有1个字节,也就是8位,不满足32位怎么办,这就需要整型提升了!(详细整型提升大家可以查阅资料哦)
如果是一个有符号数的话:高位补符号位如果是一个无符号数的话:高位补0
2、移位操作符2.1 左移<< 右移>>操作符<< 左移运算符是一个双目运算符,功能是把左边的运算数的各个二进制位向左移动指定位数。>> 右移运算符是一个双目运算符,功能是把右边的运算数的各个二进制位向右移动指定位数。
注意:<< 左移:最低位丢弃,最高位补零>> 右移:1.无符号数:最低位丢弃,最高位补零[逻辑右移]2.有符号数:最低位丢弃,最高位补符号位[算数右移]以上在补码中进行运算警告:移位运算符,请不要移动负数位,这是标准未定义的!
左移我们好说,主要是右移我们需要细讲一下: https://edit.wpgdadawant.com/uploads/news_file/blog/2022/7019/tinymce/4.png明显看到,这是在无符号数下进行右移,第一个小伙伴都不会感到惊讶, 可是第二个就有点不理解了,我们来解释下:这里有一个问题,当-1 准备放入变量b 的时候我们需要看-1的类型吗?答案是不需要!内存中放的都是二进制补码,本质上是把-1 的补码放入变量b 当中,第二,右移操作符属于计算,需要在CPU中进行,所以需要先把内存中-1 的补码拿到CPU寄存器中运算,按照我们的规则,右移中,无符号数低位丢弃高位补零,所以-1 右移完成之后就变成了0111 1111 ... 1111 1111,接着我们以%d 有符号整型打印,就会把他当作有符号数看待,最高位是0 所以被认为是正数,转化成十进制也就是如上打印的值。

第二个我们来看下有符号数右移:
https://edit.wpgdadawant.com/uploads/news_file/blog/2022/7019/tinymce/5.png这个相信大家就很好理解了,第一个高位补符号位也就是补0,低位丢弃,所以结果是0,第二个高位补符号位也就是补1,低位丢弃,值仍然不变,还是-1。
注意:a>>1 并不会改变a 变量的值,就好比如a + 1。这样写才会改变:a = a >> 1;
2.2 习题练习学完了上期的逻辑操作符,和本期的移位操作符,我们来练练手:请你设计一个宏可以指定数据第几个比特位更改为1 ,并设计一个函数将各个比特位打印出来。//参考

#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) 的值是多少?我的建议是,看到这类题,直接空着,你也可以在下面添一句,“ 你礼貌吗?” 这种表达式,在任何编译器下算出来的结果是不一样的! https://edit.wpgdadawant.com/uploads/news_file/blog/2022/7019/tinymce/6.png对于这种问题没必要去争论谁对谁错, 如果有人想跟你杠的话,那么你直接告诉他,你真的超级高水平。
好了,言归正传,我们来说一下++ 和-- 的基本理解:1.前置++ -- : 先自增(减),再使用2.后置++ -- : 先使用,再自增(减) 如果没有变量接收,那么直接自增。

例子: https://edit.wpgdadawant.com/uploads/news_file/blog/2022/7019/tinymce/7.png基本使用就是这么多,接下来我们从汇编角度来深度理解一下:
3.2 从汇编角度深入理解a++既然我们知道,后置++ 是先使用后++,如果我们单纯的就++ 一下呢,他这个值被使用到了哪里去了呢?int main()

{

    int a = 0xDD;

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

    int c = 0xEE;

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



    return 0;

}
vs2019编译器反汇编:

https://edit.wpgdadawant.com/uploads/news_file/blog/2022/7019/tinymce/8.png
结论:后置++ 完整的含义是先使用,在自增,如果没有变量接收,那么直接自增。注意:在不同的编译器可能处理过程不同,不过这是一个基本的研究过程,比单纯的理论学习更严谨。————————————————版权声明:本文为CSDN博主「程序猿教你打篮球」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/m0_61784621/article/details/125469242

ingramward 发表于 2025-7-2 12:28

C 语言中,符号的链接属性决定了它是否可以在多个翻译单元之间共享。

albertaabbot 发表于 2025-7-2 13:15

&:按位与,对应位都为1则为1,否则为0。
|:按位或,对应位有一个为1则为1。
^:按位异或,对应位相同为0,不同为1。
~:按位取反,每个位取反。
<<:左移,最高位丢弃,最低位补零。
>>:右移,无符号数最低位丢弃,最高位补零;有符号数最低位丢弃,最高位补符号位。

逆鳞风暴 发表于 2025-7-3 18:58

非常详细的解释了按位运算符和移位操作符,对于理解底层的二进制操作非常有帮助。

懒癌晚期患者 发表于 2025-7-3 19:27

按位运算和移位操作符的讲解非常清晰,尤其是对异或操作符的解释,让我对交换两个变量的方法有了新的认识。

maudlu 发表于 2025-7-3 20:38

作用域规则决定了符号在程序中的可见性和生命周期

benjaminka 发表于 2025-7-3 21:39

它们必须以字母或下划线开头,后面可以跟任意数量的字母、数字或下划线。

backlugin 发表于 2025-7-4 11:31

即使知道运算符优先级,复杂表达式也应使用括号提高可读性(如(a | b) & c比a | b & c更清晰)。

usysm 发表于 2025-7-4 13:32

C语言符号的深度理解是一个广泛且重要的主题

jtracy3 发表于 2025-7-4 18:59

在复杂的表达式中,使用括号可以明确运算的顺序,避免因运算符优先级引起的混淆。

ulystronglll 发表于 2025-7-4 22:26

这些符号在编写C语言程序时非常常用,掌握它们的用法和特性对于编写高效、正确的程序至关重要。

10299823 发表于 2025-7-5 20:41

C语言中的注释有两种风格:

/* ... */:C语言风格的注释,可以跨越多行。
// ...:C++风格的注释,只能用于单行。

alvpeg 发表于 2025-7-6 10:21

运算符有不同的优先级和结合性,这决定了它们在表达式中的执行顺序。例如,乘法和除法运算符的优先级高于加法和减法运算符。

adolphcocker 发表于 2025-7-6 15:28

在编译过程中,符号(Symbol) 是指源代码中具有意义的标识符(Identifier),例如:

变量名:int count;
函数名:void delay_ms(int ms);
类型名:struct Student
宏定义:#define PI 3.1415926
标签(Label):loop: ... goto loop;

olivem55arlowe 发表于 2025-7-7 15:36

算术运算符:+、-、*、/、%,用于基本的数**算。
关系运算符:==、!=、>、<、>=、<=,用于比较两个值的关系。
逻辑运算符:&&(逻辑与)、||(逻辑或)、!(逻辑非),用于逻辑判断。
位运算符:&(按位与)、|(按位或)、^(按位异或)、~(按位取反)、<<(左移)、>>(右移),用于位操作。
赋值运算符:=、+=、-=、*=、/=等,用于赋值和复合赋值。

robertesth 发表于 2025-7-10 13:57

运算符是用来执行特定数学或逻辑操作的符号。

uptown 发表于 2025-7-10 16:07

C语言符号是编程的基础工具,涵盖注释、宏、运算符、关键字等,其设计直接影响代码的逻辑表达、执行效率及可维护性。

jackcat 发表于 2025-7-11 14:44

C语言中的符号主要分为以下几类:

关键字:具有特定含义的保留字,例如int、char、if、for等。关键字不能作为用户自定义的标识符使用。
运算符:用于执行各种数学和逻辑运算的符号,例如+(加法)、-(减法)、*(乘法)、/(除法)、&&(逻辑与)、||(逻辑或)等。
标识符:由用户自定义的名称,用于表示变量、函数、常量等。标识符由字母、数字和下划线组成,且首字符不能是数字。
分隔符:用于分隔语句或代码块,例如;(语句结束符)、{}(代码块)、()(函数参数)等。

qiufengsd 发表于 2025-7-11 15:34

在编译过程中,符号解析是链接器的重要任务之一。

deliahouse887 发表于 2025-7-12 10:31

局部变量的作用域通常限定在定义它们的函数内部,而全局变量的作用域则跨越整个程序。
页: [1] 2
查看完整版本: 符号的深度理解