【C语言】万字详讲操作符

目录

前言

一、操作符分类

二、算数操作符

三、移位操作符

四、位操作符

五、赋值操作符

六、单目操作符

6.1 逻辑反操作

6.2 负值与正值

6.3 取地址

6.4 sizeof

6.5 取反操作符

6.6 --和++操作符

6.7 间接访问操作符(解引用操作符)

6.8 强制类型转换

七、关系操作符

八、逻辑操作符

九、条件操作符

十、逗号表达式

十一、下标引用、函数调用和结构成员

11.1 [] 下标引用操作符

11.2  () 函数调用操作符

11.3 访问结构成员操作符

十二、表达式求值

12.1 隐式类型转换

12.2 算数转换

12.3 操作符的属性

总结


前言

这篇文章将对C语言的操作符相关知识点,进行详细的讲解,读完本篇文章,您对C语言的了解将更进一步。


一、操作符分类

C语言中对于操作符一共分为十类:(进行简单介绍)

  • 算数操作符:加减乘除取余
  • 移位操作符:左移(<<),右移(>>)
  • 位操作符:按位与(&),按为或(|),按位异或(^)
  • 赋值操作符:简单赋值:=;复合赋值:与前三类结合
  • 单目操作符:sizeof、取反~、取非!、+、-、++、--、*、强制类型转换()
  • 关系操作符:大于等于小于等等
  • 逻辑操作符:逻辑与(&&),逻辑或(||)
  • 条件操作符:也就是三目运算符
  • 逗号表达式:多个表达式由逗号分隔
  • 下标引用、函数调用和结构成员:点、->、()、[]

二、算数操作符

算数操作符,也就是对应数学上的算数运算符,最常见的为:加减乘除。

不过在C语言中,还会用到取模(取余)操作,算出操作符的书写形式如下:

+:加
-:减
*:乘
/:除
%:取余

可以看出,加减书写方式与数学中是一样的,乘除与数学中的不同,这里重点讲解一下取余操作符:

#include<stdio.h>
// 算数操作符示例代码:
int main()
{int a = 10;int b = 3;printf("加法:%d\n", a + b);printf("减法:%d\n", a - b);printf("除法:%d\n", a / b);printf("取余法:%d\n", a % b);return 0;
}

通过结果可知:

  • 除法的结果是商。
  • 取余的结果是余数

我们再看看几个例子:

对于 / 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。

加减乘除都可以用在不同类型中,那取余操作符呢?

我们可以得出:

  • 除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数。
  • % 操作符的两个操作数必须为整数。返回的是整除之后的余数。

三、移位操作符

<<:左移
>>:右移

关于移位操作符,我们先要了解一些前备知识点。

移位,移的是什么位呢?其实是二进制的位,那二进制是什么呢?

日常生活中,我们看到的数字是用十进制形式表示的,十进制的数据都是由0~9的数字组成的。相同的,二进制是常用在计算机中,由0~1的数字组成。当然,还有8进制、16进制等等,原理相同。

列如,我们算一下10的二进制,要算10的二进制,这里就涉及到如何将10进制转为二进制的知识:

10进制转二进制,我们常用除2取余的方法:

那将二进制转为其它进制呢?方法是权重法

回归主题,在C语言中,数据是以二进制的形式存储在内存中的,而移位操作符,正是对整数的二进制位进行移动。

既然是对整数的二进制位操作,那我们就需要了解整数的二进制是怎么样的呢?

整数的二进制表示形式有3种:原码、反码、补码

C语言规定:

  • 正数的原反补码都一样,而负数的原反补不一样,要通过计算。
  • 整数在内存中以补码的形式进行存储,对二进制位操作时,也是针对补码,但最终显示给我们看的是原码形式,怎么理解呢?
  • 一个整形有32个二进制位,其中第32个位是符号位:正数符号位为0,负数符号位为1。
  • 例如int a = -2; 显示给我们看的为-2,即二进制为:符号位(1).....010,但如果我们打印&a -2的地址,我们看到的与我们计算出来的不一样,这就是在内存中以补码形式进行存储,显示时以原码形式。

