我们都知道C语言中变量的类型决定了变量存储占用的空间。当我们要使用一个变量保存年龄时可以将其声明为int类型,当我们要使用一个变量保存某一科目的考试成绩时可以将其声明为float。
那么,当我们要做一个学生信息管理系统时,需要保存学生的姓名、学号、年龄等信息,该怎么做呢?
是不是这样定义
zhangsan.name=Liuyao
zhangsan.nam=520
zhangsan.age=24
但是zhangsan.name
这个东西我们可是没有学过啊!
所以就出现了结构体
什么是结构体?
结构体(struct)是由一系列具有相同类型或不同类型的数据项构成的数据集合,这些数据项称为结构体的成员。如上面的student结构体包含三个成员,分别是name、num、age。结构体是C语言中的一种构造类型。C语言的数据类型如下图:
一、定义结构体的一般形式
struct 结构名
{
成员列表;
};
成员列表的形式为 类型说明符 成员名;
举个例子1
struct student
{
char *name; // 学生名字
int num; // 学生学号
int age; // 学生年龄
};
1.此处,student是结构体名,该名字是由任意定义的,但是尽量起个有意义的名称。其相当于一个模板,可以使用这个模板去定义变量student1, student2, student3。定义的时候不要忘了struct。
2.使用该结构体创建三个变量student1, student2, student3
struct student student1, student2, student3;
定义了student1、student2、student3为struct student 类型的变量,即它们具有了struct student类型的结构。显然,此方法更清晰,因为它把name、num、age都集成在一个模板,要用的时候只要套用模板进行创建即可。这就是结构体。
初始化结构体
现在我们把student1信息表示出来
struct student student1 { "20161111","man",18,"zhangsan",90};
类型名 结构体名 变量名
-
简而言之,我们使用在一对花括号中括起来的初始化列表进行初始化,各初始化项用逗号分隔。
-
为了让初始化项与结构中各成员的关联更加明显,我们让每个成员的初始化项独占一行。这样做只是为了提高代码的可读性,对编译器而言,只需要用逗号分隔各成员的初始化项即可。
C99和C11为结构提供了指定初始化器(designated initializer)。其初始化器使用点运算符和成员名。例如,只初始化student结构中的name成员,可以这样做:
struct student student1 =
{
.name = "Liuyao"
};
也可以按照任意顺序使用指定初始化器:
struct student student1 =
{
.age = 24,
.num = 520,
.name = "Liuyao"
};
另外,对特定成员的最后一次赋值才是它实际获得的值。例如,考虑如下代码:
struct student student1 =
{
.num = 520,
.name = "Liuyao",
1314
};
此时,赋给num的值应该是1314,因为它在结构声明中紧跟在name成员之后。新值1314取代了之前的520。
- 在定义了结构体变量后,系统会为之分配内存单元。
- student1 在内存中所占的的字节为 4+1+4+1+4=14.
二、在声明类型的同事定义变量
struct 结构体名
{
成员列表;
}变量名列表;
成员列表的形式为 类型说明符 成员名;
举个例子2
struct student
{
char *name; // 学生名字
int num; // 学生学号
int age; // 学生年龄
}student1,student2;
此处省略了结构体名。虽然更简洁了,但是因为没有了名字,后面就不能用该结构定义新的变量。这个结构体的变量只能是student1和student2
三、typedef给结构体起别名
这种方法在实际操作中用的非常多,在嵌入式开发中几乎全都是用typedef给结构体起别名方法。比如你常见的STM32单片机中的程序,就是这样写的。
typedef struct 结构体名
{
成员列表;
}变量名列表;
举个例子3
// 给结构体模板struct student重新命名为student
typedef struct student
{
char *name; // 学生名字
int num; // 学生学号
int age; // 学生年龄
}student;
使用student创建三个结构体变量student1,student2, student3
student student1 , student2 , student3
此处使用typedef为结构体模板struct student定义一个别名student,关于typedef的介绍移步至。进行查看。使用typedef给结构体创建一个别名,这在实际编程用使用非常广泛,如STM32单片机固件库中,使用得很多。
访问结构体成员
结构体成员的访问需要借助结构体成员运算符——点(.)。如:
student student1; // 定义一个结构体变量student1
student1.name = "Liuyao"; // 给student1的成员name赋值
student1.num = 520; // 给student1的成员num赋值
student1.age = 23; // 给student1的成员age赋值
实例1
#include <stdio.h>
typedef struct student
{
char *name; // 学生名字
int num; // 学生学号
int age; // 学生年龄
}student;
int main(void)
{
student student1; // 定义一个结构体变量student1
/* 给结构体变量stu1的成员进行赋值 */
student1.name = "Liuyao";
student1.num = 520;
student1.age = 23;
printf("\n============================================\n");
printf("My name is %s\n", student1.name);
printf("My num is %d\n", student1.num);
printf("My age is %d\n", student1.age);
printf("欢迎关注果果小师弟学习笔记!\n");
printf("============================================\n");
return 0;
}
实例2
#define KEY_FIFO_SIZE 10
typedef struct
{
uint8_t Buf[KEY_FIFO_SIZE]; /* 键值缓冲区 */
uint8_t Read; /* 缓冲区读指针1 */
uint8_t Write; /* 缓冲区写指针 */
uint8_t Read2; /* 缓冲区读指针2 */
}KEY_FIFO_T;
//此处使用typedef为结构体模板struct 定义一个别名KEY_FIFO_T
static KEY_FIFO_T s_tKey; //定义一个结构体变量 s_tKey
void bsp_PutKey(uint8_t _KeyCode)
{
s_tKey.Buf[s_tKey.Write] = _KeyCode;
if (++s_tKey.Write >= KEY_FIFO_SIZE)
{
s_tKey.Write = 0;
}
}
static void bsp_InitKeyVar(void)
{
uint8_t i;
s_tKey.Read = 0;
s_tKey.Write = 0;
s_tKey.Read2 = 0;
}
例子三
#define GPIO_PORT_K1 GPIOI //K1 键
#define GPIO_PIN_K1 GPIO_Pin_8
#define GPIO_PORT_K2 GPIOC //K2 键
#define GPIO_PIN_K2 GPIO_Pin_13
#define GPIO_PORT_K3 GPIOH //K2 键
#define GPIO_PIN_K3 GPIO_Pin_4
#define GPIO_PORT_K4 GPIOG //摇杆UP键
#define GPIO_PIN_K4 GPIO_Pin_2
#define GPIO_PORT_K5 GPIOF //摇杆DOWN键
#define GPIO_PIN_K5 GPIO_Pin_10
#define GPIO_PORT_K6 GPIOG //摇杆LEFT键
#define GPIO_PIN_K6 GPIO_Pin_3
#define GPIO_PORT_K7 GPIOG //摇杆RIGHT键
#define GPIO_PIN_K7 GPIO_Pin_7
#define GPIO_PORT_K8 GPIOI //摇杆OK键
#define GPIO_PIN_K8 GPIO_Pin_11
typedef struct
{
uint32_t GPIO_Pin;
GPIOMode_TypeDef GPIO_Mode;
GPIOSpeed_TypeDef GPIO_Speed;
GPIOOType_TypeDef GPIO_OType;
GPIOPuPd_TypeDef GPIO_PuPd;
}GPIO_InitTypeDef;
static void bsp_InitKeyHard(void)//配置8个按键的GPIO口,为推挽模式
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_ALL_KEY, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_PIN_K1;
GPIO_Init(GPIO_PORT_K1, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_PIN_K2;
GPIO_Init(GPIO_PORT_K2, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_PIN_K3;
GPIO_Init(GPIO_PORT_K3, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_PIN_K4;
GPIO_Init(GPIO_PORT_K4, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_PIN_K5;
GPIO_Init(GPIO_PORT_K5, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_PIN_K6;
GPIO_Init(GPIO_PORT_K6, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_PIN_K7;
GPIO_Init(GPIO_PORT_K7, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_PIN_K8;
GPIO_Init(GPIO_PORT_K8, &GPIO_InitStructure);
}
这个例子是很清楚 。