C语言--13.操作符详解

在我们c语言中,有各式各样的操作符,这些操作符完成了我们对于数据的各种处理,所以它们在我们c语言中是必不可少的,加下来就让我们一起来认识一下着各式各样的操作符吧

在我们这一章中主要会介绍下列操作符与问题

算术操作符
移位操作符
位操作符
赋值操作符
单目操作符
关系操作符
逻辑操作符
条件操作符
逗号表达式
下标引用、函数调用和结构成员
表达式求值
操作符的属性
复杂表达式

 

算术操作符

首先我们来介绍我们最熟悉的算术操作符,这些曹、从小到大耳濡目染的操作符

+     -   *   /   %

这些便是我们最基础的算术操作符,加减乘除取模运算,唯一需要注意的是,取模运算中得到的值是余数,除运算得到的是商,他们都不可以去与0运算,算术操作符,很基础,并不复杂

移位操作符

下面是移位操作符,注意,移位操作符对数值的运算都是在二进制中来进行移位的

<< 左移操作符
>> 右移操作符

我们的移位操作符一共有两个,分别为左移右移,在我们谈左右移时我们先要引入一个概念:有符号数和无符号数,无符号数不会出现负数

int : 有符号数or无符号数

unsigned int : 32位(无符号数),不会出现符号数,[0,2^32-1];

signed int : 32位(有符号数),最高位为符号位,[-2^31,0,2^31-1];

有了这两个概念之后,我们的左右移操作就会分为有符号数左右移与无符号数左右移

无符号数左移右移:

<< :最高位丢弃,最低位补零

>> :最低位丢弃,最高位补零

unsigned int a = 16;//0001 0000
	printf("%d\n", a >> 1);//0000 1000       8
	printf("%d\n", a << 1);//0010 0000       32
	printf("%d\n", a);                       16
	a = a >> 1;      
	printf("%d\n", a);                       8

可以看到的是,对于一个无符号数进行左右移动,是在二进制层面上对其进行移动的,并且输出左右移并不会改变a原本的大小,只有当左右移后重新赋值给了a才会对a本身进行值的修改

下面我们通过画图对其进行内存层面的分析

这幅图便是上面代码在内存层面的实现,因为内存对于数据没有计算只有存储的能力,只有CPU具有对数值进行计算的能力,所以当定义无符号int型变量a时,空间便在内存中进行开辟,当需要对a进行运算时,将a拷贝一份到CPU中的寄存器中(上面的箭头),而后在寄存器中进行运算(左右移动),并且调用printf函数直接通过寄存器输出a左右移后的值,之后我们调用printf函数输出a的值,此时内存中a的值并没有被改变,所以会得到初始值16,然后我们在下面增加了一步将a右移后的值(使用=)重新赋回了a,也就是将寄存器中右移后的那份拷贝重新赋给了内存中的a,所以我们最后输出才能输出右移后的值8,这便是对上面代码的具体解读

那么我们将代码改为下面样子

    unsigned int a = -1;//1111 1111...1(补码)
    printf("%d\n", a);  //     1111 1111...1(补码)-> 1000 0000...1(原码)   -1   
	printf("%d\n", a >> 1);//  0111 1111...1(原码)   2147483647 
	printf("%d\n", a << 1);//  1111 1111...0(补码)-> 1000 0000..10(原码)  -2
                     

我们可以发现,当我们用-1(有符号数)赋给一个无符号整型数时,对其进行左右移运算,得到的结果依旧满足高位丢弃,低位补零,或者低位丢弃,高位补零,只是对于负数而言,是将存入的补码数据进行左右移动后,再进行转源码,得到的数才输出

所以我们可以总结:只要是无符号数(只跟本身数据类型有关),都满足右移低位丢弃,高位补零,左移高位丢弃,低位补零

补充:一般32位操作系统的寄存器也是32位的,而我们的左移右移操作,本质上是存入的二进制01编号拷贝到寄存器中后,左右移动,进行左右补零,就会变成33个比特位,而我们的寄存器只有32个比特位,不足以容纳33个比特位,进行边位的舍弃,这就是左右移动的实质

有符号数左移右移

