第三幕:C语言基础篇之运算符和表达式(原码,反码和补码介绍、C语言中常见运算符及其使用、优先级与结合性、不同数据类型间的混合运算)

一、说明:

1.数学与C语言在运算方面的异同点:

        (1)基本运算符号相似:

                加法、减法、乘法、除法在数学和C语言中都有对应的运算符号。例如,加法在数学中是"+",在C语言中也是"+"。

        (2)操作对象不同:

                数学中的运算通常是在数值、变量或表达式上进行的,而C语言中的运算符通常用于操作变量或数值,并且这些操作可以是赋值、比较、逻辑等。

        (3)优先级和结合性:

                在数学和C语言中,运算符具有优先级和结合性。但是,它们的优先级和结合性可能有些差异,需要根据语言规范或者数学规则进行理解和应用。

        (4)数据类型和精度:

                在C语言中,数据类型的选择会影响数学运算的精度和行为。例如,在C语言中,整数除法和浮点数除法会产生不同的结果,而在数学中,除法可能产生浮点数结果。

        (5)符号和功能:

                C语言中的一些运算符可能有特定的功能和用途,比如逻辑运算符、位运算符等,而这些可能在数学中并没有直接的对应。

2.原码、反码和补码

        (1)解释:

                在计算机中的存储和运算都是依靠二进制的。原码、反码和补码是为了解决计算机而存在的,因为计算机的运算器中只有加法器,所以在计算机中要将减法转换成加法来运算。在计算机中进行运算时都是以补码的形式进行计算的,因为在计算机中通常采用补码来表示有符号整数。

        (2)原码(Sign-Magnitude Representation):

                1)表示方式:最高位为符号位(0 表示正数,1 表示负数),其余位表示数值的绝对值。
                2)示例:对于 8 位二进制,`+3` 的原码是 `0000 0011`,而 `-3` 的原码是 `1000 0011`。
                3)优点:直观,易于理解。
                4)缺点:存在两个表示零的编码(`+0` 和 `-0`),而且加法和减法的操作比较繁琐。

        (3)反码(One's Complement):

                1)表示方式:正数的反码与原码相同,负数的反码是将其原码的每一位取反(0 变为 1,1 变为 0)。
                2)示例:对于 8 位二进制,`+3` 的反码是 `0000 0011`,而 `-3` 的反码是 `1111 1100`。
                3)优点:没有两个零的表示,且减法运算可以通过加法运算来实现。
                4)缺点:存在正零和负零的问题,而且需要特殊处理溢出。

        (4)补码(Two's Complement):

                1)表示方式:正数的补码与原码相同,负数的补码是将其反码加 1。
                2)示例:对于 8 位二进制,`+3` 的补码是 `0000 0011`,而 `-3` 的补码是 `1111 1101`。
                3)优点:没有两个零的表示,而且加法、减法、乘法等运算都可以通过相同的硬件逻辑实现。
                4)缺点:存在负零,但在实际应用中通常被忽略。

        (5)补充说明:

                1)零的表示:在反码和补码中,只有一种零的表示,即全零的编码。
                2)溢出处理:对于补码,溢出的情况需要特殊处理,而反码和原码需要额外的符号位来表示溢出。

        (6)计算机运算示例:

                1)计算14 - 21

                首先将14和-21转换为原码,就是将他们转换为二进制数字,14的原码为00001110,最高位的0为符号位。

                然后再将他们的原码转换为补码,因为正整数的原码、反码和补码都是一样的,所以14的反码和补码都为00001110,-21的原码为10010101,下面我们根据原码、反码和补码之间的转换规则可以得到-21的反码和补码分别为11101010,11101011。

                再然后将他们的补码按照小学数学中的加法(以二进制的形式进行相加,等于2时进位)相加为11111001,补码相加的到的结果还是补码。。

                最后将计算得到的结果重新转换为原码,因为正整数的原码、反码和补码都是一样的,所以我们首先观察最高位的符号位,我们计算的到的结果的最高位为1(负数),所以我们倒着来将补码转换成原码,首先将11111001减1,得到结果的反码为11111000,再最高位的符号位不变,其他位取反得到结果的原码为10000111,然后我们就可以将这个二进制转换为十进制得到我们想要的结果为-7

