【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;
	//定义指针变量P
	int* 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---->1

1|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;
else
        b = -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语言中操作符相关知识点的讲解,感谢老铁们的耐心阅读,希望多多支持!!!关注我!后续有更多的干货❤❤❤❤

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

java导出数据到excel表中

java导出数据到excel表中 环境说明项目结构1.controller层2.service层3.实现层4.工具类&#xff1a;ExcelUtil.java5.ProductModel.java类 使用的Maven依赖postman请求展示&#xff0c;返回内容需要前端接收浏览器接收说明&#xff08;如果下载下来的为zip类型&#xff0c;记得…

矽塔SA8321 单通道 2.7-12.0V 持续电流 3.0A H 桥驱动芯片

描述 SA8321是为消费类产品&#xff0c;玩具和其他低压或者电池供电的运动控制类应用提供了一个集成的电机驱动器解决方案。此器件能够驱动一个直流无刷电机&#xff0c;由一个内部电荷泵生成所需的栅极驱动电压电路和4个功率 NMOS组成H桥驱动&#xff0c;集成了电机正转/反…

polkit服务启动失败

使用systemctl 命令报错 Authorization not available. Check if polkit service is running or see debug message for more information. 查看polkit状态是失败的状态&#xff0c;报缺少libstdc.so.6 systemctl status polkit 需要安装libstdc.so.6库 先加载所有安装包 …

网络安全产品---堡垒机

what 在网上搜索 运维审计与风险控制系统就是是堡垒机 我认为的堡垒机就是提供高效运维、认证管理、访问控制、安全审计和报表分析功能的云服务设备 实现高效运维的同时最大程度控制运维风险。 how 能够对运维人员维护过程进行全面跟踪、控制、记录、回放 支持细粒度配置…

最新Java面试题3【2024中级】

互联网大厂面试题 1&#xff1a;阿里巴巴Java面试题 2&#xff1a;阿里云Java面试题-实习生岗 3&#xff1a;腾讯Java面试题-高级 4&#xff1a;字节跳动Java面试题 5&#xff1a;字节跳动Java面试题-大数据方向 6&#xff1a;百度Java面试题 7&#xff1a;蚂蚁金服Java…

SpringMVC02:注解模式

SpringMVC02&#xff1a;注解模式 文章目录 SpringMVC02&#xff1a;注解模式前言一、代码编写&#xff1a;1. 编写jsp页面2. 在web.xml中&#xff0c;注册DispatcherServlet&#xff08;须要绑定SpringMVC配置文件&#xff09;3. 编写SpringMVC 的 配置文件4. 编写Controller类…

医学临床预测模型发展新趋势-并联式

医学临床预测模型发展新姿势-并联式 现有的预测模型是对单个结局指标进行分类或者回归&#xff0c;得出最终的结论&#xff0c;而辅助医生进行临床决策。众所周知&#xff0c;临床决策过程中&#xff0c;医生通常会考虑多个结局指标来做出最终的决策&#xff1b;临床研究中也通…

【JavaScript编程实操14】DOM实操_回到顶部

前言 本次主要是针对Javascript阶段的DOM实操方面的练习&#xff0c;本次主要实现当页面内容过多时&#xff0c;可以点击按钮&#xff0c;快速回到页面顶部的效果。这次的实现逻辑比较简单&#xff0c;主要是应用函数实现页面的回到顶部功能&#xff0c;this.scrollTo(0, 0)可以…

万界星空科技机器人组装行业MES系统

一、为什么选择万界星空科技&#xff1f; 万界星空科技作为一家在云MES系统的研发、生产自动化方面拥有很多年行业经验的科技型企业&#xff0c;多年来专注于云MES系统的研发与技术支持服务&#xff0c;目前已成为国内知名的智能制造整体解决方案提供商。 公司凝聚了一支经验…

Redis系列之Cluster集群搭建

在上一篇博客&#xff0c;我们学习Redis哨兵Sentinel集群的搭建&#xff0c;redis的哨兵模式提供了比如监控、自动故障转移等高可用方案&#xff0c;但是这种方案&#xff0c;容量相对固定&#xff0c;要进行持续扩容或者数据分片就不适合&#xff0c;所以有另外一种更复杂的集…

