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 
int main()
{
	//得到的是小数,但是是放入整型数据,因此得到的是3
	printf("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;
}

C语言操作符_第1张图片

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 

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;

C语言操作符_第2张图片
C语言操作符_第3张图片

看完上面的例子后,咱们再看一个负整数的例子。
#define  _CRT_SECURE_NO_WARNINGS
#include 

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。

C语言操作符_第4张图片

2.2:右移操作符(>>)

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

2.2.1:逻辑右移

#define  _CRT_SECURE_NO_WARNINGS
#include 

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;
}

C语言操作符_第5张图片

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

2.2.2:算术右移

#define  _CRT_SECURE_NO_WARNINGS
#include 

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;
}

C语言操作符_第6张图片

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

C语言操作符_第7张图片

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

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

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

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

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

3.1:按位与操作符(&)

首先是按位与操作符,按位与操作符用一句话来总结就是**"同时为1则1",**可能家人们对这句话不是特别理解,我们来看一段代码。
#define  _CRT_SECURE_NO_WARNINGS
#include 

int main()
{
	int a = 5;
	int b = -3;
	int c = a & b;
	printf("%d\n", c);
	return 0;
}

C语言操作符_第8张图片

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

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

-3:原码:10000000000000000000000000000011
   反码:11111111111111111111111111111100
   补码:11111111111111111111111111111101
 5:补码:00000000000000000000000000000101
 c:补码: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 

int main()
{
	int a = 5;
	int b = -3;
	int c = a | b;
	printf("%d\n", c);
	return 0;
}

C语言操作符_第9张图片

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

int main()
{
	int a = 5;
	int b = -3;
	int c = a ^ b;
	printf("%d\n", c);
	return 0;
}

C语言操作符_第10张图片
将5和-3进行按位异或操作然后赋值给c,此时c的值为5,那么为什么为-8呢?博主带着大家来解析一下。

-3:原码:10000000000000000000000000000011
   反码:11111111111111111111111111111100
   补码:11111111111111111111111111111101
 5:补码:00000000000000000000000000000101
 c:补码: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 

int main()
{
	//初始化体重为120
	int weight = 120;
	//不满意就赋值
	weight = 90;
	return 0;
}

4.1:复合赋值符

    +=
	-=
	*=
	/=
	%=
	>>=
	<<=
	&=
	|=
	^=
像上面的运算符都可以写成复合的效果,那什么是复合的效果呢,我们来看一段代码。
#define  _CRT_SECURE_NO_WARNINGS
#include 

int main()
{
	int a = 10;
	//等价于 a = a + 1
	a += 1;
	int b = 5;
	//等价于 b = b << 3
	b <<= 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们下去以后可以自己去进行尝试.

C语言操作符_第11张图片

5:单目操作符

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

5.1:逻辑反操作符(!)

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

#define  _CRT_SECURE_NO_WARNINGS
#include 
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的值是多少。

C语言操作符_第12张图片

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

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

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

C语言操作符_第13张图片

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

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

取地址操作符与解引用操作符这里博主就直接通过代码来进行讲解。
#define  _CRT_SECURE_NO_WARNINGS
#include 
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

C语言操作符_第14张图片

5.4 sizeof操作符

sizeof操作符在之前我们就已经见过了,可以求出变量(数据类型)所占的空间大小。我们直接来看下面这段代码
#define  _CRT_SECURE_NO_WARNINGS
#include 
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;
}

C语言操作符_第15张图片

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

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

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

C语言操作符_第16张图片

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

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

5.6:++,–操作符

++与--操作符,这里我们直接看代码。
#define  _CRT_SECURE_NO_WARNINGS
#include 
int main()
{
	int a = 5;
	//先使用,后+1
	int b = ++a;
	//a = 6;b = 6 
	printf("前置++后:a = %d,b = %d\n", a,b);
	 b = a++;
	//b = 6,a = 7
	printf("后置++后: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:前置++和后置++对要自增的变量的本身没区别

C语言操作符_第17张图片

5.7:强制类型转换操作符

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

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

C语言操作符_第18张图片

6:关系操作符

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

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

7:逻辑操作符

&&   逻辑与
||   逻辑或

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

7.1:逻辑与操作(&&)

#define  _CRT_SECURE_NO_WARNINGS
#include 

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;
}

C语言操作符_第19张图片

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

7.2:逻辑或操作(| |)

#define  _CRT_SECURE_NO_WARNINGS
#include 
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;
}

C语言操作符_第20张图片

为什么这段代码执行出来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 
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.

C语言操作符_第21张图片

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

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

9.1:下标引用

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

C语言操作符_第22张图片

9.2:函数调用操作符

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

#define  _CRT_SECURE_NO_WARNINGS
#include 
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;
}

C语言操作符_第23张图片

9.3:结构体成员访问

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

#define  _CRT_SECURE_NO_WARNINGS
#include 
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;
}

C语言操作符_第24张图片

10:操作符的属性

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

10.1优先级

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

3 + 4 * 5;

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

10.2结合性

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

5 * 6 / 2;

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

C语言操作符_第25张图片

上图是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 
int main()
{
	char a = 5;
	char b = 126;
	char c = a + b;
	printf("%d\n", c);
	return 0;
}

C语言操作符_第26张图片

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:算术转换

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

C语言操作符_第27张图片

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

11.3:问题表达式解析

11.3.1:表达式1

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

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

C语言操作符_第28张图片

11.3.2:表达式2

c + --c;

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

11.3.3:表达式3

#include 
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们觉得博主讲的不错的话,请动动你们滴的小手给博主点个赞,你们滴鼓励将成为博主源源不断滴动力!

你可能感兴趣的:(C语言,c语言)