结构成员访问操作符&操作符的优先级、结合性&表达式求值
1、结构体简单介绍
结构体是一种用来描述一个复杂对象的自定义数据类型。
比如我们要描述一个学生,而一个学生需要名字、年龄、学号、身高、体重等多种属性。对于这个问题,仅用C语言的内置类型(char、short、int、long、float、double等)是不够的。为了解决这一问题,C语言加入了结构体,让程序员可以自己创造合适的类型。
1.1、结构的声明
struct tag
{
member-list;
}variable-list;
其中,“ struct ”是结构体的 关键字 ;“ member-list ”是结构体的 成员列表 ,相当于结构体的属性;“ variable ”是结构体的 变量列表 ,被用于定义结构体变量。
比如,我们来描述一个学生。
struct Student
{
char name[20];//这是名字
int age;//这是年龄
char id[20];//这是学号
float height;//这是身高
int weight;//这是体重
};//分号加上
1.2、结构体变量的定义和初始化
1.2.1、变量的定义
我们还是以描述学生为例。
struct Student
{
char name[20];
int age;
char id[20];
float height;
int weight;
}p1, p2; //声明同时定义变量
struct Student p3; //定义变量
int main()
{
struct Student p4; //定义只能在main函数内使用的变量
return 0;
}
上述代码中,p1、p2是我们在声明类型的同时定义的变量, 用逗号隔开,为全局变量 ,可以在任何地方进行调用;p3是我们另外定义的结构体变量,也是 全局变量 ;而p4是在main函数内定义的,所以其只能在main函数内使用,以此类推,定义在其他函数或语句内的变量也是如此。
1.2.2、初始化
结构体变量的初始化有两种:一是 按顺序初始化 ,二是 指定顺序初始化 。
我们还是以描述学生为例。
struct Student
{
char name[20];
int age;
char id[20];
float height;
int weight;
}p1, p2;
struct Student p3;
int main()
{
struct Student p4;
struct Student p5 = {"张三", 24, "202508101724", 175.5f, 56};
struct Student p6 = {.age = 23, .name = "李四", .id = "20250810", .height = 174.3f, .weight = 55};
return 0;
}
首先,初始化值写在 大括号 内。若是按顺序初始化,只需 将初始化值按顺序与格式要求列出,用逗号隔开即可 ,如p5;
若是指定顺序初始化,则按照 .成员名 的格式写出,并在其后输入初始化值 。不同成员变量的初始化用逗号隔开,如p6。
1.2.3、结构体嵌套初始化
假设我们有多个学生,每一个学生都需要描述,而他们又有共同的属性,比如在读学校、入学年份。这时,我们可以使用嵌套初始化。为了简化说明,我们去掉几个属性。
struct School
{
char school_name[20];
int enter_year;
};
struct Student
{
struct School s;
char name[20];
int age;
char id[20];
}p1 = {{"阳光中学", 2025}, "王五", 16, "20251752"};
struct Student p2 = {{"阳光中学", 2024}, "赵六", 17, "20251754"};
如上面代码所示, 首先要在结构体成员变量中加入所需其他结构体变量 ;
然后, 加入的结构体变量初始化时另外加上一个大括号 。其他步骤同 1.2.2 的做法。
1.3、结构成员访问操作符
结构成员访问操作符有两种:点操作符( . ),和( -> )。分别用于结构体成员的直接访问,和结构体成员的直接访问。
1.3.1、结构体成员的直接访问
结构体成员的直接访问是通过点操作符访问的。点操作符接受两个操作数。点操作符的使用方式: 结构体变量.成员名 。继续以描述学生为例。
#include<stdio.h>
struct Student
{
char name[20];
int age;
char id[20];
float height;
int weight;
};
int main()
{
struct Student p5 = {"张三", 24, "202508101724", 175.5f, 56};
printf(" name:%s\n age:%d\n id:%s\n height:%f\n weight:%d\n", p5.name, p5.age, p5.id, p5.height, p5.weight);
return 0;
}
而对于嵌套过的结构体,我们则可以使用 较大范围结构体变量.较小范围结构体变量.成员名 这一方法。我们来举例。
#include<stdio.h>
struct School
{
char school_name[20];
int enter_year;
};
struct Student
{
struct School s;
char name[20];
int age;
char id[20];
}p1 = {{"阳光中学", 2025}, "王五", 16, "20251752"};
int main()
{
printf(" school_name:%s\n enter_year:%d\n", p1.s.school_name, p1.s.enter_year);
return 0;
}
1.3.2、结构体成员的间接访问
有时候我们得到的不是一个结构体变量,而是得到了一个指向结构体的指针。
由于我还未学习指针,暂时不做讲解。还请读者们谅解。
2、操作符的属性:优先级、结合性
这两个属性决定了表达式求值的计算顺序。
2.1、优先级
顾名思义,就是一个表达式中若包含多种操作符,优先级较高的操作符先执行。
例如
#include<stdio.h>
int main()
{
printf("%d\n", 4 + 5 * 6);
return 0;
}
上面例子中,4 + 5 * 6 表达式中包含加法运算符( + )和乘法运算符( * ),而 * 的优先级高于 + ,所以计算结果是34,而不是54。
下面是部分运算符的优先级顺序(从高到低)。再下面是参考表格。
• 圆括号( () ) //圆括号的优先级最高,可以修改其他操作符的优先级
• ⾃增运算符( ++ ),⾃减运算符( -- )
• 单⽬运算符( + 和 - )
• 乘法( * ),除法( / )
• 加法( + ),减法( - )
• 关系运算符( < 、 > 等)
• 赋值运算符( = )

