结构体知识点
问题引入:管理学生信息(学生信息管理系统)。假设每个学生,学校关心如下内容: 学号 char StuId[9]; // 西邮学号8位,再加一个0结束标志,故而设定为9位 姓名 char StuName[21]; // 一个汉字两个字节,此处假设最多十个汉字,再加一个0结束标志,故而设定为21位 性别 int StuSex; 年龄 int StuAge; // 一般设定为出生年月日,这样不用每年进行管理 成绩 int Score; // 成绩本可能出现小数,但是此处为了练习用整型表示,不到万不得已不用float和double,运算太慢了
假设有50名学生的相关信息需要管理…… char StuId[50][9]; char StuName[50][21]; int StuSex[50]; int StuAge[50]; int Score[50];
若使用上述多个数组形式处理相关任务,则,应保证同一名学生5个方面的信息数据,保存在下标相同的数组元素中,以保证逻辑上的一致性。但是,在“排序”等事务要求下,需要用多次交换来保证逻辑一致性,这使得上述方法太可怕了! 另外:上述方法并没有在逻辑层面保持与人文信息的一致性。 为了简化上述问题的解决,也为了能使C语言更符合人类思维习惯,提出结构体的概念。
|
补充:二维数组 int a[3][4] <=> int [4] a[3] 理解为:a数组有3个元素,每个元素是一维数组,即 int [4] 类型 上述语句定义了一个3行4列的二维数组。 二维数组m共有12个元素,每个元素都是int类型的。 这12个元素的下标分别是: a[0][0]、a[0][1]、a[0][2]、a[0][3]、 a[1][0]、a[1][1]、a[1][2]、a[1][3]、 a[2][0]、a[2][1]、a[2][2]、a[2][3]、
char StuName[50][21] => char[21] StuName[50] |
一、结构体的基本操作1.结构体的声明struct STUDENT_INFORMATION { char StuId[9]; char StuName[21]; int StuSex; int StuAge; int Score; };
结构体的声明,实质上是定义了一个新的数据类型 |
2.结构体的使用方法(1) struct STUDENT_INFORMATION a; int b; 上述两条语句都是变量定义语句; 其中,a和b地位相同,都是变量名称; int和struct STUDENT_INFORMATION地位相同,都是数据类型!
(2) strcut STUDENT_INFORMATION a, b[50], *p; 通常将结构体类型的变量称为:(结构体)实例; b是一个拥有50个struct STUDENT_INFORMATION实例元素的数组,b[0]到b[49]就是独立的50个类似a的结构体实例; p是一个能够指向struct STUDENT_INFORMATION实例的指针变量,其指类就是struct STUDENT_INFORMATION。
(3) 结构体的使用有2个基本步骤: 1)声明结构体数据类型; 2)用结构体类型作为数据类型,定义新的实例(数组、指针)。 在上述两步之后,通过对实例的成员的引用,实现对实例的引用。 |
3.访问结构体的成员struct STUDENT_INFORMATION { char StuId[9]; char StuName[21]; int StuSex; int StuAge; int Score; };
(1) strcut STUDENT_INFORMATION a, b[50], *p; //要求:对a实例进行赋值 张三,男,19岁,03151038,61 注:男为1,女为0 正确写法: strcpy(a.StuName, “张三”); strcpy("a.StuName", “张三”); (错误) strcpy(a.StuId, “03151038”); a.StuSex = 1; a.StuAge = 19; a.Score = 61;
注意: a.StuName = 张三; a.StuName = “张三”; a.StuName[21] = “张三”; a.StuName[0] = “张三”; a.StuName = {“张三”}; 上述5种写法都是错误的!原因:StuName是常量!数组名称的本质是首地址常量,常量不能被赋值!
(2) strcut STUDENT_INFORMATION a, b[50], *p; //要求:对b数组的第一个元素所代表的学生赋值如下信息: 04151038 李四 女 18 99 strcpy(b[0].StuName, “李四”); strcpy(b[0].StuId, “04151038”); b[0].StuSex = 0; b[0].StuAge = 18; b[0].Score = 99;
(3) strcut STUDENT_INFORMATION a, b[50], *p; p = &a; (p指向a) *p = a; (*在等号左边应念作:p所指向的空间,即实例a) 如何通过指针引用其所指向的实例的诸成员? 一般思路: *p.Age 错误 上述的”.”是一个运算符:取结构体实例成员运算符;其优先级高于!(逻辑非)运算符! 在C语言的所有运算符中,优先级最高的前三个运算符是: () -> [] -> . (注意此处的点) *p.Age <-> *(p.Age) 这应该称为:“p实例的Age”所指向的空间,这与原本要表达的意义完全无关! 正确表达: (*p).Age 繁琐 通过(),提高*的运算顺序: p->Age 简单 正确念法是:p所指向的实例的Age成员
.运算符与->运算符优先级相同! 两个运算符的差别: 实例.成员 指针->成员
综上:访问结构体的成员规则如下(以StuSex为例) strcut STUDENT_INFORMATION a, b[50], *p; a.StuSex b[0].StuSex p->StuAge
|
二、结构体内存映像图struct ABC { int a; char b; double c; };
从上述结构体的内存映像图可知,一个结构体的长度,原则上是各成员长度之和。因此,sizeof(struct ABC) => 13B 注意:上机实验时,结果并非13B,而是比13B大,个中原因与一个较难、较深的概念有关:内存对其模式,该问题只能在进一步的课程《数据结构与算法》中深入探讨。 |
三、结构体实例赋初值struct STI { char Id[9]; char Name[21]; char Sex; //也可以用int Sex; int Age; int Score; };
struct STI a = {"03142069", "安宁", 'f', 23, 100}; struct STI b[3] = { {“02150103”, “张三疯子”, ‘m’, 19, 100}, {“02150101”, “仇娇雅子”, ‘f’, 22, 30}, {“02150013”, “啤酒瓶子”, ‘m’, 20, 0}, }; struct STI b[3] = {“02150103”, “张三疯子”, ‘m’, 19, 100, “02150101”, “仇娇雅子”, ‘f’, 22, 30, }; //不完全赋初值
问题:表示性别时为何用单引号,忘记了,下去去查,进行理解。
注意”:C语言允许在结构体实例之间直接赋值! struct STI a = {…}; struct STI b; b = a; // 这就可以完成将a的所有成员数据,赋值给b的所有成员(不要再进行成员内部一个一个的赋值) |
四、复合结构体稍微专业一些的学生信息,示例如下: struct DATE //先定义出生日期 { short year; char month; char day; }; // 经过上述语句,定义了一个struct DATE的数据类型,以后,就可以使用这个数据类型了!
struct STU_INF { char stuId[9]; char stuName[21]; char stuSex; struct DATE stuBirth; // 这个成员本身是另一个结构体的实例!它有着自己的三个成员:year,month和day成员! };
对struct STU_INF的使用: struct STU_INF a; a有四个成员:a.stuId、a.stuName、a.stuSex和a.stuBirth; 其中的a.stuBirth自己存在3个成员:a.stuBirth.year 、a.stuBirth.month 、a.stuBirth.day 。 |