(记住计算机中的运算都是以补码的形式进行运算的,在接下来的内容里正整数的我将不会再提将补码转换成原码,不过大家要知道补码是要转换成原码后才能将二进制的数字以十进制表示,主要是因为正整数的原码、反码和补码都是一样的)

二 、C语言中常见的运算符

        C语言中有多种运算符,它们用于执行各种操作,包括算术运算、关系运算、逻辑运算等。以下是C语言中常见的运算符:

1. 算术运算符:

        `+`:加法
        `-`:减法
        `*`:乘法
        `/`:除法
        `%`:取模(求余)

2. 关系运算符:

        `==`:等于
        `!=`:不等于
        `<`:小于
        `>`:大于
        `<=`:小于等于
        `>=`:大于等于

3. 逻辑运算符:

        `&&`:逻辑与
        `||`:逻辑或
        `!`:逻辑非

4. 位运算符:

        `&`:按位与
        `|`:按位或
        `^`:按位异或
        `~`:按位取反
        `<<`:左移
        `>>`:右移

5. 赋值运算符:

        `=`:赋值
        `+=`:加法赋值
        `-=`:减法赋值
        `*=`:乘法赋值
        `/=`:除法赋值
        `%=`:取模赋值
        `&=`:按位与赋值
        `|=`:按位或赋值
        `^=`:按位异或赋值
        `<<=`:左移赋值
        `>>=`:右移赋值

6.增量和减量运算符:

        `++`:递增
        `--`:递减

7. 条件运算符:

        `? :`:条件运算符(三元运算符)

8. 逗号运算符:

        `,`:逗号运算符,用于分隔表达式,从左到右计算,返回最后一个表达式的值。

9. 地址和间接访问运算符:

        `&`:取地址运算符
        `*`:间接访问运算符

10.sizeof运算符:

        `sizeof`:返回数据类型的大小(以字节为单位)

三、C语言中常见运算符的使用方法

1. 算术运算符

    用途:用于执行基本的数学运算。例如,加法和减法用于加减操作,乘法和除法用于乘除操作,取模用于计算余数。

        `+`(加法):将两个数相加。

int sum = a + b;

        `-`(减法):从第一个数中减去第二个数。

int difference = a - b;

        `*`(乘法):将两个数相乘。

int product = a * b;

(由于键盘上面没有' × '号,所以运算符' × '由' * '代替)

        `/`(除法):将第一个数除以第二个数。

double quotient = (double)a / b;

(同样的由于键盘上没有' ÷ '号,运算符' ÷ '由' / '代替)

        注意:在使用' / '时,两个实数相除的结果是双精度实数,两个整数相乘的结果是整数,例如:如果你想计算' 3 ÷ 2 '的值,直接在代码中写' 3/2 ',那么得到的值是1,会舍去小数的部分(不会按四舍五入的方式舍去,是直接去掉小数部分),所以大家如果想计算整数之间的除法就要进行强制类型转换,必须要将其中的一个整数转换成双精度浮点数,下面是使用' / '的代码示例:

#include<stdio.h>

int main() {
	int a = 3;
	int b = 2;
	double quotient1 = a / b;
	double quotient2 = (double)a / b;
	printf("%g   %g", quotient1, quotient2);
	return 0;
}

输出结果为(前面的文章中讲过占位符' %g '的作用是不会输出多余的0):

        `%`(取模):返回第一个数除以第二个数的余数。

int remainder = a % b;

注意:' % '运算符处理的两个数字必须是整数!它的结果也是整数,除了' % '运算符要求参加运算的数字必须是整数外,其他的运算符操作的数可以是任何算数类型。

2. 关系运算符

    用途:用于比较两个值之间的关系,返回布尔结果(真或假)。通常在条件语句中使用。

        `==`(等于):检查两个值是否相等。(刚学C语言的同学们要注意,在判断相等的时候经常会有同学将两个等号写成一个等号,这是一个很多人都会犯的错误,而且很多人在写了一个等号之后通常在系统报错的时候都找不到自己的代码哪里出现了错误,这是个必须要注意的问题!!!)