我们对-2进行图解,讲解原反补的概念:

我们也可以通过编译器来看看:-5在编译器中存储

 

既然内存中以补码形式存储,对于正数来说,原反补都一样,那负数进行二进制操作后,如何将补码转回原码呢?

两种方法:

  1. 补码-1取反
  2. 补码取反+1

<<左移操作符:二进制位相左移动,右边补0。

对a的二进制位进行操作,a的值会改变吗?并不会,这就跟int a = 2; int b = a+2;操作一样,并不会改变a的值,如果要改变,必须对自身赋值。


>>右移操作符:有两种:

  1. 算数右移:右边丢弃,左边用原理的符号位补充(0为正数、1为负数)
  2. 逻辑右移:右边丢弃,左边用0填充。

具体那种形式,是由编译器决定的,对于大多数编译器来说,用的是算数右移,具体可以看图解:

最后,关于移位操作符有两点需要注意:

  • 移位操作符的操作数只能是整数。
  • 对于移动运算符,不要移动负数位,这个是标准未定义的。如:a << -1、a >> -2 .....

四、位操作符

&:按位与
|:按位或
^:按位异或
注:他们的操作数必须是整数

关于位操作符,从名字就能猜出,是针对于二进制位的操作,具体作用如下:

&:一个为0,都为0,两个为1才为1

|:一个为1,都为1,两个为0才为0

^:相同为0,相异为1

代码+图解:

//位操作符
int main()
{int num1 = 2;int num2 = 3;int num3 = num1 & num2;int num4 = num1 | num2;int num5 = num1 ^ num2;printf("&:%d\n", num3);printf("|:%d\n", num4);printf("^:%d\n", num5);return 0;
}

按位异或的三个特点:

  • 两个相同值进行异或等于0,因为相同为0
  • 任何数与0异或等于本身,因为0遇到1相异为1,遇到0,相同为0,还是本身的二进制
  • 支持交换律,如:a^a^b == a^b^a

五、赋值操作符

赋值操作符是C语言用的最多的一个操作符,没什么好讲的,就是简单的赋值操作。

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;//连续赋值
这样的代码感觉怎么样?
那同样的语义,你看看:
x = y+1;
a = x;
这样的写法是不是更加清晰爽朗而且易于调试。

赋值操作符还可以与其它操作符结合,称为复合赋值符:其实自己对自己操作后,赋值给自己

+=
-=
*=
/=
%=
>>=
<<=
&=
|=
^=
int x = 10;
x = x+10;
x += 10;//复合赋值
//其他运算符一样的道理。这样写更加简洁。

这里要区分一个点:赋值和初始化是不同的:

  • 创建一个变量的同时又赋值:初始化
  • 创建后,再赋值:赋值

六、单目操作符

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

6.1 逻辑反操作

! 逻辑反操作符,只有两种结果:真变假,假变真,看代码:

//逻辑反操作
int main()
{int a = 0;if (!a) //当a为假时执行{;}return 0;
}

6.2 负值与正值

跟数学中的正负符号相同。不常用,看代码:

//正、负值
int main()
{//将a变为-数int a = 2;int a = -a;return 0;
}

6.3 取地址

&取地址操作符常用在获取一个数据的地址,常跟指针搭配使用,看代码:

//&取地址
int main()
{//查看a的地址int a = 2;int* p = &a;printf("%p\n", p);return 0;
}

6.4 sizeof

sizeof是一个单目操作符,不是一个库函数哦,其作用是求类型的字节大小。看代码理解:

#include <stdio.h>
int main()
{int a = -10;printf("%d\n", sizeof(a));printf("%d\n", sizeof(int));printf("%d\n", sizeof a);//这样写行不行?printf("%d\n", sizeof int);//这样写行不行?return 0;
}

