结构体
声明结构体类型
格式:
struct <结构体名> {
<成员变量>;
};
声明结构体,计算机并没有分配内存,只是描述了一个结构体
例:
struct student{
char* name;
int age;
int ID;
int hight;
float weight;
//...
};
结构体成员
char* name,int age,int ID…这些便是结构体的成员。
空结构体
空结构体 也就是在声明结构体时没有设计成员变量
struct <结构体名> {
};
匿名结构体
匿名结构体 也就是结构体的声明中没有结构体名
struct{
<成员变量>;
};
这样就声明了一个匿名结构体。
因为没有结构体名,之后就没办法再使用该结构体。
所以,通常会在声明的时候,在大括号后直接定义变量名。
struct {
<结构体名>;
} <变量名1>, <变量名2>...;
像这样在声明的时候创建变量,而因为没有结构体名,所以匿名结构体只能使用定义变量一次。
结构体的嵌套定义
结构体也可以作为另一个结构体的成员变量
如:
struct birth {
int year;
int month;
int day;
};
struct student{
char* name;
int age;
int ID;
struct birth birthday;
};
如上,生日结构体成为了学生结构体的一个成员变量
结构体变量
声明结构体变量
声明了一个结构体变量,格式为
sturct <结构体名> <变量名>;
如:
struct student{
char* name;
int age;
int ID;
};
int main()
{
struct student stu1;
struct student stu2;
return 0;
}
stu1, stu2便是结构体变量。
但是这样的话,每次声明、传参等操作时都要写一个struct关键词,比较麻烦。
而typedef函数可以简便该情况。
使用typedef
typedef ;
type:原类型名称
name:新名称
typedef函数是专门给类型取名的
运用typedef函数,我们可以将上面代码改为
struct student{
char* name;
int age;
int ID;
};
typedef struct student student;
int main()
{
student stu1;
student stu2;
return 0;
}
这样就不用再写struct了。
更进一步,可以将结构体的声明与typedef结合起来,再结合上面所说的匿名结构体,因此可以将上面代码简化改为
typedef struct{
char* name;
int age;
int ID;
}student;
int main()
{
student stu1;
student stu2;
return 0;
}
但因为在声明时取了名字,因此与匿名结构体不同,他可以通过该名字来定义变量。
结构体变量的赋值
用花括号将成员变量数值括起来,在依次赋值。
例:
typedef struct{
int year;
int month;
int day;
}birth;
typedef struct {
char* name;
int age;
int ID;
birth birthday;
}student;
int main()
{
student stu1 = { "张三", 18, 1001, { 2005, 1, 1 } };
return 0;
}
访问成员结构体成员
- 访问成员变量使用 . 操作符
例:
#include <stdio.h>
typedef struct{
int year;
int month;
int day;
}birth;
typedef struct {
char* name;
int age;
int ID;
birth birthday;
}student;
int main()
{
student stu1 = { "张三", 18, 1001, { 2005, 1, 1 } };
printf("姓名:%s\t年龄:%d\t学号:%d\t出生日期:%d年%d月%d日",
stu1.name,stu1.age,stu1.ID,stu1.birthday.year,
stu1.birthday.month, stu1.birthday.day);
return 0;
}
嵌套结构体就需要逐层访问
结构体作为函数参数
结构体也可以作为参数传入函数中
例:
#include <stdio.h>
typedef struct{
int year;
int month;
int day;
}birth;
typedef struct {
char* name;
int age;
int ID;
birth birthday;
}student;
void pri ntstu(student stu) {
printf("姓名:%s\t年龄:%d\t学号:%d\t出生日期:%d年%d月%d日\n",
stu.name, stu.age, stu.ID,
stu.birthday.year, stu.birthday.month, stu.birthday.day);
}
int main()
{
student stu1 = { "张三", 18, 1001, { 2005, 1, 1 } };
student stu2 = { "李四", 18, 1002, { 2005, 2, 2 } };
printstu(stu1);
printstu(stu2);
return 0;
}
结构体指针
当结构体作为参数传入函数时,由于结构体内存较大,使得传入速度较慢,因此,常用结构体指针作为参数传入函数中
声明结构体指针
跟一般指针类似
例:
student *pstu = &stu1;
结构体指针访问结构体成员
- 通过结构体指针访问成员变量应该用 ->操作符
pstu1 ->name
结构体指针作为函数参数
将结构体指针作为参数传入函数,注意参数格式
void printstu(student *pstu);
因此,上面的代码可以改成
#include <stdio.h>
typedef struct{
int year;
int month;
int day;
}birth;
typedef struct {
char* name;
int age;
int ID;
birth birthday;
}student;
void printstu(student *pstu) {
printf("姓名:%s\t年龄:%d\t学号:%d\t出生日期:%d年%d月%d日\n",
pstu->name, pstu->age, pstu->ID,
pstu->birthday.year, pstu->birthday.month, pstu->birthday.day);
}
int main()
{
student stu1 = { "张三", 18, 1001, { 2005, 1, 1 } };
student stu2 = { "李四", 18, 1002, { 2005, 2, 2 } };
student* pstu1 = &stu1;
student* pstu2 = &stu2;
printstu(pstu1);
printstu(pstu2);
return 0;
}
注意:因为pstu是结构体指针,所以用->;而birthday只是结构体,因此还是用 . 。
结构体数组
声明结构体数组
与普通数组声明相似
int a[10]
int:类型
a:数组名
10:申请了10个int单元的内存
因此,结构体数组声明
struct <结构体名> <数组名> [<数量>];
例:
struct book b[10];
初始化结构体数组
与普通数组一样,例子:
typedef struct {
char* name;
int age;
int ID;
}student;
int main()
{
student stu[] = { {"张三", 18, 1001},
{ "李四", 18, 1002 },
{"赵五", 19, 1003}};//初始化了三个学生信息
return 0;
}
访问结构体数组的成员
在结构体名后加 . 运算符,然后再是成员名;
例子:
stu[2].name//第三个学生的姓名
结构体的内存计算
计算方式
利用sizeof可以计算出内存
例:
#include <stdio.h>
typedef struct{
int year;
int month;
int day;
}birth;
typedef struct {
char* name;
int age;
int ID;
birth birthday;
}student;
int main()
{
student stu1 = { "张三", 18, 1001, { 2005, 1, 1 } };
student stu2 = { "李四", 18, 1002, { 2005, 2, 2 } };
printf("生日结构体所占字节数:%d\n", sizeof(birth));
printf("学生结构体所占字节数:%d\n", sizeof(student));
return 0;
}
因为birth里有三个int,一个int占用4个字节,因此birth占用12个字节;
按理来说,如果char*占4个字节的话,学生结构体应该占用24个字节,那为什么实际上是32?
内存对齐
对齐规则
- 数据成员对齐规则:结构(struct或联合union)的数据成员,第一个成员在与结构体变量偏移量为0的地址处,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。
- 结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。
- 结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储。)
来看一个例子:
#include <stdio.h>
typedef struct {
char c;
int i;
}A;
int main()
{
printf("A所占字节数:%d", sizeof(A));
return 0;
}
分析:
- 第一个是char类型,后面的每个数据成员存储的起始位置要从该成员大小的整数倍开始算(也就是1的倍数,故int i 的起始位置是1)。
- 判断当前总内存(5)是否为最大成员内存(4)的整数倍,如果不是需要补齐到满足最大成员内存的最小整数倍(2倍,即8)。
- 故int i 的起始位置修改为4,因此结构体内存为8.