if (a == b) {
    // 当a等于b是执行的代码
}

        `!=`(不等于):检查两个值是否不相等。

if (a != b) {
    // 当a不等于b实现的代码
}

         `<`(小于):检查第一个值是否小于第二个值。

if (a < b) {
    // 当a小于b时执行的代码
}

         `>`(大于):检查第一个值是否大于第二个值。

if (a > b) {
    // 当a大于b时执行的代码
}

         `<=`(小于等于):检查第一个值是否小于或等于第二个值。

if (a <= b) {
    // 当a小于等于b时执行的代码
}

        `>=`(大于等于):检查第一个值是否大于或等于第二个值。

if (a >= b) {
    // 当a大于等于b时执行的代码
}

3. 逻辑运算符

    用途:用于执行逻辑运算。逻辑与(' && ')和逻辑或(' || ')用于组合多个条件,逻辑非(' ! ')用于取反。

        `&&`(逻辑与):如果两个条件都为真,则整个条件为真。

if (表达式1 && 表达式2) {
    // 当两个表达式都为真时执行的代码
}

        `||`(逻辑或):如果至少有一个条件为真,则整个条件为真。

if (表达式1 || 表达式2) {
    // 至少一个表达式为真时执行的代码
}

        `!`(逻辑非):反转条件的真假。

if (!表达式) {
    // 表达式为假使执行的代码
}

4. 位运算符

    用途:用于对整数的二进制进行操作。按位与(' & ')、按位或(' | ')和按位异或(' ^ ')用于执行位级别的操作,而左移(' <<  ')和右移(' >> ')用于移动二进制位(在讲完位运算符后我会讲一讲在计算机中数字是如何存储的)。

         `&`(按位与):对两个数的每一位执行与操作。(所对应的二进制位都为真则为真)

#include<stdio.h>

int main(){
    int a = 3;//二进制形式为00000011
    int b = 5;//二进制形式为00000101
    int result = a & b;
    printf("%d", result);
    return 0;
}

        大家请先猜一下上面的代码的输出结果是多少呢?(其实int型占4个字节,为32位,这里为了更好的说明清楚,我们就以8位为例)

代码输出结果:

        解释:(大家有想带这个结果吗?)在讲位运算符的作用的时候我们就已经说了,位运算符是对二进制位的操作,而3所对应的二进制数字为00000011, 5所对应的二进制数字为00000101,然后再结合' & '运算符的性质(都为真则真,C语言中非零为真)将3和5所对应的二进制位一一对应,从低位开始,3所对应的二进制数字最低位1对应着5所对应的二进制数字最低位1,1和1都为真,所以result所对应的最低二进制位为1......以此类推result所对应的二进制数字为00000001,而' %d '占位符代表的是打印十进制数字,因此输出的结果是1。

        `|`(按位或):对两个数的每一位执行或操作。(一位为真则真)

#include<stdio.h>

int main(){
    int a = 3;//二进制形式为00000011    
    int b = 5;//二进制形式为00000101
    int result = a | b;
    printf("%d", result);
    return 0;
}

        根据' & '的运算形式再结合' | '的运算法则,则可以得出result的值的二进制形式为00000111,也就是说输出的结果是7。

代码输出结果:

        `^`(按位异或):对两个数的每一位执行异或操作。(不是次方哦,是异或,所对应的二进制位相同则为假,不同则为真)

#include<stdio.h>

int main(){
    int a = 3;//二进制形式为00000011
    int b = 5;//二进制形式为00000101
    int result = a ^ b;
    printf("%d", result);
    return 0;
}

        根据上面的两个例子,相信大家结合一下' ^ '的运算法则就能得出结果了。

