C语言操作符

文章目录

  • 1:算术操作符
  • 2:移位操作符(移动的是二进制序列中的补码)
    • 2.1:知识补充(原码,反码,补码与二进制)
    • 2.2:左移操作符(<<)
    • 2.2:右移操作符(>>)
      • 2.2.1:逻辑右移
      • 2.2.2:算术右移
  • 3:位操作符(运算用的是二进制位的补码)
    • 3.1:按位与操作符(&)
    • 3.2:按位或操作符(|)
    • 3.3:按位异或操作符(^)
  • 4.赋值操作符
    • 4.1:复合赋值符
  • 5:单目操作符
    • 5.1:逻辑反操作符(!)
    • 5.2:正值,负值操作符(+ -)
    • 5.3:取地址操作符和解引用操作符(& *)
    • 5.4 sizeof操作符
    • 5.5:按位取反操作符(操作的是补码)
    • 5.6:++,--操作符
    • 5.7:强制类型转换操作符
  • 6:关系操作符
  • 7:逻辑操作符
    • 7.1:逻辑与操作(&&)
    • 7.2:逻辑或操作(| |)
  • 8.条件操作符(三目操作符)
  • 9:下标引用、函数调用和结构体成员访问操作符
    • 9.1:下标引用
    • 9.2:函数调用操作符
    • 9.3:结构体成员访问
  • 10:操作符的属性
    • 10.1优先级
    • 10.2结合性
  • 11:表达式求值
    • 11.1:整型提升
      • 11.1.1整型提升的意义:
      • 11.1.2:如何进行整型提升
    • 11.2:算术转换
    • 11.3:问题表达式解析
      • 11.3.1:表达式1
      • 11.3.2:表达式2
      • 11.3.3:表达式3

嘻嘻,家人们,今天咱们来详解C语言中的操作符,好啦,废话不多讲,开干!

1:算术操作符

算术操作符呢其实就是我们日常生活中常见的加减乘除还有取余,这里博主就不具体展开讲了,就针对这些操作符中的小注意事项来进行讲解。

+   -   *(乘法)  /(除法)   %(取余)
  • (1):除了%操作符以外,其他的几个操作符都可以作用于整数和浮点数。
  • (2):对于/操作符,如果两个操作符都为整数,则执行整数除法(即只保留整数部分,去除小数部分),而只要存在浮点数执行的就是浮点数除法
  • (3):%操作符的两个操作数必须为整数,返回的是整除之后的余数
#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{//得到的是小数,但是是放入整型数据,因此得到的是3printf("7   / 2    = %d\n", 7 / 2);printf("7.0 / 2.0  = %d\n", 7.0 / 2.0);/**只要浮点数存在,那么执行的就是浮点数除法*/printf("7.0 / 2    = %lf\n", 7.0 / 2);printf("7.0 / 2.0  = %lf\n", 7.0 / 2.0);//%操作符的两个操作数必须为整数,返回的是整数之后的余数printf("7   %%  2   = %d\n", 7 % 2);return 0;
}

在这里插入图片描述

2:移位操作符(移动的是二进制序列中的补码)

2.1:知识补充(原码,反码,补码与二进制)

在讲移位操作符之前,首先博主得补充些小知识,原码,反码和补码与二进制的相关知识

在日常生活中我们经常能够听到2进制、8进制、10进制、16进制的讲法,那是什么意思呢?其实2进制、8进制、10进制、16进制是数值的不同表示形式而已。拿具体的数字15来举例。

(1):15的2进制:1111
(2):15的8进制:17
(3):15的10进制:15
(4):15的16进制:F

博主重点介绍二进制,首先还是得从10进制讲起,10进制就是我们在日常生活中经常使用的,10进制数字有这两种特点:
(1):10进制中满10进1
(2):10进制的数字每一位都是0~9的数字组成。

那么其实二进制也是一样的,
(1):二进制中满2进1。
(2):二进制的数字每一位都0~1的数字组成。

了解了二进制相关的概念后,接下来博主将讲解原码、反码、补码

整数的2进制表示方法有三种,即原码,反码和补码
三种表示方法均有符号位和数值位两部分,符号位都是用0表示"正",用1表示"负",而数值位最高位的一位是被当做符号位,剩下的都是数值位。
正整数的原、反、补码都相同
负整数的三种表示方法各不相同
(1):原码:直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。
(2):反码:符号位不变,其他位依次按位取反就可以得到反码。
(3):补码:反码+1就得到补码。
对于无符号数整数来说,没有符号位,所有位均位有效位。

2.2:左移操作符(<<)

