您的位置:首页 > 健康 > 养生 > 操作符详解(下)

操作符详解(下)

2024/10/10 14:26:47 来源:https://blog.csdn.net/2303_81091851/article/details/141757981  浏览:    关键词:操作符详解(下)

6、单目操作符

!、++、--、&、*、+、-、~、sizeof、(类型)

单目操作符只有一个操作数,除了&、*,剩下的我们之前讲过了,这两个我们再之后的指针我们再讲。

7、逗号表达式

表达式,表达式,表达式,表达式

用逗号隔开的多个表达式就是逗号表达式

逗号表达式依次从左往右执行,最后的表达式就是整个表达式的值;

比如下面代码,看c的逗号表达式,从左往右执行,a>b为假是0,a,这两个表达式没有任何影响,接着a+10,赋予b,b=11,最后b>a,为真,返回1就是整个表达式的结果;

int main()
{int a = 1;int b = 3;int c = (a > b, a, b = a + 10, b > a);return 0;
}

有时候,a<b,是这个判断的条件,那从左往右计算,是会影响到最后的表达式的结果的,所以逗号表达式前面计算也是重要的;

 if(a = b + 10,b = c / 5,a < b)

举个例子,假设有个APP函数的值赋予a,a又是CPP函数的参数,接着while循环,如果a>0,执行代码,执行完后,再继续之前的代码,那这样写就比较冗余,我们可以用逗号表达式写出来;

