脆皮之(进制转换讲解)‘C语言操作符详解’ (包含结构体)

                 大家好吖,我是脆皮炸鸡

这一节是补充之前没有涉及到的操作符,其余操作符可以见之前的一篇笔记脆皮猛冲C语言之“数据类型和变量”and“分支与循环”

如果这一章节对大家有帮助的话,留个小心心鼓励一下我叭,我比较喜欢追动漫,动漫结尾经常听的一句就是“点赞加关注,追番不迷路”,哈哈哈哈,大家懂我的吧!!

在这里插入图片描述

(一). 二进制和其他进制转换

同样一个数字,可以写成二 / 八 /十 /十六进制

1.1 先讲解什么是二进制,八进制,十进制,十六进制

(从大家最熟悉的十进制入手)
(1)十进制:含有数字0到9,满10进1。(例如:9之后是10)
(2)二进制:只含有数字0和1,满2进1。(例如:满二进一,2用二进制表示为10,3就表示为11,4表示为100)
(3)八进制:含有数字0到7,满8进1。(例如:满八进一,8用八进制表示为10,9表示为11)
(4)十六进制:含有数字0到9,以及字母a-f,(a是10,b是11…f是15)满16进1。(例如:17用十六进制表示为11)

  • 十进制的123为什么是一百二十三呢?十进制的每一位都是有权重的,(从右到左)个位,十位,百位的权重依次是10的0次方, 10的1次方 , 10的二次方(图片表示)
  • 其他进制与十进制相似,每一位都是有权重的,举例二进制,个位,十位,百位的权重依次是2的0次方, 2的1次方 , 2的二次方
    在这里插入图片描述

1.2 十进制转二进制

以十进制的n转换为二进制为例
将n/ 2,余数留下,商继续 / 2,直到商为0 。最后将所得的余数倒着写出,就是125的二进制了(任何数除以2,它的余数肯定是0或者1)
在这里插入图片描述

  • 在此大家可以尝试写一个代码,将十进制数字转化二进制
void er(int x)
{
	if (x / 2 != 0) {         //写的时候先想好递归终止条件(这里是商=0时递归结束)
		er(x / 2);
		printf("%d", x % 2);
	}
	else if (x / 2 == 0) {         
		printf("%d", x % 2);
	}
}
int main()
{
	int n = 0;
	scanf("%d", &n);
	er(n);
}
  • 如果十进制的这个数字比较小,可以直接凑一下(如图所示)
    在这里插入图片描述

1.3 二进制转八进制

先看一下八进制的数字用二进制都是如何表示的

在这里插入图片描述

  • (1)由此可见,八进制的任何一个数字,最多需要3个二进制位,不够三位,在前面补0。
  • (2)0开头的数字,才表示八进制(以123举例,0123是3*1+2*8+1*64=83)
    在这里插入图片描述
  • (3)这一步学习如何将二进制转换为八进制:从右往左,每三位看作一个;如果到最左边不够三位,在前面补0。
    在这里插入图片描述

1.4 二进制转十六进制

先看一下十六进制的数字用二进制都是如何表示的

在这里插入图片描述

  • (1)由此可见,十六进制的任何一个数字,最多需要4个二进制位,不够四位,在前面补0。
  • (2)0x开头的数字,表示十六进制。
  • (3)这一步学习如何将二进制转换为十六进制:从右往左,每四位看作一个;如果到最左边不够四位,在前面补0。
    在这里插入图片描述

(二).移位操作符

移位操作符:移动的是存储在内存中的二进制位(补码)

所以在此之前,我们先讲解一下什么是补码

2.1 原码,反码和补码

在了解二进制的操作符之前,我们需要先明白什么是原码,反码和补码。

当我们要把一个数转换成二进制表示的时候,整数的二进制有三种表示形式(此处先不考虑浮点数):原码,反码和补码。

有符号整数(有正有负)的原码,反码和补码,都是由符号位(1位)和数值位(31位)组成。二进制序列中,最高位的那一位是符号位(正是0,负是1),其余的31位是数值位(int默认的是signed int,int的长度是4个字节,即32个比特位)

对于无符号整型(unsigned int),它根本就没有符号,也就没有符号位,所有的位都是数值位

  • 原码:直接将数值按照正负数的形式翻译成二进制得到的就是原码。
  • 反码:原码的符号位不变,其余位按位取反(~)
  • 补码:反码+1在这里插入图片描述

正整数的原码,反码,补码相同