代码输出结果:

        `~`(按位取反):对数的每一位执行取反操作。(就算是符号位也要变哦!!!

#include<stdio.h>

int main(){
    int a = 3;//二进制形式为00000011
    int result = ~a;
    printf("%d", result);
    return 0;
}

        大家想一想输出的结果是多少呢?注意取反之后变为了负数哦,并且计算机中的运算都是以补码的形式哦!

输出结果:

        怎么样?大家想的结果对了吗?3的进制位为00000011,因为为正整数,所以补码为00000011,取反之后为11111100,注意最高位的符号位为1(负数)所以我们要按照原码、反码和补码的转换规则将其转换,首先将结果减1变为反码为11111011,再最高位符号位不变其余位取反变为原码为10000100,最后再将其转换为十进制为-4。

        `<<`(左移):将数的二进制表示向左移动指定的位数。(就是将所有的位数向左移动,因为向左移左边的位数溢出,所以会删掉,而右边会多出来一位则会用0补齐)

#include<stdio.h>

int main(){
    int a = 3;//二进制形式为00000011
    int result = a << 1;
    printf("%d\n", result);
    return 0;
}

        大家想一想输出的结果会是多少呢?

输出结果:

        代码中的第三行' int result = a << 1'意思就是a的二进制位向左移动一位后将值赋值给result,也就是说3原来所代表的二进制位为00000011,' a << 1 '为00000110,十进制数字代表为6,注意a的值本身是不变的,我们将代码改一下:

#include<stdio.h>

int main(){
    int a = 3;//二进制形式为00000011
    int result = a << 1;
    printf("%d\n", result);
    printf("%d", a);
    return 0;
}

运行结果为:

        假如第三行的代码我写的是' int result = a << 2 '那么result所代表的二进制数为00001100,输出结果就会变为12。' int result = a << n '也就是说n为几,就会移动几位。

        `>>`(右移):将数的二进制表示向右移动指定的位数。(同理,这个运算符就是将二进制位向右移)

#include<stdio.h>

int main(){
    int a = 3;//二进制形式为00000011
    int result = a >> 1;
    printf("%d\n", result);
    return 0;
}

        大家再想一想这个输出的结果将会是多少呢?

输出结果:

5. 赋值运算符

    用途:用于给变量赋值。复合赋值运算符结合了算数运算符和赋值操作,例如' += '表示将右侧的值加到左边的变量上。

        `=`(赋值):将右侧的值赋给左侧的变量。

int x = 10;

        `+=`、`-=`、`*=`、`/=`、`%=` 等(复合赋值运算符):结合算术运算和赋值。

x += 5;  // 相当于 x = x + 5;
x -= 5;  // 相当于 x = x - 5;
......

6. 增量和减量运算符

    用途:增量运算符' ++ '用于递增变量,减量运算符' -- '用于递减变量。

        (1)增量运算符 (++):

           用法:`++variable` 或 `variable++`
        示例:

int a = 5;
int b = ++a; // 先将a的值增加1,然后将结果赋给b
// 现在a和b的值都是6

        (2)减量运算符 (--):

          用法:`--variable` 或 `variable--`
        示例:

int x = 8;
int y = x--; // 先将x的值赋给y,然后再将x的值减少1
// 现在x的值是7,y的值是8

注意:自增和自减运算符比较特殊,假如有变量' a ',' ++a '和' a++ '是不同的,前一个是a先自加再使用自加后的值,后一个是先使用a的值,然后a再自加。

        问答环节:

题目:请尝试预测程序运行后的输出结果,并解释为什么???(请大家动动脑筋,在评论区说出你的答案吧!)

#include <stdio.h>

int main() {
    int x = 5;
    int y, z;

    y = x++;  // 自增运算符在变量后面
    z = ++x;  // 自增运算符在变量前面

    printf("x = %d, y = %d, z = %d\n", x, y, z);

    return 0;
}

7.条件运算符

    用途:也称为三元运算符,用于根据条件选择两个值之一。

        在C语言中,条件运算符是一种三元运算符,通常称为三元条件运算符。它的语法形式为:

条件表达式 ? 值1 : 值2;

        如果条件表达式的值为真(非零),则整个表达式的值为 `值1`;否则,表达式的值为 `值2`。

以下是一个简单的示例:

#include <stdio.h>

int main() {
    int a = 5;
    int b = 10;
    int max;

    // 使用条件运算符来找到两个数的最大值
    max = (a > b) ? a : b;

    printf("%d和%d中最大的数是%d", a, b, max);

    return 0;
}

在这个例子中,如果 `a > b` 为真,那么 `max` 将等于 `a`,否则它将等于 `b`。

输出结果:

        使用条件运算符的好处:条件运算符常用于简单的条件赋值,使代码更简洁和可读。

8.逗号运算符

    用途:用于在表达式中分隔多个子表达式,按照从左到右的顺讯计算, 返回最后一个表达式的值

        在C语言中,逗号运算符(`,`)是一个二元运算符,用于连接两个表达式,并返回右侧表达式的值。逗号运算符的使用方式如下:

表达式1, 表达式2

逗号运算符的执行顺序是从左到右,首先计算 `表达式1`,然后计算 `表达式2`,最后返回 `表达式2` 的值。

逗号运算符主要有两个用途:

        1. 表达式连接:(表达式的连接是指使用逗号运算符(`,`)将多个表达式组合在一起,形成一个由逗号分隔的表达式序列。在这个序列中,每个表达式按照从左到右的顺序依次执行,最终整个表达式的值为序列中最右侧表达式的值。)

int a = 5, b = 10, c;
c = (a++, b++, a + b);
// 此时,a的值为6,b的值为11,c的值为a + b,即17

        2.在`for`循环中使用:

for (int i = 0, j = 10; i < 5; i++, j--) {
    // 循环体
}
// 在这个例子中,逗号用于连接初始化表达式、循环条件表达式和递增/递减表达式

注意:虽然逗号运算符在一些特定情况下很有用,但过度使用它可能会降低代码的可读性,因此应谨慎使用。

9.地址和间接访问运算符

    用途:' & '用于获取变量的地址,' * '用于通过指针访问指向的对象。

(1)地址运算符 (`&`):

        用法:`&变量`
        示例:

int num = 42;
int *ptr = &num; // 获取变量num的地址并赋给指针ptr

        `&` 用于获取变量的地址。在上面的例子中,`&num` 表示变量 `num` 的地址,这个地址被赋给指针 `ptr`。

        (2)间接访问运算符 (`*`):

        用法:`*指针`
        示例:

int value = *ptr; // 通过指针ptr访问其所指向的变量的值

        `*` 用于访问指针所指向的变量的值。在上面的例子中,`*ptr` 表示指针 `ptr` 所指向的变量的值。这两个运算符通常一起使用,以实现指针的声明、赋值、访问等操作。例如:

int num = 10;
int *ptr = &num; // 声明指针并将变量num的地址赋给指针ptr
int value = *ptr; // 通过指针ptr访问其所指向的变量的值

这里,`ptr` 是指向 `num` 变量的指针,通过 `*ptr` 可以访问 `num` 变量的值。指针和间接访问是C语言中强大的特性,用于处理内存和动态分配等方面的任务。(这里大家先对指针有个大致的印象,在之后的学习中我们会详细的讲一讲与指针相关的知识。)

10.sizeof运算符

    用途:返回数据类型或对象的大小(以字节为单位)。通常用于动态内存分配和其他需要考虑内存大小的操作

        在C语言中,`sizeof` 运算符用于获取数据类型或对象的大小(以字节为单位)。`sizeof` 运算符的一般语法如下:

sizeof(type)

sizeof(expression)

其中,`type` 是数据类型,而 `expression` 可以是一个变量、数组、指针等。

示例:

#include <stdio.h>

int main() {
    int intSize = sizeof(int);
    printf("Size of int: %d bytes\n", intSize);

    double doubleSize = sizeof(double);
    printf("Size of double: %d bytes\n", doubleSize);

    char charArray[10];
    int arraySize = sizeof(charArray);
    printf("Size of charArray: %d bytes\n", arraySize);

    return 0;
}

在上面的例子中,`sizeof` 运算符用于获取 `int`、`double` 和 `charArray` 的大小,并将结果打印出来。这有助于程序员了解不同数据类型或对象在内存中占用的空间大小,且`sizeof` 运算符在编写与机器无关的代码和进行内存管理时非常有用。

注意:`sizeof` 返回的是一个 `size_t` 类型的值,通常使用 `%zu` 格式说明符进行打印。

例如:

printf("Size of int: %zu bytes\n", sizeof(int));

四、算数表达式和运算符的优先级与结合性

在C语言中,算数表达式中的运算符具有不同的优先级和结合性。以下是一些常见的算数运算符,按照优先级从高到低的顺序列出,并指明它们的结合性:

1. 结合性:从左到右

        `()`: 圆括号,用于改变运算符的优先级。

        `++`, `--`: 自增和自减运算符。

        `+`, `-`: 一元正号和一元负号。

        `*`, `/`, `%`: 乘法、除法和取模运算符。

        `+`, `-`: 加法和减法运算符。

        `<<`, `>>`: 位左移和位右移运算符。

        `<`, `<=`, `>`, `>=`: 关系运算符。

        `==`, `!=`: 相等性运算符。

        `&`: 按位与运算符。

        `^`: 按位异或运算符。

        `|`: 按位或运算符。

        `&&`: 逻辑与运算符。

        `||`: 逻辑或运算符。

2. 结合性:从右到左

        `?:`: 条件运算符。

        `=`, `+=`, `-=`, `*=`, `/=`, `%=`: 赋值运算符。

        注意:在表达式中,具有较高优先级的运算符会先于具有较低优先级的运算符进行计算。当优先级相同时,根据结合性决定计算的方向。

示例:

int result = 2 + 3 * 4;  // 先乘法后加法,result的值为14

使用括号可以改变运算符的优先级,例如:

int result = (2 + 3) * 4;  // 先加法后乘法,result的值为20

(也不必死记,只要知道:算数运算符是自左至右(左结合性),赋值运算符是自右至左(右结合性),其他复杂的遇到的时候查一下就可以了)

五、不同类型数据间的混合运算

        在C语言中,不同类型的数据之间进行混合运算时,会发生一些类型转换。C语言有两种类型的转换:隐式类型转换和显式类型转换。

1. 隐式类型转换(自动类型转换):

        当不同类型的数据进行运算时,系统会自动进行类型转换,将其中一个操作数的类型转换为另一个操作数的类型。这通常是为了保持精度或避免溢出。
        规则:按照一定的规则,将低精度类型自动转换为高精度类型。(当int、float和double类型在一起计算时,系统会将所有的数据类型转换为double型,如果字符(char)型数据和整型数据进行计算,就是将字符的ASCII值与整型数据进行运算,如果字符型数据与实型数据进行计算,则会将字符的ASCII码值转换为double型数据然后再进行计算)
        示例:

int a = 5;
double b = 2.5;
double result = a + b;  // int被隐式转换为double,result的值为7.5

2. 显式类型转换(强制类型转换):

        程序员可以使用强制类型转换来显式指定类型转换的方式。
        语法:`(type) expression`
        示例:

int a = 5;
double b = 2.5;
double result = a + b;  // int被隐式转换为double,result的值为7.5

        注意:在混合运算中,重要的是要注意类型的范围和精度。例如,将浮点数转换为整数时,小数部分将被截断,可能导致数据丢失。需要谨慎处理混合运算,以确保获得正确的结果并避免数据损失。

double a = 5.8;
int b = (int)a;  // b的值为5,小数部分被截断

六、总结

        本篇文章介绍了计算机是如何进行计算的,讲解了C语言中常见的运算符及其使用的方法、算数表达式和运算符的优先级,以及不同类型数据间的混合运算。

        好了以上就是本篇文章的主要内容了,希望各位小伙伴能从中学到知识,如果大家有所帮助的话,就请点点赞,点点关注和收藏吧!!!

        下期预告:第四幕:C语言基础篇之不同类型数据间的混合运算(隐式类型转换(整数提升、算数转换、整数浮点数转换、赋值转换)、显示类型转换(语法、目的、数据的丢失和溢出、指正类型转换、使用建议))

(最近已经没什么事情啦,可以连更了(如果我不懒的话),我之前的文章是不是一篇写的太多了,太冗杂了,我在考虑将每篇文章不再写的太长,分几次写,在这里想听听小伙伴们的意见。o(╥﹏╥)o)

  • 32
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值