【万字讲解c语言操作符,堪称操作符“百科全书”】

C语言学习入门之操作符的使用

学习新知识啦- ̗̀(๑ᵔ⌔ᵔ๑)



一、操作符分类

在这里插入图片描述


2. 算数操作符

有 + - * / %
要注意的是

除法
1.整数除法(除号的两端都是整数)
2.浮点数除法(除号的两端只要有一个是小数就执行小数除法)

举例如下

int main()
{
   int r = 7 / 2;
   printf("%d\n", r);//打印3

   double d = 7 / 2;
   printf("%lf\n", d);//打印3

   double d = 7.0 / 2.0;
   printf("%lf\n", d);//打印3.5

   return 0;
}

除法中除数不能为零

举例如下

int main()
{

	int n = 0;
	int r = 5 / n;
	return 0;//编译器会报错
}

%
得到的是整除后的余数
要注意:% 取模操作符的两个操作数必须都是整数

举例如下

int main()
{
   int r = 15 % 8;
   printf("%d\n", r);//得到7
   return 0;
}

3. 位移操作符

在这里插入图片描述

3.1 >>

要注意:移位操作符的操作数只能是整数

举例如下

int main()
{
	int a = 15;	
	int b = a >> 1;//移动就是a中的2进制信息,并且>>左右都是整数,不能是小数

	return 0;
}

要计算移位还要知道一个知识点:

计算机,能够处理的是二进制的信息,如1和0。
15 就是十进制

例如:

在这里插入图片描述
那么15的二进制就是:
在这里插入图片描述

一个整形是4个字节 = 32bit位
那么15放进a里面要凑够32个bit位要怎么写呢?

规定:最高位是0表示它是正数,最高位是1表示它是负数
所以15的二进制表示形式如下:
00000000000000000000000000001111

而整数的二进制表达形式: 有三种表达形式

1.原码:正数,最高位为0;负数,最高位为1
2.反码:原码的符号位不变,其他位按位取反(也就是说原来是1的变成0,原来是0的变成1)
3.补码:反码+1

正整数的原码、反码、补码是相同的
负整数的原码、反码、补码是要计算的

所以15的原码,反码,补码都是:00000000000000000000000000001111

举例子:

int main()
{
	int a = 15;
00000000000000000000000000001111 - 原码
00000000000000000000000000001111 - 反码
00000000000000000000000000001111 - 补码

	int b = -15;
10000000000000000000000000001111 - 原码
11111111111111111111111111110000 - 反码(原码的符号位不变,其他位按位取反得到的就是反码)
11111111111111111111111111110001 - 补码(反码+1就是补码)
	
    return 0;
//}

要注意:整数在内存中存储的是补码
计算的时候也是使用补码计算的


所以移位移动的是二进制的补码

int main()
{
	int a = 15;
	int b = a >> 1;
	printf("%d\n", b);//7
	printf("%d\n", a);//15
	return 0;
}

上面的代码,a向右移动一位,b=7,a=15
下面是移动的方法:
在这里插入图片描述

右移分为两种
1.算数右移(右边丢弃,左边补原来的符号位)
2.逻辑右移(右边丢弃,左边直接补0)
C语言没有明确规定到底是算数右移还是逻辑右移,一般编译器上采用的是算术右移

右移之后,就变成了00000000000000000000000000000111
在这里插入图片描述
通过计算就得到了b=7,而a依然是15,这个运算不会让a的值变化

举一个负数例子:

int main()
{
	int a = -15;
	int b = a >> 1;
	printf("%d\n", b);//-8
	return 0;
}

在这里插入图片描述

我们是对-15的补码进行移动
补码右移得到b的二进制序列:11111111111111111111111111111000
但这是b的补码,我们还要推算出原码
补码-1就是反码:11111111111111111111111111110111
反码按位取反就是原码:10000000000000000000000000001000
再按照二进制的计算方法,结果为b = -8


3.2 <<

左移相对右移就相对简单一点
左移的移动原理:左边丢弃,右边补0
举例:

int main()
{
	int a = 6;
	int b = a << 1;
	printf("%d\n", b);
	return 0;
}

a的二进制序列:00000000000000000000000000000110
左移得到b:00000000000000000000000000001100
正数的原反补码相同,所以b=12,a不变为6

警告:对于一位运算符,不要移动负数位,这个是标准未定义的。

nt main()
{
	int a = 5;
	int b = a >> -2;//标准未定义行为

	return 0;
}

4.位操作符

也是操作二进制位
在这里插入图片描述


4.1 &按位与 计算方法

int main()
{
	int a = 3;
	//00000000000000000000000000000011 - 补码
	int b = -5;
	//10000000000000000000000000000101
	//11111111111111111111111111111010 - 反码
	//11111111111111111111111111111011 - 补码
	
	int c = a & b;
	//& -- 对应二进制位有0则为0,两个同时为1,才是1
	//00000000000000000000000000000011 -a补码
	//11111111111111111111111111111011 -b补码
	//00000000000000000000000000000011 -c补码
	//符号位是0为正数,原反补相同
	
	printf("%d\n", c);//计算的是原码,c=3

	return 0;
}

4.2 | 按位或 计算方法

 int main()
{
	int a = 3;
	//00000000000000000000000000000011 - 补码
	int b = -5;
	//10000000000000000000000000000101
	//11111111111111111111111111111010
	//11111111111111111111111111111011 - 补码
	//
	int c = a | b;
	//| - 按(2进制)位或 - 对应的二进制位有1则为1,两个同时为0才是0
	//00000000000000000000000000000011 - a补码
	//11111111111111111111111111111011 - b补码
	//11111111111111111111111111111011 - 得到c补码
	//11111111111111111111111111111010 - 反码
	//10000000000000000000000000000101 - 原码 -5
	printf("%d\n", c);//-5

	return 0;
}

4.3 ^ 按位异或计算方法

int main()
{
	int a = 3;
	//00000000000000000000000000000011 - 补码
	int b = -5;
	//10000000000000000000000000000101 - 原码
	//11111111111111111111111111111010 - 反码
	//11111111111111111111111111111011 - 补码
	//
	int c = a ^ b;
	//^ - 按(二进制)位异或 -对应的二进制位相同为0,相异为1
	//00000000000000000000000000000011 -a补码
	//11111111111111111111111111111011 -b补码
	//11111111111111111111111111111000 -得到c补码
	//11111111111111111111111111110111 -c反码
	//10000000000000000000000000001000 -c原码 -8
	printf("%d\n", c);//-8

	return 0;
}

举一个题目看看是怎么应用叭:

不能创建临时变量(第三个变量),如何实现两个数的交换?

交换2个整型变量,正常情况下是这样写的:

int main()
{
	int a = 3;
	int b = 5;
	printf("交换前:a=%d b=%d\n", a, b);
	int tmp = a;
	a = b;
	b = tmp;
	printf("交换后:a=%d b=%d\n", a, b);
	
	return 0;
}

而题目要求不能创建临时变量,我们可以这样写:

int main()
{
	int a = 3;
	int b = 5;
	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;
}

但缺点就是容易溢出

也有第③种写法,运用到 ^ 的知识

int main()
{
	int a = 3;
	int b = 5;
	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;
}

如下图说明:
在这里插入图片描述

用异或实现交换的原理就是:
1.两个相同的数异或为0
2.任何数异或0都等于那个数

但这种例子可读性不高,效率也不如第一种好。

5.赋值操作符

赋值操作符是一个很棒的操作符,它可以让你得到一个你之前不满意的值,也就是你可以给自己重新赋值。

int weight = 120;//体重
weight = 89;//不满意就赋值
double salary = 10000.0;
salary = 20000.0;//使用赋值操作符赋值

赋值操作符可以连续使用,比如:
int a = 10
int x = 0;
int y = 20;
a = x = y+1;//连续赋值(先把y+1的结果赋给x,再把x的结果赋给a)
但更推荐下面这种赋值方式:
x = y+1;
a=x;
这样的写法是不是更加清晰爽朗而且易于调试。


5.1 复合赋值符

+=
-=
*=
/=
%=
>>=
<<=
&=
|=
^=

这些运算符都可以写成复合的效果
比如:

int x = 10;
x = x+10;
x += 10;//复合赋值
//其他运算符一样的道理。这样写更加简洁

6. 单目操作符

只有一个操作数

6.1 单目操作符介绍

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

! 逻辑反操作 (把假变成真,把真变成假)

int main()
{
	int flag = 0;//假
	if (flag == 0)//为真,打印hehe
	{
		printf("hehe\n");
	}
	if (!flag)//flag为假,但!flag为真,打印hehe
	{
		printf("hehe\n");
	}
	if (flag)//假,不打印
	{
		printf("haha\n");
	}
	return 0;
}

  -负值   +正值
int main()
{
	int a = 5;
	int b = -a;
	printf("%d\n", b);//b=-5
	return 0;
}

& * 应用于指针

int main()
{
	int a = 10;
	//pa是指针变量
	int* pa = &a;//&-取地址操作符 - 取出a的地址 - 存在pa里-pa的类型写成int * - *告诉我pa是指针变量 - int告诉我pa指向的那个对象a的类型是int 

	*pa = 20;//解引用操作符(间接访问操作符)-单目操作符-通用pa中存放的地址,找到指向的空间(内容)
	int c = *pa;

	return 0;
}

sizeof

int main()
{
	int a = 10;
	printf("%d\n", sizeof(int));//4
	❌错误写法:printf("%d\n", sizeof int);//类型时括号不能去掉
	
	printf("%d\n", sizeof(a));//4
	printf("%d\n", sizeof a);//括号可以去掉,说明sizeof不是函数-函数后面的括号不能去掉

	return 0;
}

sizeof还可以计算数组大小

int main()
{
	int arr[10] = {0};
	printf("%d\n", sizeof(arr));//40 - 数组名表示单独放在sizeof内部,计算整个数组的大小单位字节
	printf("%d\n", sizeof(int [10]));//40
	return 0;
}

~ 按补码二进制位取反

int main()
{
	int a = 0;//正数
	printf("%d\n", ~a);
	//00000000000000000000000000000000 -a补码
	//11111111111111111111111111111111 - ~a补码
	//11111111111111111111111111111110 - ~a补码-1得到反码
	//10000000000000000000000000000001 - ~a反码按位取反得到原码
	return 0;
}

二进制位的综合运用:

#include<stdio.h>
int main()
{
	int a = 13;
            	/00000000000000000000000000001101/00000000000000000000000000010000//1左移4位得到②
	            /把①倒数第五位数字加上1变成00000000000000000000000000011101③
	a |= (1<<4);//①按位或②
	printf("%d\n", a);
	            /00000000000000000000000000011101/变成③之后又想变回①
	            /11111111111111111111111111101111/ 则③要按位与&⑤得到①
                /00000000000000000000000000001101/而⑤是怎么得呢?是②~按位取反得到的

	a &= (~(1 << 4));
	printf("%d\n", a);

	return 0;
}

 --前置、后置 --
 ++前置、后置++

后置++

int main()
{
	int a = 1;
	int b = a++;//后置++,先使用,后++(先把a的值赋给b,a再+1)
	//b=a,a=a+1
	printf("a=%d b=%d\n", a, b);//2 1
	return 0;
}

后置- -

int main()
{
	int a = 1;
	int b = a--;//后置--,先使用,后--(先把a的值赋给b,a再-1)
	//b=a,a=a-1
	printf("a=%d b=%d\n", a, b);//0 1
	return 0;
}

前置++

int main()
{
	int a = 1;
	int b = ++a;//前置++,先++,后使用(a先+1,再把a的值赋给b)
	//a=a+1,b=a
	printf("a=%d b=%d\n", a, b);//2 2
	return 0;
}

前置- -

int main()
{
	int a = 1;
	int b = --a;//前置--,先--,后使用(a先-1,再把a的值赋给b)
	//a=a-1,b=a
	printf("a=%d b=%d\n", a, b);//0 0
	return 0;
}


(类型) 强制类型转换
建议能不用则不用,强扭的瓜不甜。

int main()
{
	int a = (int)3.14;//3.14是double类型,在前面加(类型),就转换类型了
	printf("%d\n", a);//打印3,因为是整型,不保留小数
	// int a = int(3.14)//err
	return 0;
}

7.关系操作符

 >
 >=
 <
 <=
 !=                    用于测试“不相等”
 ==                   用于测试“相等”

8.逻辑操作符

&&                         逻辑与(并且)
||                         逻辑或(或者)

练习①

int main()
{
	int a = 0;
	int b = 0;
	scanf("%d %d", &a, &b);
	//a 和 b 都是5,打印 hehe
	if (a == 5 && b == 5)
	{
		printf("hehe\n");
	}
	//a 或者 b是5 打印哈哈
	if (a == 5 || b == 5)
	{
		printf("haha\n");
	}
	return 0;
}

练习②

打印闰年
int main()
{
	int y = 0;
	scanf("%d", &y);
	//1. 能被4整除,并且不能被100整除
	//2. 能被400整除是闰年
	if (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0))
	{
		printf("闰年\n");
	}
	else
	{
		printf("不是闰年\n");
	}
	return 0;
}