int b = 10;
//原码00000000000000000000000000001010
//反码00000000000000000000000000001010
//补码00000000000000000000000000001010

负整数的原码,反码,补码各不相同

int b = -10;
//原码10000000000000000000000000001010
//反码11111111111111111111111111110101
//补码11111111111111111111111111110110

对于整数来说,储存在内存中的其实是补码(看到一个数字,先写原码,再取反加一)

  • 两个数据运算时,是用它们的补码在计算。用补码运算,记住:补码补码补码
    好处一:使用补码,可以将符号位和数值域统一处理
    好处二:加法和减法也可以统一处理(CPU只有加法器)
    好处三:补码与原码相互转换,其运算过程是相同的(取反加一),不需要额外的硬件电路

2.2 移位操作符

1.移位操作符移动的是存储在内存中的二进制位(补码),且操作数只能是整数
2.对于移位运算符,不要移动负数位(比如向左移动-3位)

  1. 左移操作符:<<
    向左移动一位,最左边的丢弃,最右边补0。
    在这里插入图片描述
int main()
{
	int a = -9;  //a是负整数,补码计算
	               //原码:10000000000000000000000000001001
	               //反码:11111111111111111111111111110110
                   //补码:11111111111111111111111111110111
	int b = a << 1;
	//      左移一位的补码:11111111111111111111111111101110   
	//               取反:10000000000000000000000000010001
	//(这一步得到原码)加一:10000000000000000000000000010010      
	                                      //1*2(1)+1*2(4)=2+16=18
	printf("%d\n", a); //此处,a是不变的,b是向左移一的a
	               -9    //如果想让a向左移一,可以写成int a = a<<;
	printf("%d\n", b); //b是向左移一的a
	               -18
}
int main()
{
	int a = 9;   //a是正整数,原码即补码
	//补码:00000000000000000000000000001001    //9
	//左一:00000000000000000000000000010010   //18
	//左移一之后是补码,正整数补码即原码
	int b = a << 1;
	printf("%d\n", a);
	printf("%d\n", b);
}

从中可以发现:左移一位有乘二的效果

2 . 右移操作符: >>
右移有两种:算术右移(右边丢弃,左边“原符号位”补)和逻辑右移(右边丢弃,左边0补)哪种方式右移是编译器决定的(通常采用的是算术右移)

  • 右移一有除二的效果(移位的那个数是偶数时)
int main()
{
	int a = 10;
	//原码:    00000000000000000000000000001010 正数的原码就是补码
	int b = a >>1 ;
	//右一补码:00000000000000000000000000000101 由开头的0看出是正数,补即原,5
	printf("%d\n", a);
	printf("%d\n", b);

}

(三)位操作符

3.1操作符

  • 操作数同样必须是整数
  • 在进行运算时,将补码分别写成两行且对照

按位与:&(双目操作符)(一列都是1,才是1;有0则为0 <全一则一,有0则0>)

按位或:|(双目操作符)(有1则1,全0则为0)

按位异或:^(双目操作符)(相同为0,相异为1)(好处:用异或符交换两个整数,不会出现栈溢出)

按位取反:~(单目操作符)(按照二进制位全部取反,包括符号位)

  • 注意:
  • 操作数同样必须是整数
  • 在进行运算时,将补码分别写成两行且对照

区分之前的逻辑操作符
&&(逻辑与),||(逻辑或),关注操作符两边的真假。
&(按位与) , | (按位或),关注的是二进制位的运算

区分两种不同的&
a & b, 此处的&是双目操作符,按位与
printf(“%d”,&a), 此处的&是单目操作符,取地址符

int main()
{    //按位与&  //全1则1,有0则0
	int num1 = -3;
	//原码:10000000000000000000000000000011
	//反码:11111111111111111111111111111100
	//补码:11111111111111111111111111111101
	int num2 = 5;
	//补码即原码:00000000000000000000000000000101
	//将两个补码放一起,两行对齐:同一列:按位与&:全1则1,有0则0
	    //11111111111111111111111111111101
	    //00000000000000000000000000000101
		
	//&为:00000000000000000000000000000101 即5,正整数的补码即原码(同一列:按位与&:全1则1,有0则0)
	//开头是0则正数,不用再求原码;开头是1,求原码 
	
	//|为:11111111111111111111111111111101 接下来补变原(|:有1则1,全0则0)
	//原: 10000000000000000000000000000011 即-3

	//^为:11111111111111111111111111111000 开头1,求原码:取反加一
	//原: 10000000000000000000000000001000 即-8

	   //补码:11111111111111111111111111111101
	//~num1为:00000000000000000000000000000010 正数,补即原

	printf("%d\n", num1 & num2); //5
	printf("%d\n", num1 | num2); //-3
	printf("%d\n", num1 ^ num2); //-8
	printf("%d\n", ~num1); //2

}
  • 不用第三个变量就可以交换两个数的方式(之前是创建临时变量:效率高)
  • (1)这种方式,如果a和b太大的话,会导致栈溢出
