【c初阶】操作符和表达式

(补充)原码反码补码

关于原码反码、补码的转换规则
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1. 操作符

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

1.1 算术操作符

+ - * / %
算术操作符中的乘除与数学中的符号不一样
//算数操作符
int main()
{
	printf("%d\n", 10 / 3); //3
	printf("%lf\n", 10 / 3.0); // 3.333……
	printf("%d\n", 10 % 3); //1
	printf("%d\n", -5 % 3);  //-2
	printf("%d\n", 5 % (-3)); // 2
	return 0;
} 

注意点:

  1. 除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数。
  2. 对于 / 操作符如果两个操作数都为整数,执行整数除法,得到的是商。而只要有浮点数执行的就是浮点数除法。
  3. % 操作符的两个操作数必须为整数。余数的符号需要满足这个等式 (a / b) * b + a % b = a

1.2 移位操作符

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

注:移位操作符是对补码形式的二进制位进行操作

1.2.1 左移操作符

移位规则:左边丢弃,右边补0

//移位操作符
int main()
{
	int a = 1; //整数原反补码相同,补码 00000000 000000000 00000000 00000001
	int b = -1; 
	//原码 10000000 000000000 00000000 00000001
	//反码 11111111 111111111 11111111 11111110
	//补码 11111111 111111111 11111111 11111111
	
	//左移操作符
	a = a << 1;
	printf("%d\n", a); //2
	b = b << 1;
	printf("%d\n", b); //-2
	return 0;
}
对于正整数原反补相同,所以移位之后的二进制是什么样子的最后打印出来的就是什么样子

对于负整数,二进制有3中表示形式,原码、反码、补码,
但是在计算机中始终存储的是补码,计算机只能操作补码,
这就需要我们分析操作后的补码变成什么样子并且需要将该补码转化成原码来对照打印出来的结果观察是否一致
对于a,补码左移1位,左边丢弃,右边补0
最后a的补码是 00000000 00000000 00000000 00000010
计算机认为这是正数,正数的原反补相同,所以原码也是00000000 00000000 00000000 00000010
对应的十进制就是2
对于b,补码是 11111111 11111111 11111111 11111111
左移一位,最后的补码是11111111 11111111 11111111 11111110
计算机认为这是负数,所以我们需要认为的转换成原码
转换后的原码10000000 00000000 00000000 00000010
对应的十进制就是-2

1.3.2 右移操作符

移位规则:

  1. 逻辑右移:右边丢弃,左边补0
  2. 算数右移:右边丢弃,左边补符号位
//移位操作符
int main()
{
	int a = 1; //整数原反补码相同,补码 00000000 000000000 00000000 00000001
	int b = -1;
	//原码 10000000 000000000 00000000 00000001
	//反码 11111111 111111111 11111111 11111110
	//补码 11111111 111111111 11111111 11111111

	//右移操作符
	a = a >> 1;
	printf("%d\n", a); //0
	b = b >> 1;
	printf("%d\n", b); //逻辑右移是一个正数 算数右移是一个负数
	return 0;
}

在这里插入图片描述
在MSVC编译器下、执行算数右移
经实验得知,在大多数编译器下执行的都是算术右移

对于a,补码右移1位,右边丢弃,右边补符号位
最后a的补码是 00000000 00000000 00000000 00000000
计算机认为这是正数,正数的原反补相同,所以原码也是00000000 00000000 00000000 00000000
对应的十进制就是0
对于b,补码是 11111111 11111111 11111111 11111111
右移一位,最后的补码是11111111 11111111 11111111 11111111
计算机认为这是负数,所以我们需要认为的转换成原码
转换后的原码10000000 00000000 00000000 00000001
对应的十进制就是-1

总结:

  1. 移位操作符移动的是补码形式的二进制位
  2. 移位操作符不改变本身的值
  3. 移位操作符的操作数只能是整数
  4. 移位运算符不要移动负数位,这是未定义的

1.2 位操作符

位操作符名称规则
&按位与1&1=1
|按位或0|0=0
^按位异或1^0=1
~按位取反~0=1
注意点:
  1. 位操作符操作的是补码形式的二进制位

  2. 位操作符的操作数必须是整数