练习③

int main()
{
	int i = 0, a = 0, b = 2, c = 3, d = 4;
	i = a++ && ++b && d++;
    printf("a = %d\nb = %d\nc = %d\nd = %d\n", a, b, c, d);//1234

	return 0;
}


打印结果依然是1234,为什么bcd的值没变呢?
注意:&&操作符有一个规则,左边为假,右边就不计算了!
在这里插入图片描述


int main()
{
	int i = 0, a =1 , b = 2, c = 3, d = 4;
	i = a++ && ++b && d++;
    printf("a = %d\nb = %d\nc = %d\nd = %d\n", a, b, c, d);

	return 0;
}

打印结果为2335,因为a=1为真,右边才会计算,b=3,又因为a,b都为真,d才会计算,d=5


int main()
{
	int i = 0, a = 1, b = 2, c = 3, d = 4;

    i = a++||++b||d++;
	
	printf("a = %d\nb = %d\nc = %d\nd = %d\n", a, b, c, d);//
	return 0;
}

a++,先使用,后++,所以a=1,为真,进入下面的打印,后面的便不再计算。
注意:|| 这个操作符的左边为真,右边不再计算

int main()
{
	int i = 0, a =0, b = 2, c = 3, d = 4;

    i = a++||++b||d++;
	
	printf("a = %d\nb = %d\nc = %d\nd = %d\n", a, b, c, d);//1334
	return 0;
}