int main()
{
	int a = 9;
	int b = 2;
	a = a + b;  //a=11
	b = a - b;  //b= 11-2 =9
	a = a - b;  //a= 11-9 =2
	printf("%d %d", a, b);
}

(2)用异或符不会有这种情况

a = a ^ b; 
b = a ^ b;
a = a ^ b;

先讲解一下,如果两个相同的数字按位异或会怎么样(相同为0,相异为一)。3^3

(1)相同的两个数字,按位异或之后等于0 (a^a=0)
3:00000000000000000000000000000011
3:00000000000000000000000000000011
^:00000000000000000000000000000000(即0)
(2)0和任意一个数字按位异或,结果还是那个数字 (0^a=a)
那0^3呢
3:00000000000000000000000000000011
0:00000000000000000000000000000000
^:00000000000000000000000000000011(即3)
(2)0和任意一个数字按位异或,结果还是那个数字 (0^a=a)

(3)异或支持交换律:a ^ b ^ a = a ^ a ^ b(=0 ^ b=b)

3.1.1小练习:求⼀个整数存储在内存中的⼆进制中1的个数。

方法一:

回想一下之前,十进制如何转为二进制,while(%2,再/2)

int erjinzhi(unsigned int x)  //为什么要用unsigned int,而不是int?
{
	int count = 0;
	while (x)  //要想好限制条件:当x为0时就不用再%2,/2
	{
		if (x % 2 == 1) {
			++count;
		}
		x = x / 2;
	}
	return count;
}
int main()
{
	int a = 0;
	scanf("%d", &a);
	int c = erjinzhi(a);
	printf("%d", c);
}

输入负数的话,如果输入的参数是int(有符号整形,有+有-),以-1为例,
原:10000000000000000000000000000001
反:111111111111111111111111111111111110
补:111111111111111111111111111111111111
应该是32个1,但输出是0,原因如下:
在这里插入图片描述

在这里插入图片描述

方法二(n&1)

这种方法不用在意是有符号还是无符号

-1补码:1111111111111111111111111111111 1 将补码&1
 1补码:0000000000000000000000000000000 1 
-1&10000000000000000000000000000000 1 

&:有0则0,而1的前面31位是0。则1不管&谁,前面31位将都是0,即起决定性的一位是最后一位,即32位。若是想判断n的倒数第二位,可以将n右移一.【将这一个完整的过程放在循环中即可,记得写上循环终止条件】
这种方法不管有几个1,都需要循环32次

int erjinzhi(int x)
{
	int count = 0;
	while(x!=0)  //x没有变为0之前,一直可以循环
	{
	    int r = x & 1;
		if (1 == r) {
			++count;
		}
		x = x / 2;
	}
	return count;
}

int main()
{
	int n = 0;
	scanf("%d", &n);
	int r = erjinzhi(n);
	printf("%d", r);
}
第三种方法 n=n&(n-1)

探索一下n&(n-1)

在这里插入图片描述
从中可以发现:每n&(n-1)一次,就少一个0.
每执行一次n=n&(n-1),就会减少一个n的二进制序列中最右边的1
这种方法比上一种方法好,原因在于:有几个1循环几次,不用32次

在这里插入图片描述
那循环了几次呢?
在这里插入图片描述

while(x != 0)
{
	n = n & (n - 1);
	++count;
}

3.1.2小练习:判断a是否为2的次方

如何知道a是否为2的次方,先想一下2的次方的二进制表示成什么。一次方(2):10;二次方(4):100;三次方(8):1000;可以从中发现不管二的几次方,都只有1个1。


方法一:a&1,如果按位与32次,只有一次是1,可证明他是2的次方

方法二:n&(n-1),每执行一次少一个1。
在这里插入图片描述

3.1.3小练习:将a的⼆进制位的第n位,变为0或者1

先从简单的开始,编写代码将13⼆进制序列的第5位修改为1,然后再改回0