//位操作符
int main()
{
	int a = 1;		//补码00000000 000000000 00000000 00000001
	int b = -1;		//补码11111111 111111111 11111111 11111111
	int c = a & b;  //补码00000000 000000000 00000000 00000001
	printf("%d\n", c);//1
	c = a | b;		//补码11111111 111111111 11111111 11111111
	printf("%d\n", c);//-1
	c = a ^ b;		//补码11111111 111111111 11111111 11111110
	printf("%d\n", c);//-2
	c = ~a;			//补码11111111 111111111 11111111 11111110
	printf("%d\n", c);//-2
	return 0;
}

1.2.1 变态的面试题

来看一道变态的面试题

不能创建临时变量,完成两个数的交换

常规思路(创建临时变量)

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

解法1(相加法)

int main()
{
	int a = 1;
	int b = 2;
	printf("交换前:a=%d,b=%d\n", a, b);
	a = a + b; //a里存放2个数的和
	b = a - b; //和减一个数就是另一个数
	a = a - b; //和减一个数就是另一个数
	printf("交换后:a=%d,b=%d\n", a, b);

	return 0;
}

当a和b很大时,这种解法行不通

解法2(相减法)

int main()
{
	int a = 1;
	int b = 2;
	printf("交换前:a=%d,b=%d\n", a, b);
	a = a - b; //a里面存放的是两数之差
	b = a + b; //两数之差加一个数等于另一个数
	a = b - a; //一个数减两数之差等于另一个数
	printf("交换后:a=%d,b=%d\n", a, b);

	return 0;
}

解法3(异或法)

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

之所以能够这么写是因为异或满足3个条件

a ^ a = 0
a ^ 0 = a
异或满足交换律

注:这种写法的缺点有3个

  1. 可读性不高
  2. 只能对整数交换时才可以使用
  3. 效率不高,需要对每个位进行计算

1.2.2 练习题

编写代码实现求一个整数存储在内存中的二进制中1的个数

三种方法求二进制中1的个数


1.3 赋值操作符

赋值操作符可以改变变量的值

int main()
{
	int weight = 120;//体重
	weight = 89; //不满意重新赋值
	double salary = 100000.0;
	salary = 20000.0; //使用赋值操作符
	//赋值操作符可以连续使用
	int a = 10;
	int x = 0;
	int y = 20;
	a = x = y + 1;//连续赋值
	printf("%d %d\n", x, a); //21 21
	return 0;
}

连续赋值不易于调试,看起来没有分开写清楚


1.4 复合赋值符

中间不能有空格
复合赋值只有以下几种
+=
-=
*=
/=
%=
<<=
>>=
^=
|=
&=
int main()
{
	int a = 1;
	a += 2; // a = a + 2
	return 0;
}

关于复合赋值符,需要注意几点
1.复合赋值符号右边是一个整体

int main()
{
	int a = 1;
	a += 1 + 2 * 3; //a = a + (1 + 2 * 3)
	printf("%d\n", a);
	return 0;
}

2.复合赋值符号左边是可以表达某段空间的表达式

int main()
{
	int a = 1;
	a + 3 += 2;//左值是不可以修改的
	return 0;
}

在这里插入图片描述
3. A += 1 不完全等于 A = A + 1
A += 1,中A只会计算一次,A = A + 1,A会计算2次
在这里插入图片描述


1.5 单目操作符

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

1.5.1 逻辑取反!操作符

逻辑否定( logic-NOT )算子在其操作数为真(非零)时产生值0,在其操作数为假( 0 )时产生值1。结果具有int类型。操作数必须是整数、浮点数或指针值。
在这里插入图片描述
注:vs2019逻辑取反的结果是1个字节,但是规定的是4个字节,这可能是vs2019当时设置的bug?,因为vs2019逻辑与、或的结果是int型

这是用Dev测试的结果

在这里插入图片描述


1.5.2 sizeof操作符

关键字的大小给出了与一个变量或类型相关的存储量,以字节为单位。该关键字返回一个类型size _ t的值。
该表达式要么是标识符,要么是表达式

注意1
sizeof是操作符不是函数
操作数是变量时可以省略()
操作数是类型时不可以省略()
以下写法正确
sizeof(a)
sizeof a
sizeof(int)
以下写法不正确
sizeof int
注意2
sizeof是在编译期间就执行的表达式,不会再程序执行时运算
因此sizeof是不求值表达式
求的是类型所占的空间,不在乎空间里面存放的是什么
int a = 1;
int b = sizeof(a++)
printf("%d", a); //1 编译的时候没有编译a++
char a = 6;
printf("%d\n", sizeof(a + 2)); //a + 2是算术表达式,会进行类型提升,最后表达式的值是int
int main()
{
	char a = 6;
	char b;
	printf("%d\n", sizeof(b = a + 2)); // 1
	//等号左边是b,char类型,
	//编译器知道了sizeof的值是1,不会进行计算表达式
	printf("%d\n", a); //6
	return 0;
}