a为0,a++(先使用,后++)所以a为假,计算b,++b(先++,后使用),b=3,为真,a||b为真,d不计算,进入打印。


注意:逻辑操作符 && || ! 计算结果是真,使用1表示

int main()
{
	int a = 3 && 5;
	printf("%d\n", a);//a=1

	return 0;
}

9.条件操作符

也叫三木操作符(有三个操作数)
像a+b,有两个数a和b,就是双目操作符
!a,是单目操作符
在这里插入图片描述
看下面的例子

判断较大值

int main()
{
	int a = 0;
	int b = 0;
	int m = 0;
	scanf("%d%d", &a, &b);
	if (a > b)
		m = a;
	else
		m = b;

	printf("%d\n", m);
	return 0;
}

而用三木操作符表示会更加简洁明了

int main()
{
	int a = 0;
	int b = 0;
	int m = 0;
	scanf("%d%d", &a, &b);

	m = (a > b ? a : b);

	printf("%d\n", m);
	return 0;
}

10.逗号表达式

exp1,exp2,exp3,…expN
逗号表达式:从左向右计算,整个表达式的结果是最后一个表达式的结果!

举例:

int main()
{
	int a = 1;
	int b = 2;
	int c = (a > b, a = b + 10, a, b = a + 1);//逗号表达式
	printf("%d\n", a);
	printf("%d\n", b);
	printf("%d\n", c);

	return 0;
}

a = get_val();
count_val(a);
while (a > 0)
{
	a = get_val();
	count_val(a);


}

如果使用逗号表达式,改写:

while (a = get_val(), count_val(a), a > 0)
{

}

11.下标引用,函数调用和结构成员

11.1 [ ]下标引用操作符

操作数:一个数组名+一个索引值

举例:

int main()
{
	int arr[10] = { 1,2,3,4,5 };
	//              0 1 2 3 4 5 6 7 8 9
	//数组的起始是有下标的,下标是0开始的
	printf("%d\n", arr[2]);//[] 下标引用操作符,arr 和 2 是[] 的两个操作数
	//3 + 5
	//
	return 0;
}

11.2()函数调用操作符

接受一个或多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数

举例:

#include <string.h>

int Add(int x, int y)
{
	return x + y;
}

void test()
{
	printf("hehe\n");
}
int main()
{
	//int len = strlen("abc");//() 函数调用操作符
	//() 的操作数是:strlen "abc"
	//printf("%d\n", len);

	int c = Add(3, 5);//() 函数调用操作符
	//Add 3 5
	//对于函数调用操作符来说,最少有1个操作数

	printf("%d\n", c);

	test();
	return 0;
}

11.3 访问一个结构的成员

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