13的二进制表示:00000000000000000000000000001101(第五位修改为1,可以考虑在第五位按位或1(有1则1,全0则0),如果原来是0,按位或1之后,则变为1;如果原来是1的话,按位或之后还是1。接下来需要考虑如何第五位按位或1,数字1是000000000000000000000000000000000001,将第五位变为1,最后一位变为0,可以采用左移4位

那之后如何再改回0呢?可以将第五位按位与0,其他位置按位与1。&:全1则1,有0则0。(第五位按位与0。如果第五位原来是1,按位与0之后就会变为0;原来是0,按位与0还是0)(其余位置按位与1,如果原来是1,&1之后还是1;原来是0,&1之后还是0)

所以我们需要按位与的是什么呢:11111111111111111111111111101111
如何得到这个数字呢?  取反q: 00000000000000000000000000010000 
那q是什么呢?不就是1<<4
//将13的第五位数字变为1,再变回0
int main()
{
	int a = 13;  //1101 即13
	a = a | (1 << 4);  //11101 即29
	printf("%d\n", a);  
	a = a & (~(1 << 4));  //又变回1101了
	printf("%d\n", a);  
}

扩展:

int main()
{
	int a = 0;
	int n = 0;
	printf("将a的/⼆进制位的第n位,变为0或者1,请输入a和n\n");
	scanf("%d %d", &a,&n);
	a = a | (1 << (n-1));
	a = a & (~(1 << (n - 1)));
	printf("%d\n", a);
	return 0;
}

(四)单目操作符

单⽬操作符有这些:
!、++、–、&、*、+、-、~ 、sizeof、(类型)
只剩下 *(解引用操作符)没有讲解,在指针的时候再做讲解

(五)逗号表达式

  1. 逗号表达式就是用逗号隔开的多个表达式
    exp1, exp2, exp3, …expN
  2. 逗号表达式,从左到右依次执行,最后一个表达式的结果即整个表达式的结果。
  3. 不要跳过前面的表达式直接计算最后一个表达式作为整个表达式的结果,因为前面表达式的计算可能会影响后面表达式的计算
int a = 1;
int b = 3;
int c = (a>b,a=b+10,a,b=a+1);
printf("%d",c);
如果直接计算最后一个,结果就是b=1+1=2,c就是2
但如果从左到右依次计算:b=12.c也是
  1. 也可以这样用:if(逗号表达式)
if (a =b + 1, c=a / 2, d > 0)
  1. 也可以用于while循环,将前面几个表达式理解为先执行的(即do while中的do),最后一个表达式作为是否进入循环的判断条件

(六)下标引用操作符[ ],函数调用操作符()

  1. 下标引用操作符[ ],[ ]的操作数有两个:一个是函数名,一个是下标(即索引值)
int man[10]={0};  
int w = man[3];  //man是操作数;索引值3也是操作数
  1. 函数调用操作符( ),( )的操作数至少1个:就是函数名
    因为有的自定义的函数不用传参。
print();  //操作时:1个 print
printf("haha");  //操作数:2个.函数名printf," "里面的haha
printf("%d",a);  //操作数:3个.printf," "里面的%d, a
int w = Add(3,5); //操作数:3个.Add,3,5

(七)结构成员访问操作符

7.1 结构体

c语言已经提供了内置类型,如:char,int,long,float,double,但光有这些是不够的,他们比较单一,不够我们描述一些复杂对象,比如:一名学生(int年龄,char名字,float成绩等等)为了描述对象中各种不同的类型,C语言增加了结构体自定义的数据类型),让程序员可以自己创造适合的类型。

结构是一些值的集合,这些值称为成员变量。在此之前我们学习了数组,数组也是一些值的集合,只不过这些值必须是同一类型。而结构的每个成员可以是不同类型。(比如int,数组,指针,其他结构体)

  1. 结构体的声明:
    结构体的关键字是struct,
struct 标签名(自己写)
{
    各个成员变量组成了成员列表
	成员列表;(1个及以上)
}变量列表;

举个例子:int a ;在这里面,int是类型,类型用来创建整型变量(int类型创建了int变量a),我写了一个

struct Student
{
	char name[20];
	int age;
	float score;
};

int是类型,那我写的这个类型是什么呢?是struct Student,在类型后面还要写创建的变量。完整的结构体的声明是struct Student s1(struct类型创建了struct变量s1)
在这里插入图片描述