移位规则:左边丢弃,右边补0
#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>int main()
{int m = 7;int n = m << 1;printf("m = %d\n", m);printf("n = %d\n", n);return 0;
}

上面这段代码,将变量m左移一位之后然后赋值给n,7是正整数,因此原码,反码,补码相同,且int类型占四个字节,一个字节是8位,因此是32位。
7的补码:0000 0000 0000 0000 0000 0000 0000 0111
左移一位之后的补码:0000 0000 0000 0000 0000 0000 0000 1110
打印的时候,打印的是原码,而正整数的原码反码补码相同,因此此时将左移之后的补码转换为10进制数之后就是14,因此n = 14;

在这里插入图片描述
在这里插入图片描述

看完上面的例子后,咱们再看一个负整数的例子。
#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>int main()
{/*-7原码:10000000000000000000000000000111反码:11111111111111111111111111111000补码:11111111111111111111111111111001*/int m = -7;/* * 左移一位之后补码:11111111111111111111111111110010反码:11111111111111111111111111110001原码:10000000000000000000000000001110*/int n = m << 1;printf("m = %d\n", m);printf("n = %d\n", n);return 0;
}

此时,我将m赋值为-7,然后左移一位后赋值给n,在之前我们了解过,对于负整数,我们要求出补码,首先对原码取反得到反码,反码再+1得到补码,接着再左移一位(左边丢弃,右边补0)赋值给n,由于在输出打印的时候,是以原码的形式打印的,因此我们还需要求出左移之后的原码,根据计算可得,-7在左移一位之后得到的是-14,因此n的值为-14。

在这里插入图片描述

2.2:右移操作符(>>)

移位规则:首先算数右移分两种:
  • 1.逻辑右移:左边用0填充,右边丢弃。
  • 2.算术右移:左边用原该值的符号位进行填充,右边丢弃。

2.2.1:逻辑右移

#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>int main()
{/*-7原码:10000000000000000000000000000111反码:11111111111111111111111111111000补码:11111111111111111111111111111001*/int m = -7;int n = m >> 1;/*补码:01111111111111111111111111111100反码:01111111111111111111111111111011原码:00000000000000000000000000000100*/printf("m = %d\n", m);printf("n = %d\n", n);return 0;
}

在这里插入图片描述

如果是按照逻辑右移的方式来进行移位的话,-7在右移一位之后,应该会变成4。

2.2.2:算术右移

#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>int main()
{/*-7原码:10000000000000000000000000000111反码:11111111111111111111111111111000补码:11111111111111111111111111111001*/int m = -7;int n = m >> 1;/*补码:11111111111111111111111111111100反码:11111111111111111111111111111011原码:10000000000000000000000000000100*/printf("m = %d\n", m);printf("n = %d\n", n);return 0;
}

在这里插入图片描述

若按照算术右移的方式,在右边补上符号位,左边丢弃,在上面我们讲解过,对于有符号整数,最高位为符号位,0代表正,1代表负,因此-7在进行算术右移的时候应该补1,按照算术右移的方式,-7在算术右移一位之后,应该会变成-4。

在这里插入图片描述

通过观察结果可知,博主使用的vs2022在这里采用的是算术右移,因此n的是为-4。

(1)对于移位操作符,不要移动负数位,这个标准是未定义的。
(2)移位操作符的操作数只能是整数

3:位操作符(运算用的是二进制位的补码)

位操作符有三种,分别是
> 按位与--------- &
按位或--------- |
按位异或------ ^

我们分别来看这三个操作符。

3.1:按位与操作符(&)

首先是按位与操作符,按位与操作符用一句话来总结就是**"同时为1则1",**可能家人们对这句话不是特别理解,我们来看一段代码。
#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>int main()
{int a = 5;int b = -3;int c = a & b;printf("%d\n", c);return 0;
}

在这里插入图片描述

将5和-3进行按位与操作然后赋值给c,此时c的值为5,那么为什么为5呢?博主带着大家来解析一下。

在上面博主有讲到,按位与操作符同样计算的也是二进制位中的补码,因此首先我们将5和-3的补码表示出来。

-3:原码:10000000000000000000000000000011反码:11111111111111111111111111111100补码:111111111111111111111111111111015:补码:00000000000000000000000000000101c:补码:00000000000000000000000000000101

将5和-3的补码表示出来后,接下来就要对每一位进行按位与操作,什么叫同时为1则为1呢,譬如在这里,5的二进制位的最高位为0,-3的二进制为的最高位为1,由于不同时为1,那么在进行按位与操作后,因此c的二进制位的最高位得到的是0。再来,5的二进制位的最低位是1,-3的二进制位的最低位也是1,因此c的二进制位的最低位为1。其他的二进制位同样按照这种方式进行计算。在进行计算后,得到c的补码,我们发现最高位是0,因此c是正整数,那么原、反、补码相同,将其二进制转换为十进制得到的就是5。