在我们简述左右移之前,我们先来对原反补来进行复习

    int x = -2;//(当未声明时默认为有符号数)
	//原码: 1000 0000 0000 0000 0000 0000 0000 0010
	//反码: 1111 1111 1111 1111 1111 1111 1111 1101 (除符号位,其他位取反得到反码)
	//补码: 1111 1111 1111 1111 1111 1111 1111 1110 (反码+1得到补码)

接下来我们对其进行左右移动

     signed int x = -2;// 1111 1111 1111 1111 1111 1111 1111 1110
	
//	(左移)补码1111 1111 1111 1111 1111 1111 1111 1100
	//反码1111 1111 1111 1111 1111 1111 1111 1011(减一)
	//原码1000 0000 0000 0000 0000 0000 0000 1100
	printf("%d\n", x << 1);//1111 1111 1111 1111 1111 1111 1111 1100    结果:-4
//  (右移)补码1111 1111 1111 1111 1111 1111 1111 1111
	//反码      1111 1111 1111 1111 1111 1111 1111 1110(减一)
	//原码      1000 0000 0000 0000 0000 0000 0000 0001
	printf("%d\n", x >> 1);// 1111 1111 1111 1111 1111 1111 1111 1111   结果:-1

我们可以观察到,当数据进行左移时,不论是否有符号,都满足最高位舍弃,最低位补零即可。

而进行右移时,若为无符号数,右移规则同左移(逻辑移位),左边用零填充,右边丢弃,若为有符号数(算术移位),右移时补符号位,事实上无符号数右移填充的也是符号位,只不过无符号数的符号位是0

补充:

    //补码1111 1111 1111 1111 1111 1111 1111 1110
	unsigned int x = -2;
	printf("%d\n", x >> 1);//0111 1111 1111 1111 1111 1111 1111 1111

当我们对无符号整形数据赋值为有符号数-2时,进行右移操作,发现补码右移后补的是0,由此可以得出结论,我们补0还是补符号位,取决于变量本身的类型(前面关键字),和内部写入数据无关

位操作符

位操作符指的就是在二进制位上进行的操作

& // 按位与  有0就0,同1才1
| // 按位或   有1就是1
^ // 按位异或   相异为1,相同为0
注:他们的操作数必须是整数。
 

上面便是我们对于10与20的按位与,按位或,按位异或,得到的结果

补充:异或操作中遵循的几个规律

1.相同为假(0),相异为真(1)

2.任何数与0异或,都是它本身

3.支持交换律与结合律

赋值操作符

赋值操作符就是我们所熟知的=

    int a = 10;
	float f = 3.14;//从“double”到“float”截断
    float b = 3.14f;
    float e = (float)3.14;//强制转化为float型数据
	double d = 3.1415;
	char c = 'a';

	a = 20;

1.我们float f =3.14会有告警,因为默认浮点数为double类型,所以在我们float类型的数值后要加个f,或者也可以在数值前面加(类型),来进行强制转化,这里可以得出=两边的类型要尽量相同

int a = 10;
int b = 20;
int c = 30;
a = b = c = 100;

2.赋值是支持连续的,a,b,c三个值都是100.

复合操作符

+=      (a = a+1)       ==         (a+=1)
-=
*=
/=
%=
>>=
<<=
&=
|=
^=

这里的符合操作符便是我们熟知的 a= a+1 和 a+=1,这两者是等价的,复合操作符方便代码的简洁直观

单目操作符

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

单目操作符的含义就是仅有一个操作数的操作符叫做单目操作符

这里我们仅对++来进行详细解读

    int a = 10;
	int b = ++a;
	printf("%d %d\n", a, b);//11 11
	int c = 10;
	int d = c++;
	printf("%d %d\n", c, d);//11 10

上面我们可以看出当a进行前++时,先对a进行+1操作,而后赋值给b,所以结果为a,b都为11

当我们的c进行后++时,先对c赋给d,而后再进行+1操作,最后的出来的就是c=11,d=10

我们对如上代码进行内存层面的解读

在我们这两个语句中,当int c =10;被执行时,10从内存中被读入寄存器,而int d =c++;中,因为后加加先使用,所以就将刚才读入的10重新赋回内存中d的位置空间中,而后执行到加加时,则再从内存中c的位置拷贝一份10到一个新的寄存器中,就结束了这两个语句,注意,仅在这两个语句中,11所处的寄存器无人赋值,所以就留在了寄存器中,当语句执行完,它自动会被释放