1.5.3 sizeof和数组

sizeof里面是数组名时,代表求整个数组所占的字节数
void f1(int arr[])
{ 
	printf("%d\n",sizeof(arr));
}

void f2(char ch[])
{
	printf("%d\n", sizeof(ch));
}

int main()
{
	int arr[10];
	char ch[10];
	printf("%d\n", sizeof(arr)); //整个数组大小 40字节
	printf("%d\n", sizeof(ch));  //整个数组大小 10字节
	//一维数组名传参,数组退化为一级指针 32位机器上指针为4字节
	f1(arr); //4
	f2(ch);  //4
}

1.5.4 自增操作符

初始C语言3中已经介绍过++ -- 操作符
这里只在说明一下当++ --操作符作为表达式的一部分时,表达式的值究竟是什么样的
在这里插入图片描述

前置++相当于先拷贝一份操作数,再让操作数的值+1,表达式的值是拷贝的值
后置++相当于先让操作数的值+1,再拷贝一份操作数,表达式的值是拷贝的值


1.6 关系操作符

>
>=
<
<=
!= //不等
== //相等
关系操作符中等号==和赋值符号=不要写错

1.7 逻辑操作符

操作覅名称功能
&&逻辑与1 && 1 = 1
||逻辑或0 || 0 = 0
逻辑取反!0=1

逻辑与和逻辑或和条件运算符(后面说)具有短路性,当根据已经算出来的表达式可以知道逻辑表达式最终的结果时,就不会往后执行表达式了

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);//1 2 3 4
    return 0; }

逻辑与具有短路性,执行a++时,a++的值为0,逻辑与必须所有表达式为真结果才为真,所以计算完a++已经出现结果,后边的表达式不再计算

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

逻辑或具有短路性,执行a++时,a++的值为0,继续执行++b,++b的值为3,为真,逻辑或必须所有的表达式为假结果才为假,所以计算完++b已经出结果,后边的表达式不再计算


1.8 条件操作符

exp1 ? exp2 : exp3

条件表达式的意思是:
表达式1成立就会计算表达式二并且将表达式2的值作为条件表达式的值
否则不计算表达式2,计算表达式3并且将表达式3的值作为条件表达式的值

int main()
{
	int a, b;
	if (a > 5)
	{
		b = 1;
	}
	else
	{
		b = -1;
	}
	return 0;
}

这段代码等价于

int main()
{
	int a, b;
	b = a > 5 ? 1 : -1;
	return 0;
}

用好条件表达式可以很好的简化代码


1.9 逗号表达式

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 %d %d\n", c, a, b); //13 12 13
	return 0;
}
从左到右依次执行逗号表达式
先执行a > b,再执行a = b + 10, 执行完后a的值变为12
再执行a,最后执行 b = a + 1,该表达式的值是13,执行完后b的值也是13
最后条件表达式的值也是最后一个表达式的值13
使用逗号表达式有时可以简化代码例如:
int get_val(void)
{

}
int count_val(int a)
{

}
int main()
{
	int a;
	a = get_val();
	count_val(a);
	while (a > 0)
	{
			//业务处理
			a = get_val();
			count_val(a);
	}
	return 0;
}
将main函数里面替换成
while (a = get_val, count_val(a), a > 0)
{
	//业务处理
}

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

1.10.1 下标引用操作符[ ]

下标引用操作符的操作数是数组名+索引值

int main()
{
	int arr[10];
	arr[9] = 1;
	//[]操作数是arr和9
	int arr2[3][2];
	arr2[2][1] = 1;
	//第一个[]操作数是arr2和2
	//第二个[]操作数是arr[2]和1
	return 0;
}

下标引用是间接访问的一种伪装

1.10.2 函数调用操作符()

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

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

1.10.3 成员访问操作符

1 .操作符:结构体名.成员
2 ->操作符:结构体指针->成员 等价于(*结构体指针).成员名

struct stu
{
	int age;
};
void SetAge1(struct stu stu)
{
	stu.age = 10;
}
void SetAge2(struct stu* pstu)
{
	pstu->age = 10;
}
int main()
{
	
	struct stu stu;
	SetAge1(stu);//拷贝了一份实参,改变的是拷贝的结构体变量,不会影响实参
	SetAge2(&stu);//拷贝了一份实参,通过拷贝的变量找到主函数结构体的成员 
	printf("%d\n", stu.age); 
}