3.2:按位或操作符(|)

讲完了按位与操作符之后,接下来再讲讲按位或操作符,按位或操作符用一句话总结就是**"其中为1则为1"。**,我们还是来看一段代码。
#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>int main()
{int a = 5;int b = -3;int c = a | b;printf("%d\n", c);return 0;
}

在这里插入图片描述

将5和-3进行按位或操作然后赋值给c,此时c的值为-3,那么为什么为-3呢?博主带着大家来解析一下,同样我们将5和-3的补码表示出来。
-3:原码:10000000000000000000000000000011反码:11111111111111111111111111111100补码:111111111111111111111111111111015:补码:00000000000000000000000000000101c:补码:11111111111111111111111111111101反码:11111111111111111111111111111100原码:10000000000000000000000000000011

将5和-3的补码表示出来后,接下来就要对每一位进行按位或操作,什么叫其中为1则为1呢?譬如在这里,5的二进制位的最高位为0,-3的二进制为的最高位为1,由于存在1,那么在进行按位或操作后,因此c的二进制位的最高位得到的是1。再来,5的二进制位的最低位是1,-3的二进制位的最低位也是1,因此c的二进制位的最低位为1。其他的二进制位同样按照这种方式进行计算。在进行计算后,得到c的补码,我们发现最高位是1,也就是说此时变量c为负整数,因此我们要求出其原码,补码求出原码的方式:**(1):符号位不变,补码减1得到反码;(2):符号位不变,数值位进行取反得到原码。**得到原码后,将其转换10进制数得到的就是-3啦!

3.3:按位异或操作符(^)

接下来我们再看一个位操作符,按位异或操作符,按位异或操作符用一句话来总结就是"相同为0,相异为1"。我们直接看下面这段代码。
#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>int main()
{int a = 5;int b = -3;int c = a ^ b;printf("%d\n", c);return 0;
}

在这里插入图片描述
将5和-3进行按位异或操作然后赋值给c,此时c的值为5,那么为什么为-8呢?博主带着大家来解析一下。

-3:原码:10000000000000000000000000000011反码:11111111111111111111111111111100补码:111111111111111111111111111111015:补码:00000000000000000000000000000101c:补码:11111111111111111111111111111000反码:11111111111111111111111111110111原码:10000000000000000000000000001000

将5和-3的补码表示出来后,接下来就要对每一位进行按位或操作,什么叫相同为0,相异为1呢?譬如在这里,5的二进制位的最高位为0,-3的二进制为的最高位为1,0和1不是相同的,那么在进行按位异或操作后,因此c的二进制位的最高位得到的是1。再来,5的二进制位的最低位是1,-3的二进制位的最低位也是1;1和1是相同的,因此c的二进制位的最低位为0。其他的二进制位同样按照这种方式进行计算。在进行计算后,得到c的补码,我们发现最高位是1,也就是说此时变量c为负整数,因此我们要求出其原码,得到原码后,将其转换10进制数得到的就是-8啦!

  • a ^ a = 0;
  • 0 ^ a = a;

PS:对于位操作符,他们的操作数也同样必须为整数。

4.赋值操作符

赋值操作符是一个很棒的操作符,他可以让你得到一个你之前不满意的值,也就是你可以给自己重新赋值。
#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>int main()
{//初始化体重为120int weight = 120;//不满意就赋值weight = 90;return 0;
}

4.1:复合赋值符

    +=-=*=/=%=>>=<<=&=|=^=
像上面的运算符都可以写成复合的效果,那什么是复合的效果呢,我们来看一段代码。
#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>int main()
{int a = 10;//等价于 a = a + 1a += 1;int b = 5;//等价于 b = b << 3b <<= 3;printf("%d\n", a);printf("%d\n", b);return 0;
}

这段代码中的a += 1等价于 a = a + 1,用变量a最初值进行加1然后再赋值给a;那么同理,b也是一样,b <<= 3 等价于 b = b << 3;将b左移三位以后然后赋值给b。其他的复合操作符也是同理哦,这里博主就不一一举例啦,uu们下去以后可以自己去进行尝试.

在这里插入图片描述

5:单目操作符