sizeof(a)或sizeof(int) 都是正确的。还有一种特殊的写法:sizeof a , 就是()里放的是变量名时,可以把()去掉,但如果是类型,()不可以去掉。


6.5 取反操作符

~ 取反操作符是针对二进制位操作的,其作用是将二进制的每一位取反,0变1,1变0,如:

int a = 2;
a:  010
~a: 101

这里有一个在做OJ题常见的代码:

while(~scanf("%d", &n))

代码中的~操作怎么理解呢?

因为scanf的返回值是格式化符的个数,当scanf读取失败时,会返回EOF,EOF为-1,
那对-1按位取反,那结果就为0,0为假,则不会进入循环


6.6 --和++操作符

--和++操作符分为前置与后置,下面讲解前置和后置有什么区别:

前置:先++/--后使用。

后置:先使用后++/--

具体看代码:

//++和--运算符
//前置++和--
#include <stdio.h>
int main()
{int a = 10;int x = ++a;//先对a进行自增,然后对使用a,也就是表达式的值是a自增之后的值。x为11。int y = --a;//先对a进行自减,然后对使用a,也就是表达式的值是a自减之后的值。y为10;return 0;
}
//后置++和--
#include <stdio.h>
int main()
{int a = 10;int x = a++;//先对a先使用,再增加,这样x的值是10;之后a变成11;int y = a--;//先对a先使用,再自减,这样y的值是11;之后a变成10;return 0;
}

我们看看这段代码:

int a = 1;
int b = (++a) + (++a) + (++a)

这是一段问题代码,在不同的编译器上运行的结果是不同的,因此对于++、--操作符,我们尽量不要在一个表达式中使用多次,这样会让代码阅读变差。


6.7 间接访问操作符(解引用操作符)

* 间接引用操作符用是指针的一种标志,跟随指针一起出现。

//*简接引用操作符
int main()
{int a = 2;//定义指针变量Pint* p = &a;//访问a的值printf("%d\n", *p);return 0;
}

6.8 强制类型转换

(类型)强制类型转换通常用在对不同类型变量之间,想进行赋值操作时。

//强制类型转换
int main()
{float b = 3.0f;int res = (int)b;printf("%d\n", res);return 0;
}

对于强制类型转换,有两点要注意:

  • 建议小转大,大转小会丢失精度
  • 强制类型转化是一种临时的状态,不是永久的

七、关系操作符

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

这些运算符没什么讲的,需要注意:

  • 在编程的过程中== 和=不小心写错,导致的错误。
  • 两个等号为等与、一个等号为赋值

八、逻辑操作符

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

逻辑操作符的结果只有两种:真或假。

注意区分与按位与和按位或的区别:

按位与和按位或对二进制位操作。
逻辑与和逻辑或关注的是真和假1&2----->0
1&&2---->11|2----->3
1||2---->1

我们看一到360的笔试题:

#include <stdio.h>
int main()
{int i = 0,a=0,b=2,c =3,d=4;i = a++ && ++b && d++;//i = a++||++b||d++;printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);return 0;
}
//程序输出的结果是什么?

如何计算这种表达式呢?

当多个||时,当从前往后执行,当发现前面的为1时,后面不需要计算了,均为1,如:(|| - 两个为假,式子为假,1个为真,式子才为真)
int i = 0, a = 1, b = 2, c = 3, d = 5
i = a++ || ++b || d++
当a==1时,就不需要执行后面的式子了,均为1,因此,当发现前面为1时,后面不会执行

将代码改为||呢?

#include <stdio.h>
int main()
{int i = 0,a=0,b=2,c =3,d=4;i = a++ && ++b && d++;//i = a++&&++b&&d++;printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);return 0;
}
//程序输出的结果是什么?
当多个&&时,当从前往后执行,当发现前面的为0时,后面不需要计算了,均为0,如:(&& 一个为假,式子为假,两个为真,式子才为真)
int i = 0, a = 0, b = 2, c = 3, d = 5
i = a++ && ++b && d++
当a==0时,就不需要执行后面的式子了,均为0,因此,当发现前面为0时,后面不会执行