2. 表达式求值

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

2.1 隐式类型转换(整型提升)

C的整型整形表达式总是至少以缺省整型类型的精度 (默认整形精度)来进行的。
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。

2.1.1 为什么要有整形提升?

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

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

通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。

所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送入CPU去执行运算。

2.1.2 整型提升的方法

整形提升是在补码的基础上提升,整形提升是按照变量的数据类型的符号位来提升的,提升后的二进制形式仍然是补码

1.有符号数整型提升高位补充符号位
2.无符号数整型提升高位补充0

//负数的整形提升
char c1 = -1;
变量c1的二进制位(补码)中只有8个比特位:
1111111
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为1
提升之后的结果是:
11111111111111111111111111111111
//正数的整形提升
char c2 = 1;
变量c2的二进制位(补码)中只有8个比特位:
00000001
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为0
提升之后的结果是:
00000000000000000000000000000001
//无符号整形提升,高位补0

2.1.3 整型提升的实例

//实例1
int main()
{
	char a, b, c;
	a = b + c;
}
表达式是整形表达式
b和c为char型,精度不足int,b、c整形提升为int型
b和c的补码进行相加,最后结果为32bit位
结果存储在char类型中,发生截断,取计算结果低8(存储在char a中的仍然是补码)
//实例2
int main()
{

	char a = 0xb6;		//10110110   
	short b = 0xb600;	//10110110 00000000
	int c = 0xb6000000; //10110110 00000000 00000000 0000000
	printf("%d\n", a); //a进行整型提升结果的补码为11111111 11111111 11111111 10110110
	printf("%d\n", 0xb6); //00000000 000000000 000000000 10110110

	printf("%d\n", b); //b会进行整形提升 结果的补码为11111111 111111111 10110110 00000000
	printf("%d\n", 0xb600); // 00000000 000000000 10110110 00000000

	printf("%d\n", c); //补码10110110 00000000 00000000 0000000 原码11001010 00000000 00000000 00000000
	printf("%d\n", 0xb6000000);//补码10110110 00000000 00000000 000000000

	//以下比较直接比较提升后的补码是否与右边值相等

	if (a == 0xb6)  //a提升为11111111 11111111 11111111 10110110  是负数,不相等
		printf("a");
	if (b == 0xb600) //b提升为11111111 11111111 10110110 00000000 是负数,不相等
		printf("b");
	if (c == 0xb6000000) //c是int型,不需要进行整型提升c的原码:10110110 00000000 00000000 00000000 
		printf("c\n"); //只会打印C
	return 0;
}

表达式a==0xb6被整型提升,其中char类型的a提升为int类型并表示为一个负值,因此这个表达式的结果为false;

表达式b==0xb600被整型提升,其中short类型的b提升为int类型并为一个负值,因此这个表达式的结果为false;

表达式c == 0xb6000000没有做整型提升,==运算符的两端都是int类型的负值,其结果为true。

在这里插入图片描述

//实例3
int main()
{
	char c = 1;
	printf("%u\n", sizeof(c)); //sizeof(char) 1 
	printf("%u\n", sizeof(+c)); //整形提升 sizeof(int) 4
	printf("%u\n", sizeof(-c)); //整型提升 sizeof(int) 4
	return 0;
}

注:整形提升也是一种运算 只是将这个数从栈帧中取出来,然后作为一种临时变量参与的运算 并没有真正改变被提升变量本身,只是临时变量被修改了


2.2 算术转换

如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行。下面的层次体系称为寻常算术转换
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iF88IilF-1675911123533)(null)]
为什么这里没有char short?
因为在表达式进行运算时char short隐式转换为int 进行运算
算术转换就是在int的基础上进行类型转换

2.2.1 算数转换实例

猜猜输出结果是什么?
//实例1
int main()
{
	int i = -1;
	if (i < sizeof(int))
	{
		printf("<\n");
	}
	else
		printf(">=\n");
	return 0;
}

在这里插入图片描述

sizeof运算的结果是size_t类型,无符号整形
intunsigned进行运算时,int会发生寻常算术转换为unsigned-1的补码11111111 11111111 11111111 11111111
而比较时会将-1的补码看做无符号数(INT_MAX)sizeof的结果进行比较
结果就是>=