此时咱们再来看这两句语句,当int a = 10;被执行时,10的一份拷贝被放入了寄存器中,而后执行到下里面语句,a++;此时又拷贝一份a放给一个新的寄存器,而后自增完毕重新赋给a,最后我们便得到了a=11,而最开始的接收10的寄存器,则在语句被执行完毕时自动释放

那么此时我们再来看++a的语句,首先,将10的一份拷贝放到寄存器中,因为是++a,先加加,所以在寄存器中完成++变为11,再赋回a,最后就得到了a为11,

所以在内存层面中,前加加比后加加效率会更好点,但是差别不大

补充:此时我们便有一个很大的疑问被解决了,那就是函数中创立的临时变量(临时变量具有临时性,按道理不能被函数外的语句调用),可以通过return语句来返回到我们main函数中,其本质就是当函数执行完毕出栈之后,return语句后的值被保留到了寄存器中,从而可以在函数结束之后将返回值返回到main函数中被接收到,这个寄存器便是EAX通用寄存器

关系操作符

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

我们的关系操作符使用起来并不困难,值得注意的是,我们的关系操作符只能用于比较int型数据比较,也可以部分浮点数比较,但是绝对不可以用于字符串的比较,字符串的比较应该使用函数来进行比较,我们的所有关系操作符得到的都是逻辑运算结果(0or1),但是尽量不要使用==来比较两个浮点数,因为会有精度方面的细微差别,会在小数点后很多位时忽略后面,使两个非常接近但不相等的值相等,再就需要注意的是,=与==很容易混哦,要格外注意下

逻辑操作符

我们的逻辑操作符和算术运算符最大的区别就是,逻辑运算符得到的结果一定是逻辑结果,也就是真(1)或者假(0),

&&     逻辑与
||           逻辑或

    按位和逻辑的区别

1 & 2 -----> 0
逐比特位进行与数据运算
0000 0000...0001&
0000 0000...0010
----------------------------
0000 0000...0000--->0
1 && 2 ----> 1
因为1,2都是非零数,都为真,所以他们逻辑与运算(有假则假,同真才真)结果也为真
1 | 2 -----> 3
逐比特位进行或数据运算
0000 0000...0001||
0000 0000...0010
----------------------------
0000 0000...0011--->3
1 || 2 ----> 1
因为1,2都为非零数,都为真,所以他们逻辑或运算(有真即真,同假才假)结果也为真

对于逻辑操作符&&以及||还存在判断后面语句是否执行的特性

    int a = 10;
	a >= 10 && printf("hello\n");//hello
	a < 10 && printf("world\n");//(未执行)
	a >= 10 || printf("hi\n");//(未执行)
	a < 10 || printf("baby\n");//baby

我们可以看到,当a=10时,下面&&语句当a>=10判断为真时,才会继续向后执行printf语句,而a<10判断为假时便不会再去执行判断后面的语句,所以不会打出world,当逻辑或a>=10判断为真时,便不会再去执行判断后面的真假,所以便不会执行hi,而当a<10判断为假时,就会继续进行执行判断后面的printf输出baby

条件表达式(三元运算符)

exp1 ? exp2 : exp3

对于我们的三元运算符而言,其判断规则是,首先执行exp1,若exp1为真,则输出exp2,若为假,则输出exp3,三元运算符最大的作用是增强了代码的简洁性,下面我们用一段代码来对三元运算符进行说明

    int x = 0;
	int y = 0;
	while (1){
		printf("Please Enter Your x#");
		scanf("%d", &x);
		printf("Please Enter Your y#");
		scanf("%d", &y);
		/*if (x > y){
			printf("max=%d\n", x);
		}
		else{
			printf("max=%d\n", y);
		}*/
		int max=(x > y) ? x : y;
		printf("max=%d\n", max);
	}

我们可以看到,底下一行的三元运算符,就可以替代上面冗余的if判断

逗号表达式

我们c语言中逗号表达式非常常见

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

值得注意的是,逗号表达式是需要从左到右依次执行完毕的,但最终结果只和最后一个逗号后面的式子有关,下面我们引入一段代码进行说明

    int a = 1;
	int b = 2;
	int c = (a > b, a = b + 10, a, b = a + 1);
    int d = (a > b, a = b + 10, a, b == a + 1);
	printf("%d\n", c);//13
    printf("%d\n", d);//0