! 	   逻辑反操作
-       负值
+	    正值
&	    取地址
sizeof  求操作数的数据类型长度(以字节为单位)
~	    对一个数的二进制位取反(操作的是补码)
--      前置、后置--
++      前置、后置++
*	    间接访问操作符(解引用操作符)
(类型)   强制类型转换

5.1:逻辑反操作符(!)

逻辑反操作符是将true(非0值)转换false(0);将false(0)转换为1(非0值)。这句话是什么意思呢?我们看下面这段代码.

#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{int data = 0;int b = !data;printf("%d\n", b);if (b){printf("hello world\n");}return 0;
}

我们定义一个变量data为0,然后对其进行逻辑反操作之后赋值给b,原本data为0,对其进行逻辑反操作后赋值给b,那么此时b为非0值,在之前我们了解过,C语言中非0为真,0为假,因此,此时if的条件判断成立,会在屏幕上打印hello world,同时我们也可以观察b的值是多少。

在这里插入图片描述

5.2:正值,负值操作符(+ -)

正值,赋值操作符就是我们在数学中所见到的正负号。

#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{int a = -5;int b = -a;int c = +a;printf("b = %d,c = %d\n", b, c);return 0;
}

在这里插入图片描述

注意:在C语言中对负数使用正值操作符然后再赋值给其他变量,该变量的值依旧为负数。

5.3:取地址操作符和解引用操作符(& *)

取地址操作符与解引用操作符这里博主就直接通过代码来进行讲解。
#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{int a = 10;//定义指针变量p,存储a的地址int* p = &a;//对p进行解引用操作,*p是通过p中存放的地址,找到所指的对象,*p其实就是a*p = 20;printf("a  = %d\n", a);printf("*p = %d\n", *p);printf("p  =%p\n", p);return 0;
}

这段代码我们定义一个变量a,然后使用&(取地址操作符)取出a的地址赋值给p,在C语言初识时,博主讲过,地址存放指针里头,因此在这里定义了一个指针变量p来存放变量a的地址;然后对指针变量p使用*(解引用操作符)找到其所指的对象,因此 *p 为变量 a

在这里插入图片描述

5.4 sizeof操作符

sizeof操作符在之前我们就已经见过了,可以求出变量(数据类型)所占的空间大小。我们直接来看下面这段代码
#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{/*sizeof是计算类型创建变量或者变量的大小,单位为字节sizeof计算的结果是size_t类型的size_t是无符号整型的对size_t类型的数据进行打印,可以使用%zd*/int a = 10;printf("%zd\n", sizeof(a));printf("%zd\n", sizeof(int));printf("%zd\n", sizeof a);return 0;
}

在这里插入图片描述

sizeof后面的括号在括号中写的不是数据类型的时候,括号可以省略,因此sizeof不是函数sizeof是操作符,单目操作符。

5.5:按位取反操作符(操作的是补码)

什么是按位取反操作符呢?所谓按位取反操作符,就是将一个数的二进制位补码进行取反,对于0取反之后则为1,对于1取反之后则为0,我们来看下面这段代码。
#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{int a = 0;int b = ~a;printf("%d\n", b);return 0;
}

在这里插入图片描述

0:补码:00000000000000000000000000000000
b:补码:11111111111111111111111111111111反码:11111111111111111111111111111110原码:10000000000000000000000000000001

为什么结果是对0按位取反后结果是-1呢?首先,将0的补码表示出来,然后对其进行按位取反,得到b的补码,我们观察b的补码发现,最高位为1,因此b为负数,然后我们求出其原码,将其转换为10进制以后就能得到b的值为-1了.

5.6:++,–操作符

++与--操作符,这里我们直接看代码。
#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{int a = 5;//先使用,后+1int b = ++a;//a = 6;b = 6 printf("前置++后:a = %d,b = %d\n", a,b);b = a++;//b = 6,a = 7printf("后置++后:a = %d,b = %d\n",a,b);return 0;
}

前置++的窍门:先+1后使用,在这里,先对a进行+1然后再将其赋值给b,那么此时a的值为6,b的值也为6。
后置++的窍门:先使用再+1,在这里,先将a的值赋值给b,由于前面对a使用一次前置++,使用完了以后a再+1,此时a的值为7,b的值依旧为6
PS:前置++和后置++对要自增的变量的本身没区别

在这里插入图片描述

5.7:强制类型转换操作符

所谓强制类型转换,就是将一种数据类型强制转换另外一种数据类型.
#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{//强制类型转换为整型;int a = (int)3.14;printf("%d",a)return 0;
}

在这里,我们将a这个double类型的变量使用强制类型转换,那么此时在屏幕上打印的时候,只会打印整数,不遵循四舍五入哦!只保留整数部分。
PS:在C语言中,直接写出浮点数,会被编译器识别为double类型,例如17.0就会被编译器识别为double类型,如果要令其为float类型,则应表示为17.0f。