因此,我们可以得出结论:

  • &&操作符左边为假时,右边不会执行,表达式为假。
  • ||操作符左边为1时,右边不会执行,表达式为真
  • 这种现象也叫短路

九、条件操作符

//条件操作符(三目运算符)
exp1 ? exp2 : exp3

首先看exp1表达式结果,如果为真则表达式值为exp2,如果为假,表达式的值为exp3。

if (a > 5)b = 3;
elseb = -3;
转换成条件表达式,是什么样?
int a = 2;
int b = 0;
int res = a>5 ? b=3 : b-3
res值为-3.
b的值为-3.

我们做个练习,使用条件表达式实现找两个数中较大值。

//使用条件表达式实现找两个数中较大值。
int main()
{int a = 10;int b = 20;int max = a > b ? a : b;printf("max: %d\n", max);return 0;
}


十、逗号表达式

exp1, exp2, exp3,....expN

逗号表达式的特点:

  • 逗号表达式,就是用逗号隔开的多个表达式。
  • 逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。

例如以下几个例子:

//代码1
int a = 1;
int b = 2;
int c = (a>b, a=b+10, a, b=a+1);//逗号表达式
c是多少?

c为13。从左向右依次计算,到在最后一个表达式时,a的值已变为12,最后整个表达式的值是最后一个表达式,因此c为13.

//代码2
if (a =b + 1, c=a / 2, d > 0)

从左向右执行,代码2中的逗号表达式并没有直接与最后一个表达式关联的变量,因此判断条件仍然为d > 0.

//代码3
a = get_val();
count_val(a);
while (a > 0)
{//业务处理a = get_val();count_val(a);
}如果使用逗号表达式,改写:
while (a = get_val(), count_val(a), a>0)
{//业务处理
}

从代码3可以看出,如果学会逗号表达式,可以大大提高代码书写效率。


十一、下标引用、函数调用和结构成员

11.1 [] 下标引用操作符

什么是操作数?操作数就是操作符作用的对象,如:3&&2,则3和2就是&&的操作数。那下标引用操作符的操作数是什么呢?

操作数:一个数组名 + 一个索引值。[]有两种用法如下:

int arr[10];//创建数组arr[9] = 10;//实用下标引用操作符。[ ]的两个操作数是arr和9。

11.2  () 函数调用操作符

其实就是调用函数时后边的圆括号 

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

#include <stdio.h>void test1(){printf("hehe\n");}void test2(const char *str){printf("%s\n", str);}int main(){test1();            //实用()作为函数调用操作符。test2("hello bit.");//实用()作为函数调用操作符。return 0;}

11.3 访问结构成员操作符

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

结构体变量.结构成员名,常用于获取结构体变量形式的结构体成员,如以下代码:

//结构体变量.结构体成员名
struct S
{int a;
};
int main()
{struct S s = { 20 };printf("%d", s.a);return 0;
}

结构体指针.结构成员名,常用于结构体指针形式的结构体成员,如以下代码:

struct S
{int a;
}s;
void test(struct S* p)
{//当然,也可以写成结构体变量.结构体成员名形式printf("%d\n", (*p).a);//结构体指针.结构体成员名,更加方便printf("%d\n", p->a);
}
int main()
{test(&s);return 0;
}

十二、表达式求值

关于C语言的操作符已经介绍完毕,学习了操作符,其实主要目的就是写出相对应的表达式,但是那么多种操作符如何进行组合呢?组合之后又该怎么确定谁先运行或者运行的方向是怎么样的呢?

关于表达式求值,有两点需要注意:

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

12.1 隐式类型转换

什么是隐式类型转换,通俗点说就是当char类型和short类型进行算术运算时,程序会隐式的对算数对象进行类型的提升,提升为int型,进行算术运算,具体的看如下讲解:

C的整型算术运算总是至少以缺省整型类型的精度来进行的。

什么意思呢?通俗点讲就是在C语言中,进行整型算术运算时,总是以int类型进行的,也就是说,如果我们对char或short类型进行加减乘除运算的话,会默认将类型变为int来进行。

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

整型提升有什么意义呢?

先看一大段概念:

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

通俗讲解:

我们通过几个例子进行说明:

char a = 3;
char b = 127;
char c = a+ b;

两个char类型的变量进行相加,程序会隐式的对这两个变量提升为int型,如何提升呢?

我们知道char类型是1字节,也就是8个二进制位,而int是4字节,也就是32比特位,整型提升的提升本质上是提升二进制位:

 

a和b的值被提升为普通整型,然后在执行加法运行,计算完之后,发现接受的变量c是一个char类型,则需要进行截断操作,也就是只保留8个二进制位:


那么整型提升的规定是什么呢?

  • 有符号位(也就是类型前不加unsigned):
  1. 按照变量的数据类型的符号位来提升
  2. 正数符号位为0,那就将不足的二进制填充为0
  3. 负数符号位为1,那就将不足的二进制填充为1
  • 无符号位(也就是类型前加了unsigned):
  1. 直接高位补0(也就是剩余的二进制位填充0)
//负数的整形提升
char c1 = -1;
变量c1的二进制位(补码)中只有8个比特位:
1111111
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为1
提升之后的结果是:
11111111111111111111111111111111
//正数的整形提升
char c2 = 1;
变量c2的二进制位(补码)中只有8个比特位:
00000001
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为0
提升之后的结果是:
00000000000000000000000000000001
//无符号整形提升,高位补0

看看下面这两个实例:

//实例1
int main()
{char a = 0xb6;short b = 0xb600;int c = 0xb6000000;if(a==0xb6)printf("a");if(b==0xb600)printf("b");if(c==0xb6000000)printf("c");return 0;
}

实例1中的a,b要进行整形提升,但是c不需要整形提升 a,b整形提升之后,变成了负数,所以表达式 a==0xb6 , b==0xb600 的结果是假,但是c不发生整形提升,则表 达式 c==0xb6000000 的结果是真. 

//实例2
int main()
{char c = 1;printf("%u\n", sizeof(c));printf("%u\n", sizeof(+c));printf("%u\n", sizeof(-c));return 0;
}

sizeof求类型的字节大小,单位为字节,为什么得出的大小不同呢?其实也是整型提升的原因,整型提升是针对char类型和short类型表达式的,注意!!是表达式,那+c和-c自然也是表达式了,那就被整型提升成了int类型,因此打印出4.


12.2 算数转换

C语言中不只有隐式转换,隐式转换是针对于小于Intl类型的转换,对象为char类型和short类型,那对于等于int类型或大于int类型的类型呢?就要用到算数转换了。

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

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

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

float f = 3.14;
int num = f;//隐式转换,会有精度丢失
num最终为3,丢失掉了0.14

12.3 操作符的属性

上文提到过,表达式求值有三个影响的因素:

  1. 操作符的优先级(就是先后执行的意思)
  2. 操作符的结合性(就是执行方向,是从左到右还是从右到左)
  3. 是否控制求值顺序(就是某个条件下,操作数可能不会执行,如:&&、||、条件表达式、逗号表达式)

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

可以参考以下这份表格:

C语言操作符表
操作符描述用法示例结构类型结合性是否控制求值顺序
()聚组(表达式)与表达 式同N/A
()函数调用rexp(rexp,...,rexp)rexpL-R
[ ]下标引用rexp[rexp]lexpL-R
.访问结构成员lexp.member_namelexpL-R
->访问结构指针成员rexp->member_namelexpL-R
++后缀自增lexp ++rexpL-R
--后缀自减lexp --rexpL-R
!逻辑反! rexprexpR-L
~按位取反~ rexprexpR-L
+单目,表示正值+ rexprexpR-L
-单目,表示负值- rexprexpR-L