我们可以看到,最后的c的值是13,这是为什么呢,我们来根据逗号表达式一点一点看,从左到右a>b不成立,然后b+10被赋值给了a,此时a的值为12,后面a+1被赋给了b,最后b为13,又因为逗号表达式的值只跟最后一个逗号后的式子有关,所以b的值最后被赋给了c,而在下面的式子中,最后一步变为了判断,b又不等于a+1,最后就会变为假(0),所以最后d的值就为0

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

这三个概念我们并不陌生,在我们之前的学习中有过许多使用,接下来用一段代码来帮我们回顾一下这三个概念

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<Windows.h>
void ForTest(){
	printf("ForTest!\n");
}
struct Stu{
	char name[32];
	char sex;
	int math;
};
int main(){
	int a[10] = { 0 };//下标引用
	a[2] = 3;
	printf("%d\n", a[2]);

	ForTest();//函数调用

	struct Stu zhangsan = { "zhangsan", 'm', 98 };
	printf("%s\n", zhangsan.name);

	struct Stu *p = &zhangsan;
	printf("%s\n", (*p).name);//结构成员的访问
	printf("%s\n", p->name);


	system("pause");
	return 0;
}

在这段代码中,我们定义了一个数组,数组的[ ]便是我们的下标引用,作用就是访问中括号内下标或偏移量的值,比如a[2]访问的就是a数组的3号下标元素,而后我们定义了一个函数,函数的()便是函数调用操作符,没有这个(),就无法调用或者构建函数,之后我们建立了一个结构体,注意,访问结构体内的成员方式这里我们介绍了三种,一种是通过结构体名.成员的方式,点的方式去访问,这便是结构体的基础访问,而后还有通过定义指针解引用去点访问成员,也是可以的,最后则是一种结构体的特殊访问方式,利用指针->去指向成员访问

接下来我们便进入了操作符这一章的较为复杂的部分,表达式求值,以及下属的隐式转换等,此部分作为对于操作符的补充,但仍是重点内容

表达式求值

表达式求职的顺序一部分是有操作符的优先级和结核性决定,同样,有些表达式的操作数在求值的过程中可能需要转换为其它类型,接下来我们来写一段代码分析一下这个问题

    char a = 10;
	char b = 20;
	int c = a + b;
	printf("%d\n", c);//30

在我们看到这段代码时,猛然一看,觉得其并没有问题,输出的结果也是我们所料想的30,但是我们细细的想一下,char类型所占用的字节数是1,而int型占用的字节数是4,这就有些奇怪了,两个字节数是1类型的数字相加为什么可以被赋给一个字节数为4类型的变量呢?,我们可以猜测得到,它在内部一定完成了某种运算,将两个char类型相加的char类型1字节的结果进行了补齐前面24个比特位的操作,而这种内部操作在名称上,就被叫做类型提升

因为整型提升是编译器自动帮助我们做的,所以它也属于隐式转化的一种

对于类型提升而言,我们需要回答3个问题

1.怎么提升?答:提升方式取决于自己本身的数据类型,整型提升看自身类型添加对应的符号位(同右移规则)

    signed char a = -10;//1111 0110
	printf("%u\n", a);//4294967286(%u代表的是无符号整型)
	                  //1111 1111 1111 1111 1111 1111 1111 0110

我们可以看到的是我们的-10在内存中存储的二进制序列为1111 0110,而我们让其以无符号整型形式打印出来的结果为4294967286,这个数字在二进制序列中打印出来的顺序就是上面那串,通过对比我们可以发现,其与-10的二进制序列的差别就在于前面24位比特位补了1,所以其满足我们的规则:char类型要提升为无符号整型(%u),自身类型为有符号char型,所以补符号位1

    unsigned char a = -10;
	printf("%u\n", a);//246(%u代表的是无符号整型)
	                  //0000 0000 0000 0000 0000 0000 1111 0110
	system("pause");

下面我们仅仅是给signed前添加了一个un变为unsigned无符号整型,我们得到的结果就为,给前面24位比特位添加自身数据类型的符号位0,得到的结果就是-10后八个比特位前面全部补零的结果,依旧满足我们的规则:char类型要提升为无符号整型(%u),自身类型为无符号char型,所以补符号位0