表格参考至:https://zh.cppreference.com/w/c/language/operator_precedence
2.2、结合性
若两个运算符优先级相同,我们就看向结合性。至于表达式的执行顺序,取决于运算符是左结合,还是右结合。
3、表达式求值
表达式求值这一块,内容有两点:整型提升、算术转换。
3.1、整型提升
以 char 类型为例。char 类型占 1 字节,8 bit 。系统计算用的是补码。
#include<stdio.h>
int main()
{
char a = 3;
//2进制值:00000011
// char 是有符号类型
//所以整型提升:00000000000000000000000000000011
char b = 127;
//2进制值:01111111
//整型提升:00000000000000000000000001111111
char c = a + b;
//相加:00000000000000000000000010000010
// c 只能存 8 bit
//为:10000010
//补转原:11111110。为:-126
printf("%d\n", c);
return 0;
}
另:char c1 = -1;
c1 只有 8 bit
c1 为:11111111
整型提升:11111111111111111111111111111111
3.2、算术转换
4、问题表达式解析
4.1、表达式1
a*b + c*d + e*f
计算表达式1时,的确可以保证 * 比 + 先计算;但是,却不能保证某一个 * 比某一个 + 先计算。换句话说,计算机计算的方式可能就是随机的。
随着代码复杂性的增加,表达式1随机的计算方法会出现问题。
为了避免这些问题,我们可以采取以下两种方法:
1、 添加括号 。( ) 是最高级的操作符,可以帮助我们规定哪一个先算,哪一个后算。
2、 拆开来写 。计算机读取并执行代码,其顺序是从上到下的。
4.2、表达式2
c + --c
在这里,我们知道,自减操作肯定是比加操作先执行。而关键是加号左边的 c 。我们知道,对 c 进行自减操作后,得到的新值将存储在变量 c 中。那么,这个 c 到底是原来的 c ,还是自减操作以后的 c ?
为了避免不同的人对上面表达式产生不同理解这一现象的发生,也许我们++可以定义 不同的变量 ?
4.3、表达式3
#include<stdio.h>
int main()
{
int i = 0;
i = i-- - --i * (i = -3) * i++ + ++i;
printf("%d\n", i);
return 0;
}
也许,你的心里会想:这啥玩意儿?!
所以,还是那一句话:一行一行写,或者用不同的变量。
4.4、表达式4
#include<stdio.h>
int fun()
{
static int count = 1;
return ++count;
}
int main()
{
int answer;
answer = fun() - fun() * fun();
printf("%d\n", answer);//输出多少?
return 0;
}
上述代码中,我们只能知道:先算乘法,再算加法。
但是,函数的调用顺序,我们无法得知。而 static 的作用是 创建不销毁 ,也就是说,调用顺序的·不同,导致不同位置函数的返回值不同,进而导致输出结果不同。
所以,我们一定要写出一个能有唯一计算路径的代码。
4.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;
}
i 的返回值是毋庸置疑的,经过 3 次自增操作,为 4 。
而 ret 的返回值,就有说法了。
4.6、总结
更多推荐

所有评论(0)