++前缀自增++ lexprexpR-L

--前缀自减-- lexprexpR-L
*间接访问* rexplexpR-L
&取地址& lexprexpR-L
sizeof取其长度,以字节 表示sizeof rexp sizeof(类型)rexpR-L
(类 型)类型转换(类型) rexprexpR-L
*乘法rexp * rexprexpL-R
/除法rexp / rexprexpL-R
%整数取余rexp % rexprexpL-R
+加法rexp + rexprexpL-R
-减法rexp - rexprexpL-R
<<左移位rexp << rexprexpL-R
>>右移位rexp >> rexprexpL-R
>大于rexp > rexprexpL-R
>=大于等于rexp >= rexprexpL-R
<小于rexp < rexprexpL-R
<=小于等于rexp <= rexprexpL-R
==等于rexp == rexprexpL-R
!=不等于rexp != rexprexpL-R
&位与rexp & rexprexpL-R
^位异或rexp ^ rexprexpL-R
|位或rexp | rexprexpL-R
&&逻辑与rexp && rexprexpL-R
||逻辑或rexp || rexprexpL-R
? :条件操作符rexp ? rexp : rexprexpN/A
=赋值lexp = rexprexpR-L
+=以...加lexp += rexprexpR-L
-=以...减lexp -= rexprexpR-L
*=以...乘lexp *= rexprexpR-L
/=以...除lexp /= rexprexpR-L
%=以...取模lexp %= rexprexpR-L
<<=以...左移lexp <<= rexprexpR-L
>>=以...右移lexp >>= rexprexpR-L
&=以...与lexp &= rexprexpR-L
^=以...异或lexp ^= rexprexpR-L
|=以...或lexp |= rexprexpR-L
逗号rexp,rexprexpL-R

按照操作符的优先级和结合性等等就能写出唯一路径的表达式吗??不一定,表达式的求值部分由操作符的优先级决定。如以下的例子:

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

代码1在计算的时候,由于*比+的优先级高,只能保证,*的计算是比+早,但是优先级并不 能决定第三个*比第一个+早执行。所以表达式的计算机顺序就可能是:

a*b
c*d
a*b + c*d
e*f
a*b + c*d + e*f
或者:
a*b
c*d
e*f
a*b + c*d
a*b + c*d + e*f

//表达式2
c + --c;

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


//代码3-非法表达式
int main()
{int i = 10;i = i-- - --i * ( i = -3 ) * i++ + ++i;printf("i = %d\n", i);return 0;
}

表达式3在不同编译器中测试结果:非法表达式程序的结果


//代码4
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(); 中我们只能通过操作符的优先级得知:先算乘法, 再算减法。 函数的调用先后顺序无法通过操作符的优先级确定。


//代码5
#include <stdio.h>
int main()
{int i = 1;int ret = (++i) + (++i) + (++i);printf("%d\n", ret);printf("%d\n", i);return 0;
}

同样的代码,在不同的编译器上运行出的结果不同。


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


总结

这就是我对C语言中操作符相关知识点的讲解,感谢老铁们的耐心阅读,希望多多支持!!!关注我!后续有更多的干货❤❤❤❤

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

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

相关文章

安装JAVA和java IDEA并汉化过程

1.安装java: 打开java的下载链接&#xff1a; Java Downloads | Oracle 然后选择对应的版本下载即可&#xff0c;我这里是windows 所以下载这个 然后正常一步步安装即可。 2.配置java环境&#xff1a; 在桌面右键此电脑然后点击属性——高级系统设置——环境变量——然后…

H5点击复制功能 兼容安卓、IOS

效果图 HTML代码 <div>链接&#xff1a;<span style"color: #FF8A21" click"CopyUrl" id"copyId"> https://blog.csdn.net/qq_51463650?spm1000.2115.3001.5343</span> </div>复制方法 const CopyUrl () > {let …

