目录
9、条件操作符
10、逗号表达式
11、下标引用、函数调用和结构成员
12、表达式求值
12.1、隐藏类型转换
12.2、算术转换
12.3、操作符的属性
紧接上文,我们将继续进行操作符的讲解
条件操作符又叫三目操作符,使用形式如下
那它具体为什么意思呢?
实例应用,求两个数的最大值
int a = 5;
int b = 8;
int max = (a > b ? a : b); //a如果大于b的话会把a赋值给max,如果a不大于b 的话则会把b赋值给max
printf("%d\n", max);
逗号表达式,就是用逗号隔开的多个表达式。
逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。
int a = (1, 2, 3, 5); //逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。
//整个表达式的最后是5,5最终赋值给a
printf("%d\n", a);
[ ] 下标引用操作符(就是数组使用的下标引用符)
操作数:一个数组名 + 一个索引值
int arr[10] = { 1,2,3,4,5,6,7,8,9,10}; //创建数组
printf("%d\n", arr[5]); //打印数组下标为5的元素
() 函数调用操作符
函数名(参数)
于函数调用操作符来说,最少有1个操作数
接下来就是我们的结构成员。结构成员与结构体有一定的联系,在我们的C语言中我们存在两种类型。
一种是内置类型:代表的是我们的‘int’和‘char’。
而另一种则是我们的自定义类型:代表的是我们的结构体,枚举,联合体等。
今天我们就讲讲我们的结构体。
结构体是自定义类型,它也被我们称为——聚合类型。那么为什么要有我们的结构体。这是因为我们生活中有些东西写为C语言代码的时候,不能单纯的用内置类型。
我们先看一下结构体关键字:struct
作用:创建不同数据类型的一个集合。它的本质是变量的集合。
语法:
struct TypeName
{
Type1 var1;
Type2 var2;
....
TypeN varN;
};
定义好一个Struct结构体之后,就可以使用了:
比如我们出版一本书,书的话肯定要有书名,要有作者和售价这几样东西,但是它们的类型又不相同。
这时候我们就可以使用结构体,下面就是我们定义的结构体和使用
//struct Book
//{
// char name[30];//成员 书名
// char author[20];//作者
// float price;//售价
//};
//
//
//int main()
//{
// struct Book b1 = {"剑来", "烽火戏诸侯", 66.5f};//书
// struct Book b2 = {"不良人", "若森", 88.6f};//书
// printf("%s %s %.1f\n", b1.name, b1.author, b1.price);
// printf("%s %s %.1f\n", b2.name, b2.author, b2.price);
//
// //结构体变量.成员名
// return 0;
//}
上述代码涉及一个操作符 ,. 操作符用于取值:b1.name,b1.auther,这里的 . 可以理解为的,那么b1.name可理解为b1.name
除了 . 操作符,我们还有->操作符,使用如下
//void Print(struct Book * p)
//{
// printf("%s %s %.1f\n", (*p).name, (*p).author, (*p).price);
// printf("%s %s %.1f\n", p->name, p->author, p->price);
// //->
// //结构指针->成员名
//}
//
//int main()
//{
// struct Book b1 = {"剑来", "烽火戏诸侯", 66.5f};//书
// struct Book b2 = { "不良人", "若森", 88.6f };//书
// Print(&b1);
// Print(&b2);
// return 0;
//}
->操作符用于指针的取值,(*p).name与p->name的计算结果是相同的
总结
. 操作符用于取值:b1.name(取值为66.5)
->操作符用于指针的取值:p->auther(取值为若森)
C语言的整形算术运算总是至少以缺省整形类型的精度来计算的。
为了获得这个精度,表达式中的字符和短整型操作数在使用之前会被转换为普通整形,这种转换被称为整形提升。这是什么意思呢?我们写一个代码来说明一下。
//int main()
//{
// char c1 = 5;
// char c2 = 127;
// char c3 = c1 + c2;
// printf("%d\n", c3);
// return 0;
//}
结果为-124;在我们这个代码中,我们需要求c的值,但是在这里我们并不是只是将a的值和b的值相加赋给c就行。在这个代码计算的过程中,我们要先将a和b转换为整形,计算出来结果也是整形,最后将这个整形赋值给c。在计算过程中,我们一般将大小小于整形类型的类型进行提升,也就是将我们的字符类型和短整型类型提升至整形类型后再进行计算,这就是我们的整形提升。
那有些人可能就会问了,这样做的意义在哪儿呢?那我们接下来就看看整型提升的意义:
搞清楚意义后有些人就想知道到底是怎么整型提升了,-124又是怎么的出来的呢?请看下文:
看到这里有些宝子可能还是不太懂,没关系我们看一下实例计算
//int main()
//{
// char c1 = 5;
// //00000000000000000000000000000101 //补码
// //00000101 - c1 (截断)
// char c2 = 127;
// //00000000000000000000000001111111 //补码
// //01111111 - c2
// char c3 = c1 + c2;
// //00000000000000000000000000000101 (c1提升后)
// //00000000000000000000000001111111 (c2提升后)
// //00000000000000000000000010000100 //补码
// //10000100 - c3(截断)
// //%d - 10进制的形式打印有符号的整数
// //11111111111111111111111110000100(提升) - 补码
// //11111111111111111111111110000011 -反码
// //10000000000000000000000001111100 --> -124
// printf("%d\n", c3);
// //
// return 0;
//}
如果操作符的各个操作属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行。下面的层次体系为寻常算数转换
如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另一个操作数的类型后执行预算
警告:
算数转换要合理,不然会有一些潜在问题
隐形转化
float n = 3.14;
int f = n;//精度缺失
在C语言中,我们复杂表达式求值有3个影响因素。
1.优先级
优先级表请看博主在操作符详解(1)里的关于优先级的表;
如果在一个表达式里面我们无法确定优先级的时候,我们可以采取加括号的方式。 另外需要注意:只有相邻操作符才需要讨论操作符的优先级。
如果相邻操作符的优先级一样的话,这个时候就取决于我们的结合性。
2.结合性
关于结合性,操作符详解(1)的表也有介绍
3.是否控制求值顺序
虽然我们了解了我们复杂表达式求值有3个影响因素。但是即使是这样我们也不能确定我们表达式计算的唯一路径。
在两种方法都是可以的。在这个代码中我们只能确定乘号比加号早,但是无法确定哪个乘法比加号早。这种表达式我们称之为问题表达式。
这里有人就要问了,这里的答案不都一样的吗?那么接下来我就再写一个问题表达式。
这道题呢就是一个问题表达式,不同的编译器呢就会有不同的结果
在C语言中,我们的函数调用也会受到问题表达式的影响
//int fun()
//{
// static int count = 1;
// return ++count;
//}
//int main()
//{
// int answer;
// answer = fun() - fun() * fun();
// printf("%d\n", answer);
// return 0;
//}
在这个代码中我们只能确定乘号比减号先算,但是无法确定哪个函数优先调用。因此它也会产生不同的结果,这个代码也是我们的问题代码。
所以在我们的代码中我们无法考我们操作符的优先级来确定我们表达式的唯一路径的式子都存在着一定的问题,我们写代码的过程要尽可能的避免问题表达式的书写。