结构体指针
定义:
结构体类型的指针变量指向结构体变量或者数组的起始地址。
语法:
struct 结构体名*指针变量列表;
举例:
struct Dog
{
char name[20];
int age;
};
struct Dog dog;
struct Dog *p = &dog;
结构体成员访问
格式:
结构体数组名->成员名;
结构体成员访问符
. :左侧是结构体变量(结构体对象/实例),也可以叫做结构体对象访问成员符;右侧是结构体成员。
->:左侧是一个指针,可也以叫结构体指针访问成员符;右侧是结构体成员。
访问结构体成员有两种类型,三种方式:
类型1:通过结构体对象访问成员
struct Stu
{
int id;
char name[20];
} stu;
//访问成员
stu.name;
类型2:通过结构体指针访问成员
1.指针引用访问成员
struct Stu
{int id;
char name[20];
}stu;struct Stu *p = &stu;
p -> name;
2.指针解引用间接访问成员
struct Stu
{int id;
char name[20];
}stu;
struct Stu *p = &stu;//指针解引用间接访问成员
(*p)-> name;
结构体数组中元素的访问
struct Stu
{
int id;
char name[20];
float scores[3];
} stus[3]={
{1,"张三",{86,88,56}},
{2,"李四",{75,66,78}},
{3,"王五",{70,99,90}}
};
//取数据 下标法
printf("%s,%2f\n",stus[1].name,stus[1].scores[2]);//李四,78
//结构体成员引用符号:->指针法
printf("%s,%2f\n",stus->name,stus ->scores[2]);// 张三,56printf("%s,%2f\n",(stus +1)->name,(stus +1)->scores[2]);// 李四,78
printf("%s,%2f\n",(*(stus + 1))->name,(*(stus + 1))-> scores[2]);// 李四,78
小贴士:
1)结构体是自定义数据类型,它是数据类型,用法类似于基本类型的int;
2)结构体数组它是存放结构体对象的数组,类似于int数组存放int数据;
3)基本类型数组怎么用,结构体数组就怎么用--->可以遍历,可以作为形式参数,也可以作为指针等。
eg:
#include <stdio.h>
// 定义结构体
struct Cat
{
char *name;// 姓名
int age;// 年龄
char color[20];// 颜色
}
// 1.结构体类型作为形式参数
void test1(struct Cat c);
// 2.结构体类型作为形式参数,结构体类型作为返回值类型
struct Cat test2(struct Cat c);
// 3.结构体数组作为形式参数
void test3(struct Cat cats[],int len);
// 4.结构体数组作为形式参数,结构体指针作为返回值数据类型21struct Cat *test4(struct Cat cats[],int len);
测试:
int main()
{
// 定义结构体对象
struct Cat cat = {"小黑",8,"baise"};
// 结构体对象作为实际参数
test1(cat);
// 定义结构体类型对象
struct Cat cat1 = {"小白",8,"heise"};
// 调用函数并接收返回值
struct Cat c1 = test2(cat1);
// 通过返回值访问结构体对象的成员
printf("%s==%d==%s\n",c1.name,c1.age,c1.color);
// 定义结构体数组
struct Cat cats[3] = {
{"汤姆",16,"蓝色"},
{"杰瑞",18,"褐色"},
{"唐老鸭",19,"白色"}
};
// 结构体数组名作为实际参数
test3(cats,3);
// 定义结构体数组并初始化
struct Cat cats1[3] = {
{"汤姆",16,"蓝色"},
{"杰瑞",18,"褐色"},
{"唐老鸭",19,"白色"}
};
// 调用函数
struct Cat *p = test4(cats1,3);
struct Cat *w;
// 通过指针运算遍历数组
for(w = p; w < p + 3; w ++)
{
// p[i][j] = *(p[i]+j) = *(*(p+i)+j) 三者等价
// 通过结构体指针访问符访问结构体的成员
printf("%s----%d----%s\n",w -> name,w -> age,w -> color);39 }
}
结构体类型求大小
规则:字节对齐(数据在内存中存储在其类型大小的整数倍上)
1.首先保证结构体中的成员存储在自身的对齐边界(类型大小的整数倍);
2.在满足1的条件下,最终大小要满足最大成员所占存储单元的整数倍;
为什么要使用字节对齐:
节省内存,提高访问效率
在GNU标准中,可以在定义结构体时,指定对齐规则:
1__attribute__((packed)); 结构体所占内存大小是所有成员所占内存大小之和
2__attribute__((aligned(n))); 设置结构体占n个字节,如果n比默认值小,n不起作用;n必须是2的次方
柔性数组:
struct st
{
...
char a[0]
}
柔性数组不占有结构体的大小。
eg:
/**
* 求结构体数据类型的大小
*/
#include <stdio.h>
// 定义测试结构体
struct TEST1
{
char a;// 1
int b; // 4
};
struct TEST1_1
{
char a;// 1
int b;// 4
}__attribute__((packed));// 取消字节对齐,取消之后,结构体数据类型大小就等于其所有成员的数据类型之和
struct TEST1_2
{
char a __attribute__((aligned(2)));
int b;
};
struct TEST2
{
char a;// 1
short c; // 2
int b; // 4
};
struct TEST3
{
int num;// 4
char name[10];// 10
char sex;// 1
int age;// 4
double score;// 8
};
struct TEST4
{
int num;// 4
short name[5];// 10
char sex;// 1
int age;// 4
int scores[2];// 8
};
int main()
{
// 创建结构体变量
struct TEST1 test1;
struct TEST2 test2;
struct TEST3 test3;
struct TEST4 test4;
struct TEST1_1 test1_1;
struct TEST1_2 test1_2;
// 计算大小
printf("%lu\n",sizeof(test1));
printf("%lu\n",sizeof(test2));
printf("%lu\n",sizeof(test3));
printf("%lu\n",sizeof(test4));
printf("%lu\n",sizeof(test1_1));
printf("%lu\n",sizeof(test1_2));
}
共用体/联合体类型
定义:
使几个不同的变量占用同一段内存的结构。共用体按定义中需要存储空间最大的成员来分配存储单元,其他成员也是用该空间,他们的首地址是相同。
定义格式:
union 共用体名称
{
数据类型 变量名;
数据类型 变量名;
...
};
共用体的定义和结构体类型类似:
1.可以有名字,也可以匿名;
2.共用体在定义时也可以定义共用体变量;
3.共用体在定义时也可以初始化成员;
4.共用体也可以作为形参和返回值类型使用;
5.共用体也可以定义共用体数组
...
也就是说,结构体的语法,共用体都支持。
注意:
1.共用体弊大于利,尽量少用,一般很少用;
2.共用体变量在某一时刻只能存一个数据,并且也只能取出一个数
3.共用体和结构体都是自定义数据类型,用法类似于基本数据类型
共用体可以是共用体的成员,也可以是结构体的成员。
结构体可以是结构体的成员,也可以是共用体的成员。
枚举
定义:
我们一般情况下,定义常量使用宏定义(#defne 宏名称 值),宏定义非常适合没有关联关系的常量;但是有时候我们可能需要对一组拥有关联关系的量进行定义,比如 周一~周日、1月~12月 等,那么使用宏定义,就不是很清晰在,这个时候就需要使用到枚举。
枚举的存在就是将多个拥有关联关系的常量组合到一起,提高代码的可读性。
说明:
枚举类型定义了一组常量,我们在开发中直接使用这些常量。(常用)
当然枚举类型也可以类似于结构体一样定义变量等操作。(不常用)
枚举常量有默认值,从0开始依次加1;我们可以在定义时指定它的值,如果个别没有赋值,可以根据赋值依次加1推导
特点:
定义了一组常量,类似于定义了多个自定义常量(宏定义)
提供了代码的可读性(避免了魔术数字)
定义语法:
定义枚举类型名以后就可以定义该枚举类型的变量
enum 枚举类型名 变量表;
在定义枚举类型的同时定义该枚举类型的变量。
enum 枚举类型名{ 枚举元素列表 } 变量表;
直接定义枚举类型变量。
enum { 枚举元素列表 } 变量表;
typedef
说明:给类型重命名,不会影响到类型本身
作用:给已有的类型起别名
格式:
typedef 已有类型名 新别名;
使用:
// 定义结构体
struct Student
{
int id;
char*name;
charseXj
int age;
};
// 类型重命名
typedef struct student stu;
// 定义变量
struct Stu stu = {1,"张甜”,'M',21};
//定义结构体的同时类型重命名
typedef struct PersonInfo
{
int a;
double b;
}Per;
// 定义变量
struct Per per = {2,5};
应用场景
1. 数据类型复杂(结构体,共用体,枚举,结构体指针)时使用
2. 为了跨平台兼容性,例如:
1. size_t:类型重命名后的数据类型,typedef unsigned long size_t;
2. unit_16:类型重命名后数据类型