目录
一、结构体 (struct)
#include <stdio.h>
struct book
{
char name[20];
float price;
char id[13];
};
int main()
{
struct book b = { "C语言", 6.66, "985211" };
return 0;
}
#include <stdio.h>
struct
{
char name[20];
float price;
char id[13];
}b;//匿名结构体类型,只能用一次,因为没有名字
int main()
{
return 0;
}
- 一个结构体再包含一个同类型的结构体变量,这样结构体变量大小就会无穷大,是不合理的
- 结构体类型重命名:typedef
#include <stdio.h>
typedef struct book
{
char name[20];
float price;
char id[13];
}book;
int main()
{
book b = { "C语言", 6.66, "985211" };
return 0;
}
1、结构体内存对齐
1、结构体内存对齐规则:①:结构体第一个成员对齐到和结构体变量起始位置偏移量为0的地方
②:其他成员对齐到对齐数的整数倍处(偏移量为对齐数的整数倍)
③:对齐数:成员自身类型的大小与编译器默认对齐数的较小值(vs默认对齐数为8,Linux无默认对齐数,对齐数就是自身成员的大小)
④:结构体总大小是最大对齐数的整数倍
⑤:如果嵌套了结构体的情况,嵌套的结构体成员对齐到自己成员中最大对齐数的整数倍处,结构体的整体大小就是最大对齐数的整数倍
⑥:如果结构体成员中有数组,char[5]当做5个char存放,int[4]当做4个int存放
2、为什么存在内存对齐?①:平台原因(移植原因):不是所有的硬件平台都能访问任意地址的任意数据,某些硬件平台只能在某些地址处取某些特定类型的数据
②:性能原因:拿空间换时间
既能节省时间又能节约空间的小tip :让占用空间小的成员尽量集中在一起
3、修改默认对齐数:#pragma pack(1)修改默认对齐数为1
……
#pragma pack( )取消设置的对齐数,还原为默认
4、结构体传参推荐传地址
2、结构体实现位段
1、位段和结构体类似,两个不同:①:成员必须是int、unsigned int、char、short、long、自定义的枚举类型等整型
②:成员最后一个冒号一个数字最后在加一个分号
#include <stdio.h>
struct stu
{
char a : 1;
char b : 1;
char c : 1;
}s;
void show(struct stu s)
{
printf("%d", sizeof(s));
}
int main()
{
show(s);
return 0;
}
位段的内存分配
1、位段的几个成员共用一个字节,所以有的成员的起始位置并不是某个字节的起始位置
2、内存分配地址时,一个字节给一个地址,字节内的比特位没有地址,所以不能对位段成员&,不能用scanf输入
3、给结构体成员赋值的方法:①:直接赋值,s.a = 1,s.b = 2
②:通过初始化赋值
③:通过移位操作赋值
4、
#include <stdio.h>
struct stu
{
char a : 1;
char b : 1;
char c : 1;
}s;
int main()
{
show(s);
s.a = 'a';//正确
scanf("%d", &a);//错误
return 0;
}
柔性数组
#include <stdio.h>
#include <stdlib.h>
struct stu
{
int n;
int a[0];
};
int main()
{
printf("%d ", sizeof(struct stu));
struct stu* p = (struct stu*)malloc(sizeof(struct stu) + sizeof(int) * 10);
struct stu* ps = (struct stu*)realloc(p, 20);
if (ps != NULL)
{
p = ps;
p->a[1] = 1;
printf("%d", p->a[1]);
}
free(ps);
ps = NULL;
return 0;
}
1、柔性数组是必须结构体最后一个元素,且是未指定大小的数组
2、无论动态内存分配前还是分配后,柔性数组的大小都不会包括在整个结构体变量的大小中
模拟实现柔性数组
#include <stdio.h>
#include <stdlib.h>
struct s
{
int a;
int* arr;
};
int main()
{
struct s* ps = (struct s*)malloc(sizeof(struct s));
if (ps != NULL)
{
ps->arr = (int*)malloc(sizeof(int) * 10);
if (ps->arr != NULL)
{
for (int i = 0; i < 10; i++)
{
ps->arr[i] = i;
}
}
}
free(ps->arr);//先释放arr,ps先释放就找不到arr了
free(ps);
ps = NULL;
return 0;
}
3、联合体(nuion)
1、联合体的成员共用同一块内存空间,大小至少是最大成员的大小 (大小最大对齐数的整数倍)
2、当最大成员的大小不是结构体最大对齐数的整数倍时,对齐到最大对齐数的整数倍处
3、
#include <stdio.h>
union stu
{
int a;//对齐数为4
short b[7];//对齐数为2,则最大对齐数为4,\
2*7 = 14 14 + 2 = 16 % 4 = 0
};
int main()
{
union stu s;
printf("%d", sizeof(s));
return 0;
}
判断大小端的两种方法
①:
#include <stdio.h>
void check_sys(int* p)
{
if (*(char*)p == 1)
printf("小端\n");
else
printf("大端\n");
}
int main()
{
int n = 1;
check_sys(&n);
return 0;
}
②:利用联合体
union un
{
char a;
int b;
};
int main()
{
union un u;
u.b = 1;
if (u.a == 1)
printf("小端");
else
printf("大端");
return 0;
}
4、枚举(enum)
1、
#include <stdio.h>
enum color
{
black,//后面要加逗号
white = 30,
yellow,
red//最后一个元素后不加逗号
};
int main()
{
printf("%d\n", black);//0 0
printf("%d\n", white);//1 30
printf("%d\n", yellow);//2 31
printf("%d\n", red);//3 32
return 0;
}
2、{ }里面的内容是枚举类型所创建的常量的可能取值,本质是常量,真正的常量(可以:
int a[yellow] = {0}, case black:)
3、枚举首元素默认为0
4、枚举所创建的常量也是要类型的(自定义的枚举类型),但c中可以赋整型给枚举常量,
cpp类型检查严格,不可以
枚举的优点,为什么不用宏定义?
①:增加代码的可读性,可维护性 ②:枚举有类型检查 ③:便于调试 ④:使用方便
是冬至呀-CSDN博客https://blog.csdn.net/2302_81218652?spm=1000.2115.3001.5343