结构体叫复杂类型也叫自定义类型
目前C语言给我们提供了char、short、long long、int long、float、double类型,这些类型我们也可以叫内置类型,是C语言本身就具有的这些类型,但是这些类型不能表达我们生活中所有的事情,如果我们要描述一本书,要知道书名,定价,作者,分类,出版社,书号等等。要涉及这么多复杂类型,所以C语言就给出了一个结构体类型,允许我们可以创造一个类型, 它描述的对象是复杂的,我们也可以叫做自定义类型。

那么就可以给一个struct,这是C语言给的一种语法形式,然后定义一个书的类型,起名叫Book,下面就给一个大括号,里面就可以输入一些描述书的属性。比如书名是一个字符串,一个字符串要存起来,要放到一个字符数组里面,那么就可以输入char name[20]; 作者亦如此,输入char author[20];定价是一个价格,有整数小数,那么可以用一个float类型,输入float price;
创建好了结构体,就要创建变量了,struct Book就是类型名,拿起它创建一个变量b1,就是一本书,因为里面有很多属性,假设给结构体初始化,给一个大括号{},里面就可以输入书名,作者,定价,特别要注意,如果定价输入小数则要加一个f,如66.5f,表示这个数字是float类型接下来就是打印,书名、作者是字符串用%s,定价用%f,要写清打印哪本书,在属性前面写上b1.,如 b1.name。这时候就运用到了.操作符

如果只要小数点后一位就在%f前面输入.1,保留两位就加上.2
如下所示:

 .操作符
struct Book
{
	char name[30];//成员
	char author[20];
	float price;
};


int main()
{
	struct Book b1 = {"C语言第一课", "鹏哥", 66.5f};//书
	struct Book b2 = {"数据结构第一课", "杭哥", 88.6f};//书
	printf("%s %s %.1f\n", b1.name, b1.author, b1.price);
	printf("%s %s %.1f\n", b2.name, b2.author, b2.price);

	//结构体变量.成员名
	return 0;
}


上面是第一种方式,还有第二种方式:

分装一个函数print();打印b1里面的信息,&b1的地址取出来给print();函数大括号里面就写上b1的类型struct Book,取出来的地址用* p接收,*表示p是一个指针,知道p指向b1时,要找到p,那么对指针解引用就是找到指针所指向的那个对象,所以用(*p).name。也有更简便的写法:p->name,意思时p所指向的那个对象

如下所示:

 ->操作符
struct Book
{
	char name[30];//成员
	char author[20];
	float price;
};

void Print(struct Book * p)
{
	printf("%s %s %.1f\n", (*p).name, (*p).author, (*p).price);
	printf("%s %s %.1f\n", p->name, p->author, p->price);
	//->
	//结构指针->成员名
}

int main()
{
	struct Book b1 = { "C语言第一课", "鹏哥", 66.5f };//书
	struct Book b2 = { "数据结构第一课", "杭哥", 88.6f };//书
	Print(&b1);
	Print(&b2);
  
	return 0;
}

12.表达式求值

操作符时如何影响表达式求值的?

表达式求值的顺序一部分时由操作符的优先级和结合性决定。
同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型。

要考虑两个问题:
1.表达式在计算的过程中,有哪些类型转换?
2.表达式的求值顺序是怎样的?


12.1隐式类型转换

类型转换分为两种:整型提升和算术转换

整型提升:
C的整型算术运算总是至少默认以整型类型的精度来进行的。

为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。

举例;

int main()
{
	char c1 = 5;
	char c2 = 127;
	
	char c3 = c1 + c2;
	
	printf("%d\n", c3);
	
	return 0;
}

c1,c2,c3都是char类型,char类型是一个字节,而为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,普通整形是4个字节,感觉它变大了,提升了,
所以要注意,所谓的整型提升是针对字符和短整型,这件事情是隐形发生的,我们没有看到而已。

整型提升的意义

表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度-般就是int的字节长度,同时也是CPU的通用寄存器的长度。

因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。

