[ 基本难度系数 ]:★★☆☆☆
一、结构体基本概念和定义
C语言提供了众多的基本类型,但现实生活中的对象一般都不是单纯的整型、浮点型或字符串,而是这些基本类型的综合体。比如一个学生,典型地应该拥有学号(整型)、姓名(字符串)、分数(浮点型)、性别(枚举)等不同侧面的属性,这些所有的属性都不应该被拆分开来,而是应该组成一个整体,代表一个完整的学生。
在C语言中,可以使用结构体来将多种不同的数据类型组装起来,形成某种现实意义的自定义的变量类型。结构体本质上是一种自定义类型。
- 结构体的语法:
结构体标签: 用来区分各个不同的结构体。
成员: 是包含在结构体内部的数据,可以是任意的数据类型。
- 结构体的定义:
struct 结构体标签
{
成员1;
成员2;
...
}; // 此处有;号
- 示例代码:
#include <stdio.h>
// 自定义结构体类型
// (1)、普通的结构体定义
// 1、学生结构体
struct student // 结构体标签:用来区分各个不同的结构体的
{
char name[128]; // 成员:包含在结构体内存的数据,可以是任意的类型(包括结构体本身、函数指针等)
char phone[12];
int id;
int gender;
int class;
int score;
int height;
int weight;
int age;
}; // 此处一定要加;号
// 2、数据节点结构体
struct data_node // 结构体标签:用来区分各个不同的结构体的
{
int a;
int b;
double c;
};
// (2)、使用typedef给结构体取别名
typedef struct
{
char name[128];
char phone[12];
int id;
int gender;
int class;
int score;
int height;
int weight;
int age;
}stu, *stu_p; // 你可以在此处写这个结构体的别名,没有结构体标签,意味着你只能够使用此处提供的结构体变量
// (3)、二合一法
typedef struct data_node2
{
char name[128];
char phone[12];
int id;
int gender;
int class;
int score;
int height;
int weight;
int age;
}node, *node_p;
// 主函数
int main(int argc, char const *argv[])
{
// (1)、普通结构体的定义
struct student fzetc; // 1、信息不够集中(类似于这种写法:int num) 2、数据类型名字固定死了,含义有局限
struct data_node data;
// (2)、使用typedef给结构体取别名
stu qingge; // 普通变量?√ 指针变量?
stu_p fanggong_p; // 普通变量? 指针变量?√
// (3)、二合一法
struct data_node2 n1; // 普通变量,普通结构体定义
node n2; // 普通变量,typedef表达
node_p n3_p; // 指针变量,typedef表达
return 0;
}
二、结构体初始化和应用
(1)、初始化
结构体跟普通变量一样,涉及定义、初始化、赋值、取址、传值等等操作,这些操作绝大部分都跟普通变量别无二致,只有少数操作有些特殊性。这其实也是结构体这种组合类型的设计初衷,就是让开发者用起来比较顺手,不跟普通变量产生太多差异。
- 结构体的定义和初始化。
- 由于结构体内部拥有多个不同类型的成员,因此初始化采用与数组类似的列表方式。
- 结构体的初始化有两种方式:①普通初始化;②指定成员初始化。
- 为了能适应结构体类型的升级迭代,一般建议采用指定成员初始化。
- 指定成员初始化的好处:
-
- 成员初始化的次序可以改变。
- 可以初始化一部分成员。
- 结构体新增了成员之后初始化语句仍然可用。
- 示例代码:
#include <stdio.h>
// 学生结构体
typedef struct student
{
// int class; // 加了一个成员变量
char name[128];
char phone[12];
int id;
int gender;
int score;
int height;
int weight;
int age;
}stu, *stu_p;
// 主函数
int main(int argc, char const *argv[])
{
// (1)、结构体的初始化
// 1、普通初始化
struct student stu1 = {"qingge", "13430343954", 9127, 1, 99, 185, 160, 18};
/*
局限性:增加一个新成员时,有可能会报以下错误
warning: initialization makes integer from pointer without a cast [-Wint-conversion]
warning: overflow in implicit constant conversion [-Woverflow]
不能够指定成员初始化,只能够顺序初始化,初始化的值一定要和类型匹配,所以后续增加结构体成员的
时候,会出现错误,不方便升级 cf手游(屎山代码,不敢动) ==》 前面的开发,对后面的开发造成影响和损坏
*/
// 2、指定成员初始化
struct student stu2 =
{
.id = 9527, // 初始化次序可以改变
.gender = 1, // 小圆点 ==> "当前结构体里面的" 什么什么成员
.score = 100,
.name = "fanggong",
.phone = "13430343958",
.height = 186,
.weight = 161,
// .age = 19 // 可以只初始化一部分成员
};
return 0;
}
(2)、引用
结构体相当于一个集合,内部包含了众多成员,每个成员实际上都是独立的变量,都可以被独立地引用。引用结构体成员非常简单,只需要使用一个成员引用符即可:
- 语法:
结构体.成员
- 示例代码:
#include <stdio.h>
// 学生结构体
typedef struct student
{
// int class; // 加了一个成员变量
char name[128];
char phone[12];
int id;
int gender;
int score;
int height;
int weight;
int age;
}stu, *stu_p;
// 主函数
int main(int argc, char const *argv[])
{
// (2)、结构体的引用
struct student stu1 = {0};
// 非字符串赋值
stu1.age = 20;
stu1.score = 101;
// 字符串赋值
// stu1.phone = "13430343959"; // 错误,这句话的意思是将地址赋值给结构体的phone数组成员,而数组名相当于一个常量指针,不能更改其地址
strcpy(stu1.phone, "13430343959"); // 正确,通过strcpy这个字符串函数,将字符串的内容赋值给结构体里面的phone数组
return 0;
}
三、结构体指针与数组
跟普通变量别无二致,可以定义指向结构体的指针,也可以定义结构体数组。
- 结构体数组(示例代码):
// 自定义结构体类型
typedef struct student
{
char name[128];
char phone[12];
int id;
int gender;
int class;
int score;
int height;
int weight;
int age;
}stu, *stu_p;
// 主函数
int main(int argc, char const *argv[])
{
struct student qingge[5] = {0}; // 因为是结构体数组,数量成员太多,建议初始化为0
// 为结构体数组第一个成员初始化
qingge[0].age = 18;
qingge[0].class = 306;
strcpy(qingge[0].name, "xiaoqing");
return 0;
}
- 结构体指针(示例代码):
// 自定义结构体类型
typedef struct student
{
char name[128];
char phone[12];
int id;
int gender;
int class;
int score;
int height;
int weight;
int age;
}stu, *stu_p;
// 初始化结构体函数(申请一个结构体的堆空间内存)
// 方法1:struct student* STU_Init(void)
// 方法2:stu* STU_Init(void)
stu_p STU_Init(void) // 返回值为stu_p类型(是一个结构体指针类型)
{
// 1、给结构体申请堆空间
stu_p p = malloc(sizeof(struct student ));
if (p != NULL) // 申请堆空间成功
{
strcpy(p->name, "qingge");
strcpy(p->phone, "13430343954");
p->id = 9127;
p->gender = 1;
p->age = 18;
// .....不写了
}
else
{
free(p); // 申请堆空间失败,释放内存(释放资源)
return NULL;
}
// 2、成功,返回指向这个结构体堆空间的内存的地址
return p;
}
// 主函数
int main(int argc, char const *argv[])
{
// 1、这个指针指向的内存在栈空间
stu fanggong = {0};
stu* fangong_p = &fanggong;
// 2、这个指针指向的内存在堆空间
stu_p p = STU_Init();
p->age = 18;
strcpy(p->name, "xiaofang");
return 0;
}
- 解析
[ 课堂课后实验 ]:
【1】、实验1:(结构体基本概念)
思考以下问题:
- 结构体初始化的基本语法是怎样的?
- 什么叫做指定成员初始化?这样做有什么好处?
【2】、实验2:(结构体基本语法)
指出以下代码片段的不妥之处。
structure node
(
char itable,
int num[5],
char * togs
)
【3】、实验3:(结构体基本语法)
定义一个结构体来存储日期(含年、月、日)。并设计一个函数,计算传入的结构体存储的日期是一年中的第几天。(不考虑大小月、闰年)