结构体(数据类型)类比于图纸,不能住人,即不能存放数值。它仅仅只是一个模板,之后还需要用模板把东西做出来,即之后还需要用类型去创建变量,在变量里面才能存放我们的数据

  1. 结构体变量的初始化
    数组里面有多个值,在初始化时使用大括号。所以可想到,结构体变量初始化时也使用大括号。

7.2 结构体成员的直接访问

结构体成员的直接访问是通过点操作符(.)访问的,点操作符接受两个操作数
使用方式:结构体变量.成员名

struct zuobiao
{
	int x;
	int y;
};
struct student
{
	char name[20];  //姓名
	int age;        //年龄
	float score;    //成绩
	struct zuobiao k;  //此处对应了成员变量也可以是其他结构体
	int arr[][9];   
};
int main()
{
	struct student s1 = { "翠花", 20 , 98.0 , {3,9} ,       {2,3} }; //逗号隔开
                                   //结构体变量初始化用{}  数组也是
	printf("%c\n", s1.name);
	printf("%d\n", s1.age);
	printf("%f\n", s1.score);
	printf("坐标是%d %d\n", s1.k.x, s1.k.y);
	printf("%d\n", s1.arr[0][0]);
	return 0;
}                                   

还有一个方式:使用方式:结构体指针->成员名(这个在之后讲)–现在补充上,大家可以在看完指针之后看。

struct stu
{
	char name[20];
	int age;
};

int main()
{
	struct stu q = { "zhangsan",34 };
	printf("%s %d\n", q.name, q.age);

	struct stu* ps = &q;
	//将struct stu理解为int即可
	printf("%s %d\n", (*ps).name, (*ps).age);
	printf("%s %d\n", ps->name, ps->age);
}

(八)操作符的属性:优先级、结合性

操作符是用来构建表达式的,那当我们用操作符构建了一个复杂的表达式,这个表达式如何执行呢?在执行的时候需要知晓操作符的优先级,结合性。这两个属性决定了表达式求值的计算顺序

先看优先级,如果两个运算符优先级相同,优先级没办法确定先计算哪个了,这时候就看结合性

    1. 何为优先级?
      优先级指的是,如果一个表达式包含多个运算符,哪个运算符应该优先执行。各种运算符的优先级是不一样的。

(1)相邻的两个操作符,优先级不同的情况下,选择优先级高的先执行
如果中间相隔了好几个操作符,是不考虑的
(2)也可以使用括号改变执行顺序(原因:由于圆括号的优先级最高,可以使用它改变其他运算符的优先级。)

int a = 3+4*5;  //此处先执行乘法,再加法
int a =(3+4)*5;  //先加法,后乘法

操作符的优先级顺序很多,下面列出最常用的(优先级从高到低)
圆括号( )
自增自减运算符(后置优先级更高)
正+,负-
乘除法* /
加减法±
关系运算符< ,>
赋值运算符=
优先级链接
2. 何为结合性?
根据运算符是左结合,还是右结合,决定执行顺序。大部分运算符是左结合(从左到右执行),少数运算符是右结合(从右到左执行),比如赋值运算符( = )

比如5*6/7在这个例子中,和/优先级相同,都是左结合运算符,所以从左到右结合。先56,再/7。

(九)整型提升和算术转换

整型提升讨论的是表达式中char和short int类型的值(字节长度比int短的)

算术转换讨论的是类型≥int类型的类型,例如:
(long double)>=8
(double)8
(float) 4
(unsigned long int,long int,)>=4
(unsigned int,int) 4

字节长度看这里链接

整型提升

char a = 10;
char b = 140;
char c = a + b;

如果表达式中出现字符char和短整型short int,要在进行计算之前将char和short int转换为普通整型,这个过程称为整型提升。

  1. 为什么要进行整型提升呢?