线性代数基础3 行列式

行列式 行列式其实在机器学习中用的并不多&#xff0c;一个矩阵必须是方阵&#xff0c;才能计算它的行列式 行列式是把矩阵变成一个标量 import numpy as np A np.array([[1,3],[2,5]]) display(A) print(矩阵A的行列式是&#xff1a;\n,np.linalg.det(A))array([[1, 3],[2, …

视频质量评价 PSNR 算法详细介绍

PSNR PSNR(Peak Signal-to-Noise Ratio,峰值信噪比)是一种常用的评价图像质量的指标,尤其在图像压缩和图像处理领域。它基于最大可能的图像信号功率和图像的噪声功率之间的比率,通常用于衡量图像恢复或图像压缩算法的效果。 原理 PSNR是基于MSE(Mean Squared Error,均…

『 论文解读 』大语言模型(LLM)代理能够自主地利用1 day漏洞,利用成功率竟高达87%,单次利用成本仅8.8美元

1. 概览 该论文主要展示了大语言模型LLM代理能够自主利用现实世界的 1 day 漏洞。研究我发现&#xff0c; GPT-4 在提供了CVE描述的情况下&#xff0c;能够成功利用 87% 的漏洞。 这与其他测试模型&#xff08;如 GPT-3.5 和其他开源 LLM &#xff09;以及开源漏洞扫描器&…

Tomcat核心组件深度解析

Server组件 Service组件 连接器Connector组件 容器Container组件

【hackmyVM】whitedoor靶机

文章目录 信息收集1.IP地址2.端口探测nmapftp服务 3.访问主页 漏洞利用1.反弹shell2.尝试提权3.base64解密 提权1.切换用户2.john爆破3.切换Gonzalo用户4.vim提权 信息收集 1.IP地址 ┌─[✗]─[userparrot]─[~] └──╼ $fping -ag 192.168.9.0/24 2> /dev/null192.168…

【小程序】IOS wx小程序解压获取源文件

根据自己手机的系统&#xff0c;获取wx小程序的缓存目录 一、微信小程序文件存放路径 安卓&#xff1a; /data/data/com.tencent.mm/MicroMsg/{{user哈希值}}/appbrand/pkg/iOS越狱&#xff1a; /User/Containers/Data/Application/{{系统UUID}}/Library/WechatPrivate/{{user…

PCIe复位方式介绍

前言 PCIe总线中定义了四种复位名称&#xff1a;冷复位&#xff08;Cold Reset&#xff09;、暖复位&#xff08;Warm Reset&#xff09;、热复位&#xff08;Hot Reset&#xff09;和功能层复位&#xff08;Function-Level Reset&#xff0c;FLR&#xff09;。其中FLR是PCIe …

RocketMQ学习笔记

kafka适合于日志收集的场景&#xff08;不需要太多topic&#xff1b;topic下面的partition多了会造成写文件的速度变慢&#xff0c;因为要造很多索引&#xff09; RocketMQ更适合于电商场景&#xff08;适用于topic特别多的情况&#xff09; 快速安装RocketMQ RocketMQ的官网…

js 函数节流和函数防抖及区别详解

文章目录 1. 前言2. 函数节流3. 函数防抖4. 总结 1. 前言 浏览器中总是有一些操作非常耗费性能。所以就有了函数节流和函数防抖来提高浏览器性能。 函数节流&#xff1a;频繁触发一个事件时候&#xff0c;每隔一段时间&#xff0c;函数只会执行一次。 函数防抖&#xff1a;当触…

某零售企业招聘管理体系搭建咨询项目

科学岗位分析&#xff0c;改善招聘流程&#xff0c;提高招聘及时率随着公司不断发展壮大&#xff0c;企业规模逐渐增大&#xff0c;部门设置也日益增多&#xff0c;因此对人员的需求也日益提高。但是目前该企业在人员招聘方面逐渐暴露出一些诸如岗位分析不到位、缺乏整体面试计…