在这里插入图片描述

6:关系操作符

>
>=
<
<=
!=      用于测试“不相等”
==      用于测试“相等”
这些关系运算符比较简单,博主就不具体讲解啦,但是要注意一些运算符使用时候的陷阱。

PS:在编程的过程中要注意== 和 =不小心写错而导致的错误哦!

7:逻辑操作符

&&   逻辑与
||   逻辑或

逻辑操作符呢博主在C语言初识那篇博客中详细讲解了概念,这里博主就不再讲解啦,忘记了的uu们可以返回C语言初识那篇博客去看看哦!博主这里就针对这两个操作符的特性来讲解短路操作.我们看下面这两段代码

7.1:逻辑与操作(&&)

#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>int main()
{int i = 0,a = 0, b = 2, c = 3, d = 4;i = a++ && ++b && d++;printf("a = %d,b = %d, c = %d, d = %d\n", a, b, c, d);printf("i = %d\n", i);return 0;
}

在这里插入图片描述

为什么这段代码执行出来只有a的值发生变化了呢?原因在于逻辑与操作符有短路操作的特性,我们在之前讲到过,**逻辑与操作是同时为真则为真,由于对变量a使用的是后置++,那么是先使用 a == 0进行判断,然而这个时候已经出现了0,那么这个表达式则为假,因此就不会再对后面的值进行判断了,这就是短路操作!**由于整个表达式的值为假,因此i的值为0,a的值为1,其他变量的值保持不变。

7.2:逻辑或操作(| |)

#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{int i = 0, j, a = 0, b = 2, c = 3, d = 4;j = a++ || ++b || d++;printf("a = %d,b = %d,c = %d,d = %d\n", a, b, c, d);printf("j = %d\n", j);return 0;
}

在这里插入图片描述

为什么这段代码执行出来a,b,j的值发生变化了呢?原因在于逻辑或操作符也有短路操作的特性,我们在之前讲到过,逻辑或操作是其中为真则为真,由于对变量a使用的是后置++,那么是先使用 a == 0进行判断,此时出现了一个0,因此继续向后判断,对b是使用的是前置++,因此是先使用再++,此时b的值为3,在之前我们讲到过,C语言中,0为假,非0为真,因此由于出现了一个真,那么整个表达式的值为真,就不会再继续向后判断,执行后面的语句了。由于整个表达式的值为真,因此j的值为1,a的值为1,b的值为3,其他变量的值保持不变。

8.条件操作符(三目操作符)

所谓三目操作符,其实和之前所学习到了if else语句一样,只是换汤不换药,万变不离其宗,我们直接看下面这段代码.
表达式1的结果为真,则执行表达式2;且表达式2的结果是整个表达式的结果;表达式3不计算
如果表达式1的结果为假,表达式2不计算;则执行表达式3;且表达式3的结果为整个表达式的结果
exp1 ? exp2 : exp3
#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{int a = 0;int b = 0;int max = 0;scanf("%d %d", &a, &b);max = a > b ? a : b;printf("max = %d\n", max);return 0;
}

我在这里用三目操作符来实现两个数找最大值,若a > b 这个表达式成立的话,那么则将a赋值给max,若a > b这个表达式为假,则将b赋值给max.

在这里插入图片描述

9:下标引用、函数调用和结构体成员访问操作符

这三个操作符,博主之前在C语言初识那篇博客中详细讲解了,忘了的uu可以回去看看哦.

9.1:下标引用

#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };//[]下标引用操作符printf("%d", arr[0]);return 0;
}

在这里插入图片描述

9.2:函数调用操作符

()函数调用
接受一个或者多个操作数:第一个操作数为函数名,剩余的操作数就是传递给函数的参数。

#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int Product(int x ,int y)
{return x * y;
}int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };//()函数调用操作符printf("hello world\n");int a = 0;int b = 0;scanf("%d %d", &a, &b);//()函数调用操作符int value = Product(a, b);printf("%d\n", value);return 0;
}

在这里插入图片描述

9.3:结构体成员访问

. 结构体变量.成员名
-> 结构体指针变量->成员名

#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
struct book
{char name[20];int  price;
};
void Print(struct book * b)
{//通过*解引用操作,找到所指的对象printf("%s %d\n", (*b).name, (*b).price);//结构体指针变量->成员名printf("%s %d\n", b->name, b->price);
}
int main()
{struct book b = { "C++书籍",25 };//结构体.成员,找到所指的对象printf("%s %d\n", b.name, b.price);Print(&b);return 0;
}