所以我们可以得出结论,当需要提升类型的时候,补0还是补1,取决于自身的类型(signed与unsigned),而不是后面放入数值的类型(规则与右移相同)

2.为什么需要类型提升?在计算时,我们本身的类型是8位比特位的char类型,而实际参与运算的是32位int型,需要等比特位进行计算,比特位不足,需要填充比特位,此时便需要类型提升

在表达式运算当中,参与运算的变量的类型必须一致

下面我们对最开始的那个问题来进行内存层面的解释

当我们进行运算之前,需要将数据先进行拷贝存放在寄存器中,又因为寄存器是32位的,所以在拷的过程中就完成了类型提升,当把a和c都放入了寄存器中时,此时便已经是两个整形数据进行运算了,运算完毕之后得到的值赋给一个新的寄存器,而后再赋回给c这边完成了我们上述的步骤

补充:有一个概念需要我们来进行理解一下,我们通过一道题来对其进行说明

    unsigned char a = 0;
	unsigned char b = 255;
	unsigned char c = 255;
	a = b + c;
	//a?//254

我们来看一下这道题,问最后a的结果是多少,这个知识需要我们在内存层面才能解答

在我们学习过隐式转化之后,这个问题已经可以得到解答,首先,b与c定义时被拷贝一份放入了寄存器中,而寄存器是32位的,此时便对他们已经完成了类型的提升,相当于两个int型数据进行相加,得到了1 1111 1110这样的二进制序列数,按道理来说答案应该就是这个二进制序列所对应的数,但是为什么最后不是呢,原来,在他们完成相加之后,还需要赋回给我们的a,但是我们的a只有8个比特位可以用来接收数据,此时编译器便会抛弃掉最高位的比特位,将1111 1110存入a中,所以我们最后得到的结果便是1111 1110二进制序列所对应的结果,254,这种因为空间大小不足,将高比特位舍去的现象叫做截断

接下来有两道经典的题来复习下我们的整型提升问题

    char a = 0xb60;
	short b = 0xb600;
	int c = 0xb6000000;
	//int 0xb6= 0000 0000 0000 0000 0000 0000 1011 0110
	//char a = 1111 1111 1111 1111 1111 1111 1011 0110
	
	if (a == 0xb6)
		printf("a");
	//int 0xb600 = 0000 0000 0000 0000 1011 0110 0000 0000
	//short b = 1111 1111 1111 1111 1111 0110 0000 0000
	if (b == 0xb600)
		printf("b");

	if (c == 0xb6000000)
		printf("c");//c

我们可以看到,最后的结果只有c,我们分析一下,char a在进行if判断时,被放置在()内,而()内的判断只能是整形判断,所以要进行类型提升,因为自身为有符号数,符号位为1,所以前面补1,而0xb6原本就为整型,只是位数只有8位,所以自动前面补零,第一个if判断不成立,第二个同理,b的类型是有符号短整型占16个比特位,所以需要提升为整型,因为它是有符号数符号数为1,所以前面16个比特位补1,而0xb600和之前一样,前面补零,c与0xb6000000类型一致,内容一致,所以输出c

    char c = 1;
	printf("%u\n", sizeof(c));//1
	printf("%u\n", sizeof(+c));//4
	printf("%u\n", sizeof(!c));//(实际)1//(理论)4
	

我们来注意观察一下这道题,当我们用sizeof来计算大小时,c是char类型,大小为1,没有问题,但是当检测+c时,结果却为4,这是为什么呢,其实,是因为在我们的运算中,有了+就相当于有了操作符需要对数据进行计算(化为正数),需要计算就要拷贝到寄存器中,再放到寄存器的过程中也就自动提升为4字节大小,所以大小为4,但是下一个!(取反)操作按照理论也应该是4呀,但是实际上输出的却是1,这里我们可以理解为是编译器的一个bug,但是在Linux操作系统中,检测的大小仍为4,这里需要注意

算术转化

在我们隐式转化中,还有一种重要的转化方式叫做算术转化

    int a = 10;          //0000 0000 0000 0000 0000 0000 0000 1010
	unsigned int b = -20;//1000 0000 0000 0000 0000 0000 0001 0100
                         //1111 1111 1111 1111 1111 1111 1110 1100
	if (b > a){
		printf("b>a\n");//结果为b>a
	}
	else{
		printf("b<=a\n");
	}

