目录
- 一、结构体成员访问操作符
- 1.1 结构体
- 二、操作符的属性:优先级、结合性
- 2.1 优先级
- 2.2 结合性
- C 运算符优先级
- 三、表达式求值
- 3.1 整型提升
- 3.2 算数转化
- 总结
一、结构体成员访问操作符
1.1 结构体
C语言已经提供了内置类型,如:char,short,int,long,float,double等,但是只有这些内置类型还是不够的,假设我想描述学生,描述一本书,这时单一的内置类型是不行的。
描述一个学生需要名字,年龄,学号,身高,体重等
描述一本书需要作者,出版社,定价等。C语言为了解决这个问题,增加了结构体这种自定义的数据类型,让程序员可以自己创造适合的类型。
注:结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量,如:标量、数组、指针,甚至是其他结构体。
这里可以和数组相比较
数组:是一组相同类型的元素的集合
struct tag
{
member-list;
}variable-list;//变量列表
这里的member-list可能有多个值,值叫成员变量。variable-list变量列表可有可无。
//声明了一个结构体类型
struct Stu
{
//1个或多个成员 - 这些成员都是用来描述学生的
char name[20];
int age;
char id[11];
float score;
//...
} s6,s7,s8;//全局变量
其大体形式就如图示代码,s6,s7,s8就是变量列表,其相当于在声明类型的时候顺便创建了几个变量,相当于全局变量,也可以不写,在下面单独创建。
//声明了一个结构体类型
struct Stu
{
//1个或多个成员 - 这些成员都是用来描述学生的
char name[20];
int age;
char id[11];
float score;
//...
} s6,s7,s8;//全局变量,声明类型的同时定义s6,s7,s8
struct Stu s4;//全局变量,定义结构体变量s4
struct Stu s5;//全局变量
int main()
{
int num = 0;
struct Stu s1 = {"zhangsan", 20, "2024010102", 85.5f};
struct Stu s2 = {.age=18, .name="wangcai",.score=95.5f,.id = "2025010102"};//指定顺序初始化
struct Stu s3;//局部变量
return 0;
}
编译器在默认情况下会将85.5认定为double类型,所以要加一个f,搞成float类型。
而且在默认情况下,结构体的初始化变量只能按顺序初始化,即按结构体的声明顺序来创建,如果想要指定初始化顺序,就要使用操作符(.)来访问结构体成员了。
使用方法:结构体变量.成员名
结构体也可以嵌套使用,即在结构体中引用结构体:
//声明了一个结构体类型
struct Peo
{
char name[30];
int age;
char tele[12];
};
struct Ebook
{
struct Peo data[100];//可以存放100个人的信息
int count;//当前已经存的个数
};
int main()
{
struct Peo p1 = {"zhangsan", 20, "15598888888"};
struct Ebook eb = { {{"wangwu", 19, "13396668866"},{"cuihua", 18, "18696866688"}}, 0};
//printf("%s\n", p1.name);
//printf("%d\n", p1.age);
//printf("%s\n", p1.tele);
printf("%s\n", eb.data[1].name);
printf("%d\n", eb.data[1].age);
printf("%s\n", eb.data[1].tele);
//. : 结构成员访问操作符
//结构体变量.结构体成员
return 0;
}
这里也不太好说,直接看监视窗格就懂了:
二、操作符的属性:优先级、结合性
C语言的操作符有两个重要的属性:优先级、结合性,这两个属性决定了表达式求值的计算顺序。
2.1 优先级
优先级指的是,如果一个表达式包含多个运算符,哪个运算符应该优先执行。各种运算符的优先级是不一样的。
3 + 4 *5
该示例中,表达式3+45里既有加法运算符,又有乘法运算符。由于乘法的优先级高于加法,所以会先计算45,而不是先计算3+4.
2.2 结合性
如果两个运算符优先级相同,优先级没办法确定先计算哪个了,这时候就需要看结合性了,则根据运算符是左结合,还是右结合,决定执行顺序。大部分运算符是左结合(从左到右执行),少数运算符是右结合(从右向左执行),比如赋值运算符(=)
5 * 6 / 2
该示例中,*和/的优先级相同,他们都是左结合运算符,所以从左到右执行,先计算5×6,再计算/2。
运算符的优先顺序很多,下面是部分运算符的优先级顺序(按照优先级从高到低排列)
- 圆括号(())
- 自增运算符(++),自减运算符(–)
- 单目运算符(+和-)
- 乘法(*),除法(/)
- 加法(+),减法(-)
- 关系运算符(<、>等)
- 赋值运算符(=)
补充:圆括号的优先级是最高的,可以使用它来改变其他运算符的优先级。
C 运算符优先级
优先级 | 运算符 | 描述 | 结合性 |
---|---|---|---|
1 | ++ – () [] . -> (type){list} | 后缀/后置自增和自减 函数调用 数组下标 结构体和共用体成员访问 通过指针访问结构体和共用体成员 复合字面量[C99] | 从左到右 |
2 | ++ – + - ! ~ (type) * & sizeof _Alignof | 前缀自增和自减[注释1] 单目正负 逻辑非和按位取反 强制类型转换 间接访问(解引用) 取地址 求大小[注释2] 对齐要求[C11] | 从右到左 |
3 | * / % | 乘法、除法和取余 | 从左到右 |
4 | + - | 加法和减法 | 从左到右 |
5 | << >> | 按位左移和右移 | 从左到右 |
6 | < <= | 用于关系运算符 < 和 ≤ (分别对应) | - |
7 | > >= | 用于关系运算符 > 和 ≥ (分别对应) | - |
8 | == != | 用于关系运算符 ==(等于)和 !=(不等于) (分别对应) | - |
9 | & | 按位与 | - |
10 | ^ | 按位异或(独占或) | - |
11 | | | 按位或(包含或) | - |
12 | && | 逻辑与 | - |
13 | || | 逻辑或 | - |
14 | ?: | 条件运算符[注释3] | 从右到左 |
15[注释4] | = += -= *= /= %= <<= >>= &= ^= |= | 简单赋值 加法赋值、减法赋值 乘法赋值、除法赋值、取余赋值 按位左移赋值、按位右移赋值 按位与赋值、按位异或赋值、按位或赋值 | - |
16 | , | 逗号 | 从左到右 |
三、表达式求值
3.1 整型提升
C语言中整型算术运算总是至少以默认整型类型的精度来进行的。
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升
整型提升的意义:
表达式中的整型运算要在CPU的响应运算器件内执行,CPU整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。
因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。
通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然极其指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送入CPU去执行运算。
如何进行整型提升呢?
- 有符号整数提升是按照变量的数据类型的符号位来提升的
- 无符号整数提升,高位补0
多说无益,拿例子来看:
看到这串代码,你的第一反应c的值是多少呢?
会不会是140呢?
哈哈我既然拿出来了,它当然就没有那么简单了,打印出的值是-116
首先排除140,因为char类型的取值范围是:-128-127,他根本就存不下140,这里就要进行整型提升的操作:
首先这里20是int类型,4个字节要放到char里面去,char只有一个字节的空间,所以只能有8个比特位,这就需要截断一部分了,这里是截断高位,保留低位,这里a和b相加的时候要发生整型提升
在我们当前的msvc编译器下,char就是所谓的signed char,所以整型提升就按第一个走
过程展示
因为这里%d注释处的原因,加和起来的c也需要整型提升
3.2 算数转化
如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数转换为另一个操作数的类型,否则操作无法进行,下面的图中的层次体系就称为寻常算术转换。
编译器会根据内容自动转换数据类型,是由小的类型向大的类型转化,就如箭头所指方向
比如这里整型100+浮点型55.5,编译器就会自动将整型100转换为浮点型100,再进行相加和。
总结
虽然在这里已经有了操作符的优先级和结合性,但如果我们写代码的时候表达式过于复杂,计算结果的路径不唯一,一串代码同一函数多次调用,任有可能写出问题代码(因为调用的先后次序无从得知,不同编译器下的计算次序可能不同),所以在平时写代码的时候,表达式还是建议多次拆分,简单化更好。
这次决定把文章的重点内容设为标题看一看效果,因为前几篇文章流量不太好,有点小伤心,希望喜欢作者文章的靓仔靓女多多点赞支持一下,你们的支持就是我最大的动力!