(1)(表达式的整型运算) 在 (CPU的相应运算器件内)执行,CPU内 整型运算器(ALU)的操作数字节长度一般就是int的字节长度(4个字节,即32个比特位),这也是CPU的通用寄存器的长度。

  1. 假设此时两个char类型的值相加,CPU执行时需要先将其转换为ALU操作数的标准长度(即int的字节长度,4个字节,即32比特位),为什么呢?通用CPU难以直接将两个8比特字节直接相加运算,所以,表达式中char,short int(各种长度可能小于int长度的整型值),都必须先转换为int或unsigned int,然后才能送入CPU去执行运算。
  • (1)先将值提升为普通整型(方法随后),然后再执行加法运算。
  • (2)加法运算完成之后,将结果截断,然后再存储于a中。
  • (3)在打印时,需要再次整型提升。
  1. 整型提升是真的提升了,但这个过程是悄咪咪完成的,我们体会不到

  2. 如何进行整型提升?(即如何将值提升为普通整型)
    (1)判断即将整型提升的值是 有符号整型 还是 无符号整型
    比如:在vs中,char默认是signed char,int默认是signed int
    (2)有符号整数的提升:按照变量的数据类型的符号位来提升。
    (3)无符号整数的提升:高位补0。
    “变量的数据类型的符号位” 这句有点长,我将在接下来的例子中解析一下

int main()
{
	char a = 20;
	//00000000000000000000000000010100 这是20的原码,也是补码
	//原本想将这32个比特位放进char创建的变量a里,但是char的字节长度是1,即8个比特位,放不进去32比特位
	//所以我们选择截断(低位)最后八位
	//最后char创建的变量a里,真正放的是:00010100 
	char b = 130;
	//同理b:10000010
	char c = a + b;
	//在进行运算之前要进行整型提升,因为运算时ALU的操作数字节长度是int的字节长度,char它不到一个整形的大小
	//a和b都是signed char,之后的提升按照有符号整型的提升(即按照变量的数据类型的符号位来提升)
	//何为:变量的数据类型的符号位
	//变量a的数据类型是signed char,它的补码是8比特位的,它的符号位是由8比特位的第一个数字决定,0
	//b同理,只是它的补码的第一个数字是1
	//a:00000000000000000000000000010100 整型提升之后的a
	//b:11111111111111111111111110000010 整型提升之后的b
	//将两个补码相加11111111111111111111111110010110 再将截断的补码存储在c中
	//截断低位(8位)10010110 这是存储的c的补码
	printf("%d", c);
	//%d是打印有符号的整数,所以这里需要对c有一次整型提升(有符号整型的提升)
	//11111111111111111111111110010110 补码(接下来取反加一)
	//10000000000000000000000001101001,再加一10000000000000000000000001101010,即-106
}
  1. 注意:char类型的取值范围是-128到127
    一共8比特位,最左边的是符号位,剩下的7位是数值位,假设全都是1,01111111计算一下这是多少,计算结果是127

算术转换

算术转换讨论的是类型≥int类型的类型,例如:
long double
double
float
(unsigned) long int
(unsigned) int

先举个例子,假设a+b,a是int,b是long int,如何计算呢?我们需要把小类型的转换为大类型的,使两个类型一致,再继续进行运算。在这里就是将a的类型转换为long int。

如果某个操作符的各个操作数(简单来理解就是:一个操作符两边的数字)属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行。那到底是谁转换成谁呢?【如果某个操作数的类型在上面这个列表中排名靠后,那么就将排名靠后的类型转换为另外一个操作数的类型后执行运算】

举例运用操作符时会遇到的问题

即使有了操作符的优先级和结合性,我们写出的表达式依然有可能不能通过操作符的属性确定唯一的计算路径,那这个表达式就是存在潜在风 险的,建议不要写出特别复杂的表达式。

例一

m = a * b + c * d + e * f ; 
//    1   2   3   4   5    

在这个式子中到底是谁想进行呢?哪个操作符先执行?

在这里面,我们只能确定相邻操作符的优先级,比如:2比1先,2比3先,5比4先;并不能确定先将ab和cd先加,还是先执行后面的ef,因为2的+和5的并不相邻。

有可能是 1 3 2 5 4
也可能是 1 3 5 2 4

这个例子没有唯一的计算路径,也不影响结果,但如果a,…,f 不是数字,而是复杂的表达式,谁先算谁后算,影响就大了。尽量不要写路径不确定的式子

例二

  • 不建议在一个表达式中反复使用一个变量
int c =1;
int m = 0; 
m = c + --c;

此处我们知道优先级:前缀减减(–)>加法(+),这个我们确实知道。但是+左边的c什么时候给准备好呢?是在–前就准备好了(这样的话,左边的c就是1)还是–后再准备(左边的c就是0)呢,这就有差异了。

例三.在表达式中加入函数

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

我们只知道先*后+,但是不知道函数的调用先后顺序,第一次调用,return 2,之后再调用是3,4

好啦,关于操作符的知识就到此结束啦!
感谢大家观看!!!
在这里插入图片描述

  • 32
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值