struct结构体(大小为全部数据类型的和)
结构是一些成员变量的集合,每个成员可以是不同类型的变量
struct tag //自定义结构体数据类型(比如int,直接在内存划了一个4个字节的空间)
{
member-list; //成员列表
}variable-list; //结构体变量列表
struct Stu
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
}; //分号不能丢
struct Book
{
char name[20];//书名
char author[20]; //作者
int price;//价格
char id[15];//书号
}book4, book5;//←这里也是创建结构体变量,在main外面的话是全局变量
struct Book book1 = {"冒险小虎队", "托马斯", 20, "ABC"};//创建struct Book类型 名字为book1
struct Book book2;
struct Book book3;
在声明结构的时候,可以不完全的声明(下面的两个结构在声明的时候省略掉了结构体标签(tag))
//匿名结构体类型
struct
{
int a;
char b;
float c; }x;
struct
{
int a;
char b;
float c; }a[20], *p;
结构体的自引用
struct Node
{
int data;
struct Node* next;//指针
};
typedef struct Stu
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
}Stu; //分号不能丢
结构体内存对齐
#include <stddef.h>
宏,用来计算结构体成员相对于起始位置的偏移量
offsetof(结构体类型名,结构体成员名)
offsetof(struct S1, c1);
//练习1
struct S1
{
char c1;//第一个对其数为1,1和8最小的整数倍为1(0)
int i; //第二个对其数为4,4和8最小的整数倍位4(4-7)
char c2;//第三个对其数为1,1和8最小的整数倍为1(8)
};
printf("%d\n", sizeof(struct S1));//总大小为最大对其数4的倍数,只能是12(0-11)
//练习2
struct S2
{
char c1;//第一个对其数为1,1和8最小的整数倍为1(0)
char c2;//第二个对其数为1,1和8最小的整数倍为1(1)
int i; //第三个对其数为4,4和8最小的整数倍位4(4-7)
};
printf("%d\n", sizeof(struct S2));//总大小为最大对其数4的倍数,只能是8(0-7)
//练习3
struct S3
{
double d;//第一个对其数为8,8和8最小的整数倍为8(0-7)
char c; //第二个对其数为1,1和8最小的整数倍为1(8)
int i; //第三个对其数为4,4和8最小的整数倍位4(12-15)
};
printf("%d\n", sizeof(struct S3));//总大小为最大对其数4的倍数,只能是16(0-15)
//练习4-结构体嵌套问题
struct S4
{
char c1;//第一个对其数为1,1和8最小的整数倍为1(0)
struct S3 s3;//第二个对其数为16,16和8最小的整数倍为8(8-23)
double d;//第三个对其数为8,8和8最小的整数倍为8(24-31)
};
printf("%d\n", sizeof(struct S4));//总大小为最大对其数4的倍数,只能是32(0-31)
结构体节省空间
1.明确结构体成员变量的值域,用适合的类型;
2.根据字节对齐的规则,将成员变量的类型根据从小到大进行声明,尽量减少中间的填补空间;
3.取消字节对齐。#pragma pack(1),自定义默认对齐,一般为2的n次方,设置为1等于取消字节对齐,用时间换空间。
4.根据特定的使用的条件下,可以考虑将结构体换成共用体。
结构体传参(尽量传地址)
#include <stdio.h>
struct S
{
int data[1000];
int num;
}
int main()
{
struct S ss = {{1,2,3,4,5}, 100};
print(ss);
print(&ss);
}
void print(struct S s)//传结构体
{
printf("%d %d %d %d\n",s.data[0],s.data[1],s.data[2],s.num);
}
void print(struct S* ps)//传结构体地址
{
printf("%d %d %d %d\n",ps→data[0],(*ps).data[1],ps→[2],s.num);//可用箭头,可用小数点
}
结构体位段
位段的成员必须是整型家族int ,unsigned int,或者char
//冒号+数字
struct A
{
int _a:2(只拿2个比特位)
int _b:5(只拿5个比特位)
int _c:10(只拿10个比特位)
int _d:30(只拿30个比特位)
}
结构体柔性数组
c99中,结构中的最后一个元素允许是未知大小的数组,这就叫做【柔性数组】成员
柔性数组成员前面必须至少有一个其他成员。
sizeof()返回的结构体大小不包含柔性数组的内存。
包含柔性数组成员的结构用malloc()函数进行内存的动态分配,并且分配的内存应该大于结构中的大小,以适应柔性数组的预期大小。
struct S
{
int n;
float s;
int *arr;//不是柔性数组
}
int main()
{
struct S* ps = (struct S*)malloc(sizeof(struct S));//结构体开辟在堆上
if (ps == NULL)
{
return 1;
}
int *prt2 = (int*)malloc(sizeof(int)*4);
if (ptr2 == NULL)
{
return 1;
}
else
{
ps->arr = ptr2;
}
//赋值
ps->n = 100;
ps->s = 5.5f;
for(int i = 0; i < 4; i++)
{
scanf("%d", &(ps->arr[i]));
}
printf("%d %f\n", ps->n, ps->s);
for(int i = 0; i < 4; i++)
{
printf("%d ", ps->arr[i]);
}
//调整数组大小
struct S* ptr = (struct S*)relloc(ps->arr, sizeof(int)*10);
if (ptr == NULL)
{
return 1;
}
else
{
ps->arr = ptr;
}
free(ps->arr);//第一次free结构体的一个成员
ps->arr = NULL;
free(ps);//第二次free整个结构体
ps = NULL;
return 0;
}
struct S
{
int n;
float s;
int arr[];//柔性数组 数组大小未知
//int arr[0];//柔性数组 数组大小未知
}
int main()
{
struct S* ps = (struct S*)malloc(sizeof(struct S) + sizeof(int)*4);//8+16
if (ps == NULL)
{
return 1;
}
//赋值
ps->n = 100;
ps->s = 5.5f;
for(int i = 0; i < 4; i++)
{
scanf("%d", &(ps->arr[i]));
}
printf("%d %f\n", ps->n, ps->s);
for(int i = 0; i < 4; i++)
{
printf("%d ", ps->arr[i]);
}
//调整数组大小
struct S* ptr = (struct S*)relloc(ps, sizeof(struct S) + sizeof(int)*10);
if (ptr == NULL)
{
return 1;
}
else
{
ps->arr = ptr;
}
free(ps);
ps = NULL;
return 0;
}
第二个有两个好处,1为方便内存释放(第一个要释放2次,因为用了两次malloc,他们的空间可能不是连续的),2为有利于访问速度(连续的内存有益于提高访问速度,也有益于减少内存碎片)
枚举enum(大小为1个数据类型的大小,只取其中之一)
枚举括号里的为枚举的可能取值,每一个可能取值都是常量
定义的时候可以赋值,否则从0开始
枚举大小是其中之一的取值大小
enum Day//星期
{
Mon, //0
Tues, //1
Wed, //2
Thur, //3
Fri, //4
Sat, //5
Sun //6
};
enum Sex//性别
{
MALE, //0
FEMALE, //1
SECRET //2
};
enum Color//颜色
{
RED, //0
GREEN, //1
BLUE //2
};
联合体union(大小为最大数据类型的大小)
#include <stdio.h>
int check_sys();
int main()
{
if (1 == check_sys())
printf("小端\n");
else
printf("大端\n");
return 0;
}
//利用结构体的储存来判断
int check_sys()
{
union checksys
{
char a;
int b;
}cs;
cs.b = 1;
return cs.a;
}
联合体大小计算
union Un1
{
char c[5];//单个成员大小1,总成员大小5(5是最大成员大小),默认对齐数8,对齐数1
int i; //单个成员大小4,总成员大小4,默认对齐数8,对齐数4(4是最大对齐数)
};
//5不是4的倍数,所以4*2,大小是8
union Un2
{
short c[7];//单个成员大小2,总成员大小14(14是最大成员大小),默认对齐数8,对齐数2
int i; //单个成员大小4,总成员大小4,默认对齐数8,对齐数4(4是最大对齐数)
};
//14不是4的倍数,所以大小是16
printf("%d\n", sizeof(union Un1));
printf("%d\n", sizeof(union Un2));