当我们对上述代码进行运行时,我们发现了一个违反直觉的事情,便是-20竟然比10,大了,其实因为int型与unsigned int类型不一致,这中间会存在我们的算数转化,将int 型 转化为unsigned int 我们内存中存入的10就是我们一般存入的二进制序列,当存入-20时,将原码转换为补码,实际比较的时候是拿补码来与我们的10进行比较的,也就是第一行与第三行进行比较,又因为其为无符号数,所以-20实际上是一个很大的数字,所以才会得到-20大于10的奇怪结论

我们的算术转化是按照一定规则来的,

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

上述顺序从下往上便是我们的算数转换规则

操作符的属性

在我们运用表达式进行求值时,首先需要考虑的一点就是这个表达式时如何进行运算的,这边会牵扯到我们操作符的优先级结合性等问题,只有当我们熟悉了解过这些操作符的操作顺序,我们才可以对表达式进行正确的求值,下面我们来了解一下控制操作符操作顺序的三种属性

复杂表达式的求值有三个影响的因素
1. 操作符的优先级         解决当我们有多个操作符操作数时先执行哪个的问题
2. 操作符的结合性         解决当我们操作符相同时,多个操作符之间从右向左执行还是从左向右执行的问题
3. 是否控制求值顺序。 解决特定的某些表达式,实际在进行对应的 表达式求值时,会根据不同的条件产生不同的执行顺序,不同路径求值
    int a = 10;
	int b = 20;
	int c = 0;
	c = a + b * 10;//优先级使表达式先执行b*10,再执行a+
	c = a + b + 10;//结合性使表达式从左向右依次执行a+b+10
	a > b&&a < c&&a == b;//控制求值顺序使得执行完a>b发现不满足就不执行后面的了

下面我们来了解一下在咱们c语言中会使用到的操作符优先级吧

以上便是我们所有的操作符顺序表

值得注意的是解引用(*)这个操作符,解引用操作符相对于大多数操作而言都是优先级较低的,一个符号最终是什么类型,取决于其先与那个操作符结合

复杂表达式

// 表达式的求值部分由操作符的优先级决定。
// 表达式 1
a * b + c * d + e * f
a * b + c * d

我们来看一下这个表达式,按照我们所学的操作符顺序可以判断这个式子的执行顺序吗?

答案是不行的,这个式子我们只能得出第二个+在第一个+之后,但是无法得出第三个*在第二个*或第一个+之后这样的结论,这是因为不同的编译器对于这种表达式的计算顺序并没有一个统一的规定,但是底下的这个表达式就可以确定其顺序了,因为他的各种优先级顺序都是确定的,路径是唯一的,所以不会有之前的那个结论

此时我们来举一个例子

    int c = 10;
	printf("%d\n", c + --c);

我们来看一下这个式子,我们第一反应其结果是什么呢?是不是先进行c--,得到c等于9,然后再进行9+9得到最后结果为18呢?

在我们vs与Linux编译器中确实是这样,但是在有的编译器中则不是的,我们来进入到内存的角度来剖析一下这个问题

我们可以看到对于上面那个寄存器确实是,先将c放到放到寄存器中,然后进行自减,将值重新赋回内存,最后得到9+9=18的结果,但是对于下面两个寄存器,CPU识别出进行运算的两个操作数相同,便会将操作数拷入寄存器,再从寄存器中拷入别的寄存器进行运算,这样效率会高,这就导致了一个问题,在下面两个中,c自减是将其一份拷贝进行自减,但是最后相加的时候却是和第二个寄存器中的c进行相加的,这就可能导致9+10的出现,问题就在于这里,c--到底影不影响运算中其它c的值呢,这个是不确定的,跟编译器有关,所以我们在以后写代码时尽量要避免这种情况的发生,避免书写这样的代码

int main ()
{
int i = 10 ;
i = i -- - -- i * ( i = - 3 ) * i ++ + ++ i ;
printf ( "i = %d\n" , i );
return 0 ;
}

上面一段代码在不同编译器中执行的结果,不同的编译器计算路径不同,所以结果也不尽相同

这边是我们关于操作符的全部内容

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值