通用CPU (general-purpose
CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned
int,然后才能送入CPU去执行运算。

也就是说:一个char类型的数据或者short类型的数据,当我们把它放在一个整型的空间里的时候,足以能放得下,而且这个过程中会产生静位移向,char类型是8个bit位,两个char类型的数据进行相加的时候,如果产生静位,有些位可能就丢了,但如果把一个char类型的数据或者一个短整型的数据放在一个整型的空间里面去,整型是32个bit位,在计算的过程中及时产生静位,它也不会丢,他只会放到更高位上去,空间是足够的,这样使得计算的精度会更高更准确些。

如何进行整型提升呢?

整型提升是按照变量的数据类型的符号来提升的

int main()
{
	char c1 = 5;
	//00000000000000000000000000000101
	//00000101 - c1(补码)   char是8个bit位,所以只存了后面8个,叫做截断
	char c2 = 127;
	//00000000000000000000000001111111
	//01111111 - c2 (补码)亦如此
	char c3 = c1 + c2;
	//c1,c2是char类型,是有符号的char,所以要进行整型提升,
	//那存的8个bit位里最高位就是符号位了,按照符号位补,所以就咔咔补够32个bit位
	//00000000000000000000000000000101  -c1
	//00000000000000000000000001111111  -c2
	//00000000000000000000000010000100  -整型提升后相加
	//char类型c3又放不下这么多,又发生截断⬇
	//10000100 - c3(补码)

	printf("%d\n", c3);
	//%d - 10进制的形式打印有符号的整数,c3是cahr类型,
	//所以要进行整型提升,符号位是1,咔咔补完1上去,但这是补码,计算的是原码
	//11111111111111111111111110000100 - 补码
	//11111111111111111111111110000011 - 反码
	//10000000000000000000000001111100 --> -124
	return 0;
}

12.2 算术转换

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

long double
double
float
unsigned long int
long int 
unsigned int 
int 

在这里插入图片描述
如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运算

上面这些类型都是>=4个字节
这些类型的表达式在计算的时候,是向上转换的, 如果表达式中有int和float类型,会把int转换成float

比如:
float f=3.14f;
int n =10;
f+n,会把n转换成float,算出float的结果
这就是寻常算数转化

如果是float和double在计算的时候会把float转换成double
long int和double在计算的时候会把long int 转换成double

12.3 操作符的属性

复杂表达式的求值有三个影响的因素
1.操作符的优先级
2.操作符的结合性
3.是否控制求值顺序


1、2 操作符的优先级和结合性

两个相邻的操作符先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。
比如:

/ 相邻操作符优先级高的先算,低的后算
/相邻操作符的优先级相同的情况下,结合性起作用

int main()
{
	int a = 2 + 3 + 5;

	return 0;
}

3.是否控制求值顺序

两个加号无法得知优先级,加法结合性是L-R,所以是从左往右计算。

逗号表达式,从左向右依次计算,只有最后一个表达式的结果才是整个表达式的结果。
逻辑与&&,左边为假,右边不计算了,在一定程度上影响求值顺序。
逻辑或||,左边为真,右边不计算了。
条件操作符?:,表达式一为真,表达式二算,表达式三不算;表达式一为假,表达式二不算,表达式三算。

这些都会影响求值顺序


但是按照表达式这三个属性就一定能求出某个表达式唯一的计算路径吗,一定能算出一个固定的结果吗?
答案是不一定

所以就产生了一些问题代码

/表达式1
 a*b+c*d+e*f

计算顺序有两种:

在这里插入图片描述

在这里插入图片描述

注释:代码1在计算的时候,由于比+的优先级高,只能保证 * 的计算是比+早,但是优先级并不是能决定第三个比第一个+早执行


/表达式2
c + --c;

在这里插入图片描述
算出两种结果
假设int c=5;
①如果先准备左边的为5,那么5+4=9;
②如果先计算右边的c,- -c,c变成4,4+4=8;


/表达式3
int fun()
{
    static int count =1;
    return ++count;
}
int main()
{
    int answer;
    answer = fun() - fun() * fun();
    printf("%d\n",answer);/输出多少?
    return 0}

调用fun函数,函数里有static修饰的变量,这个变量的特点是:出了函数不销毁,继续保持上次留下的值,所以三次调用count返回为2、3、4 ,但先调用那个函数却产生了歧义

第①种结果: 4-2 * 3 = -2
在这里插入图片描述
第②种结果:2-3 * 4 = -10
在这里插入图片描述

总结

我们写出的表达式如果不能通过操作符的属性确定唯一的计算路径,那这个表达式就是存在问题的。


₍ᵋꏿ৺ꏿᵌ₎
操作符知识点到此结束啦(撒花撒花~)
★,°:.☆( ̄▽ ̄)/$:.°★

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值