在这里插入图片描述

10:操作符的属性

C语言操作符有三个重要的属性影响因素
  • 操作符的优先级
  • 操作符的结合性
  • 是否控制求值顺序

10.1优先级

优先级指的是,如果一个表达式包含多个运算符,哪个运算符应该优先执行。各种运算符的优先级是不一样的。

3 + 4 * 5;

上⾯⽰例中,表达式 3 + 4 * 5 ⾥⾯既有加法运算符( + ),⼜有乘法运算符( * )。由于乘法的优先级⾼于加法,所以会先计算 4 * 5 ,⽽不是先计算 3 + 4 。

10.2结合性

如果两个运算符优先级相同,优先级没办法确定先计算哪个了,这时候就看结合性了,则根据运算符是左结合,还是右结合,决定执⾏顺序。大部分运算符是左结合(从左到右执行),少数运算符是右结合(从右到左执行),比如赋值运算符

5 * 6 / 2;

上面示例中,*和/的优先级相同,它们都是左结合运算符,所以从左到右执行,先计算 5 * 6,再计算6 / 2。

在这里插入图片描述

上图是C语言操作符的优先级顺序,uu们不需要全部记忆,只需要记住大概就好,在使用的时候如果忘记了查看表格即可!

11:表达式求值

11.1:整型提升

C语言中整型算术运算总是至少以缺省整型类型的精度来进行的。为了获得这个精度,表达式中的字符和短整型操作符在使用之前被转换为普通整型,这种转换称为整型提升

11.1.1整型提升的意义:

表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器的操作数的字节长度一般就是Int的字节长度,同时也是CPU的通用寄存器的长度。
因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。
通用CPU是难以直接实现两个8byte直接相加运算(虽然机器指令中可能存在这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送入CPU去执行运算。

11.1.2:如何进行整型提升

1:有符号整数提升是按照变量的数据类型的符号位来进行提升的.
2:无符号数整型提升,高位补0.

#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{char a = 5;char b = 126;char c = a + b;printf("%d\n", c);return 0;
}

在这里插入图片描述

5:补码:00000000 00000000 00000000 00000101
a:补码:00000101
126:补码:00000000 00000000 00000000 01111110
b:补码:01111110
c:补码:10000011
整型提升后
c:补码:11111111 11111111 11111111 10000011反码:11111111 11111111 11111111 10000010原码:10000000 00000000 00000000 01111101

上述代码中,将5存放在char类型的变量a中,将126存放在char类型的变量b中,由于char类型只占1个字节即8个bit位,int类型占个4个字节,32个bit位,因此会发生截断,取其前8位存放;那么分别将5和126的补码表示出来,表示出来后,取前8位,分别得到变量a与变量b的补码,最后将其补码相加得到变量c的补码,在打印变量c时,使用的是%d,十进制整数占位符,因此会发生整型提升,那么怎么提升呢?我们已经得到了变量c的补码,char类型的数据是signed char 还是 unsigned char是不确定的,C语言没有明确指定,是取决于编译器的,博主当前使用的VS上,char 类型默认是signed char即有符号数,因此在整型提升时,按照其符号位来进行提升,由于C的符号位为1,因此在整型提升时,进行高位补1,补齐剩下的24个字节,在进行整型提升后,我们得到了补码,由于在打印时打印的是原码,因此我们求出其原码,最后将其转换为10进制的数就能得出c的值为-125。

11.2:算术转换

如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数转换为另一个操作数的类型,否则操作就无法进行。下面的层次体系称为寻常算术转换

在这里插入图片描述

如果某个操作数的类型在上⾯这个列表中排名靠后,那么⾸先要转换为另外⼀个操作数的类型后执⾏运算。

11.3:问题表达式解析

11.3.1:表达式1

a * b + c * d + e * f;

上述表达式在计算的时候,由于*比+的优先级高,只能保证,的计算比+早,但是优先级并不能觉得第三个比第一个+早执行。那么就会如下两种情况的计算顺序。

在这里插入图片描述

11.3.2:表达式2

c + --c;

操作符的优先级只能决定自减–的运算在+的运算之前,但是我们并没有办法得知,+操作符的左操作数的获取在右操作数之前还是之后求值,所以结果是不可预测的。

11.3.3:表达式3

#include <stdio.h>
int fun()
{static int count = 1;return ++count;
}int main()
{int answer;answer = fun() - fun() * fun();printf("%d\n",answer);return 0;
}