//实例2
int main()
{
	int a = 1;
	double b = 2.1;
	printf("%d\n", sizeof(a + b)); //8
	return 0;
}
sizeof的操作数a + b是一个运算
运算的操作数一个是int型,一个是double型
根据寻常算术转换规则,int会转换成double在进行计算
最后表达式的结果就是double型数据
sizeof的结果就是sizeof(double)

2.3 操作符的属性

了解了各种操作符的含义和使用方法,我们想要计算表达式的结果,还需要知道各种是依据什么性质来控制表达式的求值顺序的

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

两个相邻操作符的执行顺序由它们的优先级决定。如果它们的优先级相同,它们的执行顺序由它们的结合性决定。
结合性决定了如果两个优先级相同的操作符作用于同一个操作数时先计算哪个操作符
除此之外,编译器可以自由决定使用任何顺序对表达式进行求值,只要它不违背逗号、&&、|和?:操作符所施加的限制。
注:仅当两个操作符作用于同一个操作数时,优先级和结合性的规则才有有效

在这里插入图片描述
在这里插入图片描述


2.4 问题表达式

并不是知道操作符的属性后所有的表达式都可以求出具体的值,如果不同的编译器对该表达式进行求值求出不同的结果(一个表达式有多种求值途径),这种表达式称为问题表达式

1.
int a, b, c, d, e, f;
int h = a * b + c * d + e * f;

1.表达式a * b + c * d + e * f是问题表达式
如果仅由优先级决定这个表达式的求值顺序,那么所有 3 个乘法运算将在所有加法运算之前进行。
事实上,这个顺序并不是必需的。
实际上只要保证每个乘法运算在它相邻的加法运算之前执行即可。
例如,这个表达式可能会以下面的顺序进行
1.a * b
2. c * d
3. (ab) + (cd)
4. e * f
5. (ab)+(cd) + (ef)
注意第1个加法运算在最后一个乘法运算之前执行。
如果这个表达式按以下的顺序执行,其结果是一样的:
1.a * b
2.c * d
3.e * f
4.(a
b)+(cd)
5.(a
b)+(cd)+(ef)
注:当a,b,c,d,e,f是会相互影响的表达式时
最终的结果在不同编译器上会出现差异

2.
int c = 1;
int b = c + --c;

表达式 c + --c 是问题表达式
操作符的优先级规则要求自减运算在加法运算之前进行
但我们并没有办法得知加法操作符的左操作数是在右操作数之前还是之后进行求值。
它在这个表达式中将存在区别,因为自减操作符具有副作用。
在 c 之前或之后执行,表达式的结果在两种情况下将会不同。

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

在不同编译器上的结果不同

在这里插入图片描述

4.
int main()
{
	int i = 0;
	int d = (i++) + (i++);
	printf("%d\n", d);
	return 0;
}

表达式d = (i++) + (i++)是问题表达式
这是在MSVC编译器上的结果
>
这是在gcc编译器上的结果
在这里插入图片描述
同样的表达式在不同的编译器上出现了不同解,这种表达式是问题表达式
我们反汇编看一下MSVC编译器如何处理这个式子
反汇编
因为前缀++是先拷贝操作数,再对操作数++,根据汇编代码推测MSVC编译器按如下步骤进行

  1. 拷贝一份i
  2. 拷贝另外一份i
  3. 将拷贝的进行相加存储到d中
  4. i++
  5. i++

再来分析gcc如何处理
结果是1不难想到按如下步骤进行处理

  1. 拷贝一份i,记作拷贝1
  2. i的值+1
  3. 拷贝一份i,记作拷贝 2
  4. i的值+1
  5. 拷贝1+拷贝2的结果存储在d中

这种答案不唯一的表达式都是问题表达式

5.

表达式f() + g() + h()是问题表达式
尽管左边那个加法运算必须在右边那个加法运算之前执行
但对于各个函数调用的顺序,并没有规则加以限制。
如果它们的执行具有副作用,比如执行一些 /0 任务或修改全局变量
那么函数调用顺序的不同可能会产生不同的结果。
因此,如果顺序会导致结果产生区别,你最好使用临时变量,让每个函数调用都在单独的语句中进行。
temp = f();
temp += g();
temp += h()

注:当在进行表达式运算时其中一个表达式可能会对另一个表达式产生影响需要将求值过程分成几步写


最后

看到最后,如果您觉得对您有帮助,请不要吝啬手中的赞,这对我来说很重要,也是我创作的动力,如果您觉得哪里说的不清楚或者有问题,欢迎评论区留言⭐️⭐️⭐️

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值