12.Blender 界面介绍(上)及物体基础编辑操作

设置语言 首先在菜单栏打开编辑-Preferences-界面-翻译&#xff0c;可以修改语言 这里使用的是Steam上下载的4.1版本 工具栏 左边的工具栏&#xff0c;按T就会出现&#xff0c;再按T就会隐藏 右边的工具栏是按N&#xff0c;按N显示&#xff0c;再按N隐藏 旋转画面 长按鼠…

CSS 标准流 浮动 Flex布局

目录 1. 标准流2. 浮动2.1 清除浮动 3. Flex 布局3.1 Flex 组成3.2 Flex 布局 - 主轴与侧轴对齐方式3.2.1 主轴对齐方式3.2.2 侧轴对齐方式 3.3 Flex 布局 - 修改主轴方向3.4 Flex 布局 - 弹性伸缩比3.5 Flex 布局 - 弹性盒子换行3.6 Flex 布局 - 行对齐方式 1. 标准流 标准流…

使用selenium时出现element click intercepted报错的解决办法

win10&#xff0c;python3.8.10。 selenium版本如下&#xff08;用pip38 show selenium查看&#xff09;&#xff1a; 在定位中&#xff0c;定位了一个按钮&#xff08;特点&#xff1a;button下还有span然后才是文本&#xff09;&#xff0c;代码如下&#xff1a; from sele…

ubuntu22.04 CH340/CH34x 驱动安装

CH34x驱动地址&#xff1a;CH341SER_LINUX.ZIP - 南京沁恒微电子股份有限公司 1、卸载旧驱动&#xff08;如果存在&#xff09; sudo rmmod ch341.ko 2、解压进入 driver 目录 unzip CH341SER_LINUX.ZIP cd CH341SER_LINUX/driver 3、编译 make 可能错误&#xff1a; make[1]…

51单片机中断和定时的结合应用

#include <reg52.h>unsigned int cnt 0;sbit led P1^1;// 初始化定时器 void TimerSetup(){TMOD 0x01; // 定时器的第1个模式TH0 0xB8; // 定时器的初始值-高位TL0 0x00; // 定时器的初始值-低位TR0 1; //启动定时器cnt 0;EA 1; // 开启总中断ET0 1; // 时间中断…

4月26日 阶段性学习汇报

1.毕业设计与毕业论文 毕业设计已经弄完&#xff0c;加入了KNN算法&#xff0c;实现了基于四种常见病的判断&#xff0c;毕业论文写完&#xff0c;格式还需要调整&#xff0c;下周一发给指导老师初稿。目前在弄答辩ppt&#xff08;25%&#xff09;。25号26号两天都在参加校运会…

Python升级打怪(5)

链式调用:用一个函数的返回值作为另外一个函数参数 嵌套调用:一个函数在另一个函数定义里面&#xff0c;而调用该定义函数既可以使用在其里面的函数 在Pycharm中调试器的左下角能够看到函数之间的"调用栈" 调用栈里面描述了当前这个代码的函数之间&#xff0c;调用…

AI生图美学在淘宝的实践应用

本文介绍了如何制定和应用美学标准来评估和改善人工智能生成的图像质量&#xff0c;特别是在电商领域的应用&#xff0c;主要分为制定美学标准、训练美学模型、应用美学模型、升级淘宝风格模型四个步骤。 美学的定义与分析 图像质量标准&#xff1a;现代设计框架下&#xff0c;…

黑马-设计模式-笔记(未完)

一、基础 UML类图 可见性&#xff1a; public- private#protected 表示方式&#xff1a;属性&#xff1a;可见性 名称:类型[默认值]方法&#xff1a;可见性 名称(参数)[:返回类型] 关系&#xff1a;关联关系&#xff1a;实线&#xff0c;引用关系&#xff0c;类属性里有另一个…

Pycharm/Dataspell中使用jupyter导入ros humble包