上述代码虽然在大多数编译器上求得的结果是一样的,但是在表达式answer = fun() - fun() * fun()
中我们只能通过操作符的优先级得知:先算乘法,再算减法,函数的调用顺序无法通过操作符的优先级确定。

好啦,家人们,关于这块的相关细节知识,博主就讲到这里了,如果uu们觉得博主讲的不错的话,请动动你们滴的小手给博主点个赞,你们滴鼓励将成为博主源源不断滴动力!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/249247.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

系统分析师-22年-下午题目

系统分析师-22年-下午题目 更多软考知识请访问 https://ruankao.blog.csdn.net/ 试题一必答&#xff0c;二、三、四、五题中任选其中两题作答 试题一 (25分) 说明 某软件公司拟开发一套博客系统&#xff0c;要求能够向用户提供一个便捷发布自已心得&#xff0c;及时有效的…

使用 axios 请求库,设置请求拦截

什么是 axios&#xff1f; 基于promise网络请求库&#xff0c;可以同构&#xff08;同一套代码可以运行在浏览器&#xff09;&#xff0c;在服务端&#xff0c;使用原生node.js的http模块&#xff0c;在客户端&#xff08;浏览器&#xff09;中&#xff0c;使用XMLHttpRequests…

Kotlin快速入门系列2

Kotlin的基本数据类型 Kotlin 的基本数值类型包括 Byte、Short、Int、Long、Float、Double 等。不同于 Java 的是&#xff0c;字符不属于数值类型&#xff0c;是一个独立的数据类型。 Java和kotlin数据类型对照如下&#xff1a; Java基本数据类型 Kotlin对象数据类型 数据类…

【Linux】基本指令(上)

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:Linux ⚙️操作环境:Xshell (操作系统:CentOS 7.9 64位) 目录 Xshell快捷键 Linux基本指令 ls指令 pwd指令 cd指令 touch指令 mkdir指令 rmdir指令/rm指令 结语 Xshell快捷键 AltEnter 全屏/取消全屏 Tab 进…

HarmonyOS模拟器启动失败,电脑蓝屏解决办法

1、在Tool->Device Manager管理界面中&#xff0c;通过Wipe User Data清理模拟器用户数据&#xff0c;然后重启模拟器&#xff1b;如果该方法无效&#xff0c;需要Delete删除已创建的Local Emulater。 2、在Tool->SDK Manager管理界面的PlatForm选项卡中&#xff0c;取消…

开阳630hv100的代码编译以及软件制作步骤

打开项目功能步骤&#xff1a; 编译awtk功能&#xff1a; 选中awtk工程&#xff0c;先编译一次awtk sdk&#xff08;如下图的3和4步骤&#xff09;&#xff1b; 编译项目代码&#xff08;如下图步骤5和6&#xff09;&#xff1b; 编译完成后&#xff0c;软件路径&#xff1a;…

1.理解AOP,使用AOP

目录 1AOP基础 1.1 AOP概述 1.2AOP快速使用 2.3 AOP核心概念 1AOP基础 首先介绍一下什么是AOP&#xff0c;再通过一个快速入门程序&#xff0c;让大家快速体验AOP程序的开发。最后再介绍AOP当中所涉及到的一些核心的概念。 1.1 AOP概述 什么是AOP&#xff1f; 说白了&am…

FPGA高端项目:Xilinx Artix7系列FPGA 多路视频缩放拼接 工程解决方案 提供4套工程源码+技术支持

目录 1、前言版本更新说明给读者的一封信FPGA就业高端项目培训计划免责声明 2、相关方案推荐我这里已有的FPGA图像缩放方案我已有的FPGA视频拼接叠加融合方案本方案的Xilinx Kintex7系列FPGA上的ov5640版本本方案的Xilinx Kintex7系列FPGA上的HDMI版本 3、设计思路框架设计框图…

代理模式(静态代理、JDK 动态代理、CGLIB 动态代理)

代理模式(静态代理、JDK 动态代理、CGLIB 动态代理) 一、代理模式概述1. 生活中的代理案例2. 为什么要使用代理3. 代理模式在 Java 中的应用4. 概述5. 生活中代理图示二、代理的实现方式1. Java 中代理图示2. 静态代理2.1 案例2.2 实现案例2.3 静态代理存在的问题三、动态代理…

Vmware 无法开启虚拟化解决方法

最近遇到了Vmware无法开启虚拟化的问题,已经解决,记录一下解决经过。 我遇到的情况是BIOS已经开启虚拟化,HV服务也停用了,但是Vmware仍然提示模块“VPMC”启动失败。网上的解决方案千篇一律,基本都是排查BIOS、停用Windows的虚拟化功能、停用HV主机服务、Vmware配置中关闭…