int main()
{a = APP();CPP(a);while (a > 0){//执行代码a = APP();CPP(a);}return 0;
}

用逗号表达式写的效果是一样的,先执行APP函数,CPP函数,再判断a是否大于0,再执行代码,循环完一次后再进行判断,再执行APP,CPP函数; 


int main()
{while (a = APP(),CPP(a), a > 0){//执行代码}return 0;
}

8、下标访问[ ]、函数调用(  )

8.1 [  ]下标引用操作符

比如我们有一个数组,我们想打印数组的第7个元素,那就是arr[6],访问数组的元素下标6,

那[ ]就是下标引用操作符,[  ]下标引用操作符的操作数有两个,一个是数组名,一个是元素下标,这两个操作数,少一个都不行,是找不到对应的元素的;

int main()
{int arr[10] = { 1,2,3,4,5,6,7 };printf("%d\n", arr[6]);return 0;
}

 8.2函数调用操作符

比如我们最简单的printf函数后面的括号( )就是函数调用操作符,add( )这个也是函数调用操作符,那函数调用操作符的操作数有几个?

这是不固定的,比如下面第一行代码操作数是函数名printf和字符串“hahaha”,操作数有两个;第二行代码操作数就是函数名add,参数2,参数3,操作数有3个;第三行代码,操作数只有一个函数名,因为它没有参数,只有函数名,那操作数就是1个;所以是不固定的;

int main()
{printf("hahaha\n");//( )操作数就是printf和“hahaha”int r = add(2, 3);//( )操作数就是add,2,3text();//( )操作数就是textreturn 0;
}

9、结构体成员访问操作符

 9.1结构体

C语言已经提供了内置类型,比如char、short、int、long、float、double,但是只有这些类型是不够的,比如要描述一个学生,学生的名字用char,但是学生的成绩、学生的体重、年龄呢?不能只用一种数据类型来描述学生,学生是一个复杂对象,那我们自定义一个合适的数据类型,结构体就是自定义的数据类型,

结构是一些值得结合,那这些值就是成员变量;

首先先声明结构体,那里面的变量就是成员变量,比如学生的名字,年龄,成绩;

成员变量可以有一个或是多个;

struct Stu
{char name[20];  //名字int  age;       //年龄float score;   //成绩
};

 9.1.1结构体的声明

先是结构体的关键字struct,tag就是结构体的名字,这个可以随便起,括号里面的就是成员变量列表,下面就是变量列表,可写可不写,但是最后的分号;一定要加上;

struct tag{member-list;}variable-list;

 9.1.2结构体变量的定义和初始化

我们定义了结构体类型,那类型就相当于一张图纸,有了图纸,我们就可以按照图纸去盖房子,我们有了学生的数据类型,我们就可以去创建一个“学生”;

那我们创建了数据类型,那就是数据类型+变量名;

int main()
{struct Stu s1;struct Stu s2;return 0;
}

那我们定义变量,那我们就可以初始化; 

那我们说数组给它多个元素,我们用括号,那结构体里面有这么多变量,我们也用括号括起来;

比如s1学生,名字是张三,年龄是18,成绩是95.5;

我们这是按着顺序给它赋值的,那能不能不按顺序给它数值呢?

struct Stu
{char name[20];  //名字int  age;       //年龄float score;   //成绩
};int main()
{struct Stu s1 = { "张三",18,95.5f };struct Stu s2 = { "李四",19,90.5f };return 0;
}

那我们可以通过结果成员访问操作符,来实现这个操作;

  .  是结构成员访问操作符,.name访问结构里的name...........

int main()
{struct Stu s1 = { .age = 18,.score = 95.5f,.name = "张三" };struct Stu s2 = { .score = 90.5f,.name = "李四",.age = 19 };return 0;
}

 那我们再复杂一点,说结构体里面还有一个结构体呢?

结构体Stu里面还有一个结构体BB,结构体BB里面有两个成员变量char a、int b;

那我们再初始化的时候,既然它还是一个结构体,那我们就还是用{ }括号给它赋值,就像s1那样;

也可以向s2那样,.bb.a、.bb.b、bb是个结构体也可以用结构成员访问操作符;

struct AA
{char a;int b;
};struct Stu
{char name[20];  //名字int  age;       //年龄struct AA  BB;float score;  //成绩
};int main()
{struct Stu s1 = { .age = 18,.score = 95.5f,.name = "张三" ,.BB = {"A",1}};struct Stu s2 = { .score = 90.5f,.name = "李四",.age = 19,.BB.a = "2",.BB.b = 1 };return 0;
}

9.2结构成员访问操作符

9.2.1结构体成员的直接访问

那创建好变量,赋值了,我们可以打印出来,比如我们打印s1学生的名字,用结构成员访问操作符,就是结构体变量.成员名;

int main()
{struct Stu s1 = { .age = 18,.score = 95.5f,.name = "张三" ,.BB = {"A",1}};struct Stu s2 = { .score = 90.5f,.name = "李四",.age = 19,.BB.a = "2",.BB.b = 1 };printf("%s\n",s1.name);printf("%d\n",s1.age);return 0;
}

10、操作符的优先级和结合性

 操作符有两个重要的属性就是优先级和结合性,决定了表达式求值的计算顺序。

10.1优先级

 比如在一个表达式里面有多个操作符,哪一个操作符先算呢?那不同的操作符的优先级是不一样的。

*的优先级比+的优先级高,所以先算5*6,乘法,再算3+30,加法;

3+5*6

10.2结合性

 如果两个操作符的优先级是一样的,那就是看结合性了,看它是左结合,从左往右计算,还是右结合,从右往左计算;大部分是操作符是左结合,左往右计算,但是有少部分是右结合,从右往左计算,比如赋值运算符 = 

* 和 / 的优先级是相同的,它们的结合性都是左结合运算符,那就是先算5*6,再 / 2;

5 * 6 / 2

 运算符的优先级顺序很多,我们只要大概记记了解一些常用的运算符优先级就行

下列按照优先级从高到低排列;

1、()圆括号;比如5 * 6 / 2,我就先算6/2,那就那就可以5 * (6 / 2);

2、自增运算符(++)、自减运算符(--)

3、单目运算符+、-

 4、乘法 *、除法  /

 5、加法+、减法-

6、关系运算符(<、>等)

7、赋值运算符 =

我们也有一个表格可以查看

https://zh.cppreference.com/w/c/language/operator_precedence

11、表达式求值

11.1整型提升

C语言中整型算术运算都是以整型类型的精度来计算的;

如果表达式中有char和short,那再使用之前先将它们转换为普通整型,那这种转换就叫做整型提升,char的底层是 ASCII码,它和short也被归为int类型里面;

那a+b就是整型提升;

int main()
{char a = 5;char b = 126;char c = a + b;return 0;
}

 整型提升的意义

表达式的整型运算是在CPU相应的运算器上运行的,CPU的整型运算器的操作数的字节长度一般是int的字节长度,因此,两个char类型相加,在CPU上实际也要先转换为CPU整型运算器操作数的字节长度,CPU是难以实现两个8比特位字节直接相加的,所以,表达式中各种长度小于int长度的整型值,都必须先转换位int后者unsigned int,再给CPU计算。

 如何整型提升?

1、有符号整数的整数提升是按照数据的符号位来提升的

2、无符号整数的整数提升,高位补 0

5的原码放在char里面,5的二进制位是32个比特位,char是8个比特位,放在char里面我们就要截断成8个比特位,126也是同样的道理;

a+b整型运算,对a,b整型提升,就要提升到32个比特位,在当前编译器char是有符号整型, 所以剩下的24个比特位,用这个数据的符号位来提升;

a和b整型提升后的结果是32个比特位,放在char里面也是要截断的;

当我们打印c,用%d打印,%d打印的是有符号的整型,%u打印的是无符号的整型,c还要整型提升,提升完后,这是c在内存的补码,但是打印的是原码,还要取反+1,得到原码;

这就i是char和short在整型运算的过程;

int main()
{char a = 5;//00000000 00000000 00000000 00000101//00000101 --截断char b = 126;//00000000 00000000 00000000 01111110//01111110 --截断char c = a + b;//00000000 00000000 00000000 000000101 --a整型提升//00000000 00000000 00000000 011111110 --b整型提升//00000000 00000000 00000000 100000011//截断//10000011 --c//用%d打印是有符号整型//对c整型提升//11111111 11111111 11111111 100000011 --补码//打印的是原码//10000000 00000000 00000000 011111101 c原码printf("%d\n",c);return 0;
}

11.2算术转换

 如果操作符的两个操作数的类型是不一样的,那就需要算术转化,另一个操作数的类型转换位另一个操作数的类型,是类型相同,可以运算;

算术转换是按照下面表格从下往上转换的;

假设一个操作数类型是float,另一个操作数是int类型,那就i是int操作数向float操作数转换变成float类型,其他的同理;

long double

double

float

unsigned long int

long int

unsigned int

int

 11.3问题表达式解析

11.3.1 表达式1

这个表达式是存在问题的,它的计算顺序不是唯一的;

a*b + c*d + e*f

 它的顺序可能是两种,都是符合表达式运算的优先级,结合性,但这不是唯一的计算路径,是会有两个结果的,比如我们写一个计算银行利息的代码,一个计算是50,另一个计算是50000,这些很可怕的;

11.3.2表达式2

这个表达式是有问题的,在优先级来说,先计算--,再计算+,但是这个表达式右边的--是知道了,那左边的c是--c之后的c,还是--c之前的c,是有争议的,这也不是唯一的计算路径。

 c + --c;

 11.3.3表达式3

这个代码是有问题的,这段代码放在不同的编译器结果是不一样的,里面的i的运算,编译器也会凌乱的,这种代码是不能这么写的,你要么拆分,存在变量里,再计算。


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

11.3.4表达式4

这段代码的fun函数里的变量用了static,变量的值是会累计的,fun函数第一次返回的是2,第二次返回的是3,第三次返回的是4,那再main函数里面谁是第一次返回,谁是第二次返回,谁是第三次返回,这是不确定的。

#include <sdtio.h>int fun(){static int count = 1;return ++count;}int main(){int answer;answer = fun() - fun() * fun();}printf( "%d\n", answer);return 0

11.3.5表达式5

这段代码也是有问题的,这段代码放在不同编译器的结果也是不一样的。

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

11.4总结

我们学了运算表达式的优先级,结合性,我们写出来的表达式不是万无一失的,通常这种表达式是有问题的,它计算的路径不是唯一的,对于以上的表达式,我们不要写成这样,我们可以根据需要,把这些表达拆分,或者用括号确定它的优先级,拆分用变量存起来,再去计算。

感谢观看,感谢指正!

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com