配置ros humble对应python包路径文件 首先在~/.local/lib/python3.10/site-packages目录下新建一个.pth文件&#xff0c;如下图所示。 将对应的ros humble的python包的路径配置在上述文件中&#xff0c;一行放置一个路径&#xff0c;对应的路径如下图所示。 完成上述操作后…

苹果电脑装虚拟机好用吗 苹果电脑装虚拟机要钱吗 Parallels对mac的损害 Parallels占用多大空间 PD19

在当今数字化的时代&#xff0c;人们对电脑系统跨设备互联的需求越来越高。作为拥有广泛用户群体的苹果电脑&#xff0c;许多用户会有在Mac系统中运行其他操作系统的需求。在这种情况下&#xff0c;安装虚拟机是一个较好的解决方案。那么接下来就给大家介绍苹果电脑装虚拟机好用…

【HarmonyOS4学习笔记】《HarmonyOS4+NEXT星河版入门到企业级实战教程》课程学习笔记(三)

课程地址&#xff1a; 黑马程序员HarmonyOS4NEXT星河版入门到企业级实战教程&#xff0c;一套精通鸿蒙应用开发 &#xff08;本篇笔记对应课程第 4 - 6节&#xff09; P5《04.快速入门》 本节来实现一个 HelloWorld 效果&#xff1a; 1、打开编辑器&#xff0c;选择新建项目&…

SpringBoot War打包部署

修改打包方式 <packaging>war</packaging>修改 Servlet 容器的 scope <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId><scope>provided</scope></d…

Mybatis入门(入门案例,IDEA配置SQL提示,JDBC介绍,lombok介绍)

目录 一、Mybatis入门案例介绍整体步骤创建SpringBoot项目pom依赖准备测试数据新建实体类配置Mybatis数据库连接信息新建接口类,编写SQL代码单元测试 二、IDEA配置SQL提示三、JDBC是什么案例JDBC和Mybatis对比 四、数据库连接池介绍如何实现一个数据库连接池切换数据库连接池 五…

Oracle导出导入dmp等文件类型的多表数据的常用方法、遇见的常见问题和解决办法(exp无效sql???)

使用PLSQL执行导出表数据的时候有两种方法 1、使用Oracle命令【imp--exp】【impdp--expdp】 但是如果你的本机没有安装有Oracle数据库&#xff0c;使用的instant client远程连接服务器上的Oracle数据库时候&#xff0c;你没有Oracle数据库带有的exp.exe、imp.exe等扩展文件&a…

Android kotlin 协程异步async与await介绍与使用

一、介绍 在kotlin语言中&#xff0c;协程是一个处理耗时的操作&#xff0c;但是很多人都知道同步和异步&#xff0c;但是不知道该如何正确的使用&#xff0c;如果处理不好&#xff0c;看似异步&#xff0c;其实在runBloacking模块中使用的结果是同步的。 针对如何同步和如何异…

day04 51单片机-矩阵按键

1 矩阵按键 1.1 需求描述 本案例实现以下功能&#xff1a;按下矩阵按键SW5到SW20&#xff0c;数码管会显示对应的按键编号。 1.2 硬件设计 1.2.1 硬件原理图 1.2.2 矩阵按键原理 1.3软件设计 1&#xff09;Int_MatrixKeyboard.h 在项目的Int目录下创建Int_MatrixKeyboard…

Acer宏碁掠夺者战斧300笔记本电脑PH315-52工厂模式原装Win10系统安装包 恢复出厂开箱状态 带恢复重置

宏碁掠夺者PH315-52原厂Windows10工厂包镜像下载&#xff0c;预装oem系统 链接&#xff1a;https://pan.baidu.com/s/1grmJzz6nW1GOaImY_ymXGw?pwdi286 提取码&#xff1a;i286 原厂W10系统自带所有驱动、PredatorSense风扇键盘控制中心、Office办公软件、出厂主题壁纸、系统…