12_结构体
结构体
结构体: 将多种数据结构封装在一起 形成新的结构
每种数据结构都有自己独立的空间
结构体关键字: struct
结构体类型定义(声明)
#include<stdio.h>
struct stu //定义结构体类型,系统不分配空间
{
int num; //成员,不支持赋值
char name[32];
};
int main(int argc, char const *argv[])
{
struct stu lucy;
struct stu alix;
lucy.num = 10; //赋值
alix.num = 100;
printf("%d\n", lucy.num);
printf("%d\n", alix.num);
return 0;
}
结构体变量初始化
struct stu hq = {"黄齐", 23}; //结构体变量初始化
printf("%s, %d\n", hq.name, hq.age);
使用memset使结构体变量清空
struct stu mark;
memset(&mark, 0, sizeof(mark)); //memset使结构体变量清空
键盘给结构体变量赋值
struct stu ks;
scanf("%d%s", &ks.age, &ks.name);
结构体变量修改值
struct stu kj= {18, "王莹"}; //初始化
kj.age = 18;
//字符串数组 必须使用字符串操作函数进行操作
// kj.name = "王莹"; //error
strcpy(kj.name, "刘渊");
printf("%s, %d\n", kj.name, kj.age);
结构体直接赋值
struct stu xiaoming = {23, "小明"};
struct stu xiaoli = xiaoming;
内存赋值
struct stu xiaoming = {23, "小明"};
struct stu xiaoli;
memcpy(&xiaoli, &xiaoming, sizeof(xiaoming)); //string.h
结构体嵌套
struct Date
{
int year;
int month;
int day;
};
struct student
{
int num;
char name[32];
struct Date date;
};
int main(int argc, char const *argv[])
{
struct student lucy = {100, "lucy", {2019, 10, 21}};
printf("%d\n", lucy.date.year);
return 0;
}
结构体数组
struct stu edu[5] = {{"小明", 20}, {"小丽", 23}, {"小华", 56}, {"小齐", 20}, {"小史", 34}};
// memset(edu, 0, sizeof(edu)); //清0
int n = sizeof(edu)/sizeof(edu[0]);
for (int i = 0; i < n; i++)
{
printf("%s, %d\n", edu[i].name, edu[i].age);
}
结构体指针变量
struct stu lucy = {"lucy", 23};
struct stu *p = &lucy; //指针变量
printf("%s, %d\n", p->name, p->age);
printf("%s, %d\n", (*p).name, (*p).age);
printf("%s, %d\n", (&lucy)->name, (&lucy)->age);
结构体的指针成员
指针成员: 指针变量作为结构体的成员
指针成员指向文字常量区
struct stu
{
int age;
char *name; //指针成员
};
int main(int argc, char const *argv[])
{
struct stu lucy = {18, "lucy"}; //"lucy" ===> 文字常量区
printf("%c\n", lucy.name[0]); //'l'
lucy.name[0] = 'U'; //段错误,文字常量区只读
return 0;
}
指针成员指向堆区
struct stu lucy;
lucy.age = 18;
//让name指向堆区
lucy.name = (char *) calloc(1, 128);
// lucy.name = "lucy"; //error 内存泄漏 name指向了文字常量区丢失了 堆区空间
strcpy(lucy.name, "lucy");
printf("%s, %d\n", lucy.name, lucy.age); //lucy, 18
lucy.name[0] = 'U';
printf("%s, %d\n", lucy.name, lucy.age); //Uucy, 18
//释放堆区空间
if (lucy.name != NULL)
{
free(lucy.name);
lucy.name = NULL;
}
浅拷贝
深拷贝
如果结构体中有指针成员, 尽量使用深拷贝
struct stu bob;
bob.name = (char *) calloc(1, 128);
bob.age = lucy.age;
strcpy(bob.name, lucy.name);
这样就不会出现问题
结构体在堆区 结构体的指针成员指向堆区
struct stu
{
char *name;
int age;
};
int main(int argc, char const *argv[])
{
struct stu *p = NULL;
p = (struct stu *)calloc(1, sizeof(struct stu));
p->name = (char *)calloc(1, 128);
p->age = 32;
strcpy(p->name, "lucy");
printf("%s, %d\n", p->name, p->age);
if (p->name != NULL) //先释放成员变量
{
free(p->name);
p->name = NULL;
}
if (p != NULL) //后释放结构体指针
{
free(p);
p = NULL;
}
return 0;
}
结构指针数组
struct stu
{
char *name;
int age;
};
int main(int argc, char const *argv[])
{
struct stu **arr = NULL;
int n = 5; // 结构数组元素个数
arr = (struct stu **)calloc(n, sizeof(struct stu *));
for (int i = 0; i < n; i++)
{
arr[i] = (struct stu *)calloc(1, sizeof(struct stu));
arr[i]->age = 10 + i;
arr[i]->name = (char *)calloc(1, 128);
sprintf(arr[i]->name, "lucy%d", i);
}
for (int i = 0; i < n; i++)
{
printf("%s, %d\n", arr[i]->name, arr[i]->age);
}
for (int i = 0; i < n; i++)
{
if (arr[i]->name != NULL)
{
free(arr[i]->name);
arr[i]->name = NULL;
}
if (arr[i] != NULL)
{
free(arr[i]);
arr[i] = NULL;
}
}
if (arr != NULL)
{
free(arr);
arr = NULL;
}
return 0;
}
结构体自动类型对齐
对齐规则
确定分配单位(一行分配多少字节)
结构体中最大的基本类型长度决定(char 1 int 4 short 2 long 8)
确定成员的偏移量
成员偏移量 = 成员自身类型的整数倍
struct Data
{
char a; //1字节
int b; //4字节
short c; //2字节
}
收尾工作
结构体的总大小 = 分配单位的整数倍
结构体嵌套结构体 自动对齐规则
确定分配单位(一行分配多少字节)
所有结构体中最大的基本类型长度决定(char 1 int 4 short 2 long 8)
确定成员的偏移量
普通成员偏移量 = 成员自身类型的整数倍
结构体成员的偏移量 = 该结构体中最大的基本类型的 整数倍
收尾工作
结构体的总大小 = 分配单位的整数倍
例子:
强制对齐
#pragma pack(value)
结构体的位域
位域(位段): 结构体中以位为单位的成员
struct Data
{
unsigned int a: 2; //a类型是unsigned int 大小只占2位二进制位
}
不要对位域取地址:
struct A Adata;
&Adata.a; //错误, 系统以字节为单位分配地址空间, 这里是位
对位域赋值 不用超过 位域本身位的宽度
data.a = 5; //溢出
另起一个存储单元
unsigned char : 0; //另起一个存储单元
无意义位段
struct Data
{
unsigned char a: 4;
unsigned char : 2; //两位无意义位段
unsigned char b: 2;
}data;
printf("%lu\n", sizeof(data)); //1字节
共用体 union
结构体: 所有成员拥有独立空间
共用体: 所有成员共享一块空间
union Data
{
char a;
short b;
int c;
};
成员abc共享同一块空间(最大成员空间决定)
union Data data;
data.a = 10;
data.b = 20;
data.c = 30;
printf("%d\n", data.a + data.b + data.c); //90(30+30+30)
成员共享同一块空间,但每个成员能操作的空间由成员自身类型长度决定
共用体将众多结构体统一, 但每个共用体空间只能存放一个数据,否则会覆盖
枚举 enum
枚举: 将枚举变量要赋的值一一列举出来
int poker_color;
enum POKER_COLOR{HONGTAO, MEIHUA, FANGKUAI, HEITAO}; //枚举列表的值默认从0开始递增