RK3588平台开发系列讲解(视频篇)RKMedia的VDEC模块

文章目录 一、 VDEC模块支持的编码标准介绍二、VDEC API的调用三、VDEC解码流程沉淀、分享、成长,让自己和他人都能有所收获!😄 📢RKMedia是RK提供的一种多媒体处理方案,可实现音视频捕获、音视频输出、音视频编解码等功能。 一、 VDEC模块支持的编码标准介绍 RK3688 V…

【51单片机】点亮第一个LED灯

目录 点亮第一个LED灯单片机 GPIO 介绍GPIO 概念GPIO 结构 LED简介软件设计点亮D1指示灯LED流水灯 橙色 点亮第一个LED灯 单片机 GPIO 介绍 GPIO 概念 GPIO&#xff08;general purpose intput output&#xff09; 是通用输入输出端口的简称&#xff0c; 可以通过软件来控制…

使用宝塔面板访问MySQL数据库

文章目录 前言一、安装访问工具二、查看数据库总结 前言 前面我们已经部署了前后端项目&#xff0c;但是却不能得到数据库的信息&#xff0c;看有谁再使用你的项目。例如员工、用户等等。本次博客进行讲解如何在宝塔面板里面访问MySQL数据库。 一、安装访问工具 1、打开软件商…

分割头篇 | 原创自研 | YOLOv8 更换 SEResNeXtBottleneck 头 | 附详细结构图

左图:ResNet 的一个模块。右图:复杂度大致相同的 ResNeXt 模块,基数(cardinality)为32。图中的一层表示为(输入通道数,滤波器大小,输出通道数)。 1. 思路 ResNeXt是微软研究院在2017年发表的成果。它的设计灵感来自于经典的ResNet模型,但ResNeXt有个特别之处:它采用…

Redis单机-主从集群-哨兵集群-分片集群 搭建教程

Redis集群 本章是基于CentOS7下的Redis集群教程&#xff0c;包括&#xff1a; 单机安装RedisRedis主从Redis分片集群 1.单机安装Redis 首先需要安装Redis所需要的依赖&#xff1a; yum install -y gcc tclredis-6.2.4.tar.gz 然后将Redis安装包上传到虚拟机的任意目录&am…

【Vue3+Vite】Vue生命周期与组件 快速学习 第三期

文章目录 一、Vue生命周期1.1 生命周期简介1.2 生命周期案例 二、Vue组件2.1 组件基础2.2 组件化入门案例2.3 组件之间传递数据2.3.1父传子2.3.2 子传父2.3.3 兄弟传参 总结 一、Vue生命周期 1.1 生命周期简介 每个 Vue 组件实例在创建时都需要经历一系列的初始化步骤&#xf…

【JavaEE spring】SpringBoot 统一功能处理

SpringBoot 统一功能处理 1. 拦截器1.1 拦截器快速⼊⻔1.2 拦截器详解1.2.1 拦截路径1.2.2 拦截器执⾏流程 1.3 登录校验1.3.1 定义拦截器1.3.2 注册配置拦截器 2. 统⼀数据返回格式2.1 快速⼊⻔2.2 存在问题2.3 案例代码修改2.4 优点 3. 统⼀异常处理 1. 拦截器 后端程序根据…

GIS应用水平考试一级—2009 年度第二次

全国信息化工程师——GIS应用水平考试 2009 年度第二次全国统一考试一级 试卷说明: 1、本试卷共9页,6个大题,满分150 分,150 分钟完卷。 2、考试方式为闭卷考试。 3、将第一、二、三題的答案用铅笔涂写到(NCIE-GIS)答题卡上。 4、将第四、五、六题的答案填写到主观题答题卡上…

vit细粒度图像分类(七)TBNet学习笔记

1.摘要 细粒度鸟类图像识别致力于实现鸟类图像的准确分类&#xff0c;是机器人视觉跟踪中的一项基础性工作。鉴于濒危鸟类的监测和保护对保护濒危鸟类具有重要意义&#xff0c;需要采用自动化方法来促进鸟类的监测。在这项工作中&#xff0c;我们提出了一种新的基于机器人视觉…

Netty源码三:NioEventLoop创建与run方法

1.入口 会调用到父类SingleThreadEventLoop的构造方法 2.SingleThreadEventLoop 继续调用父类SingleThreadEventExecutor的构造方法 3.SingleThreadEventExecutor 到这里完整的总结一下&#xff1a; 将线程执行器保存到每一个SingleThreadEventExcutor里面去创建了MpscQu…