一、结构体
结构是一些值的集合,这些值称为成员变量,结构的每个成员都可以是不同类型的变量。
1.结构体的声明
struct tag
{
member-list;
}variable-list;例子:
struct stu
{
char name[20];
int age;
char sex[5];
char id[20];
};//分号不能丢2.结构体变量的创建和初始化
struct stu
{
char name[20];
int age;
char sex[5];
char id[20];
}s3,s4;//全局变量
struct stu s2;
int main()
{
struct stu s1;//局部变量
return 0;
}特殊的结构体声明:匿名结构体类型
没有名称,只能使用一次。
struct
{
int a;
char b;
float c;
}x;
int main()
{
return 0;
}下面的代码是一个非法的代码,编译器觉得是两个不同的结构体。
struct
{
int a;
char b;
float c;
}x;
struct
{
int a;
char b;
float c;
}*p=&x;
int main()
{
return 0;
}3.结构体的自引用
在结构中包含一个类型为该结构本身的成员是否可以呢?

答案是可以,但是要写成指针的形式来指向下一个数据,如果是结构体里包含结构体,然后包含的结构体再包含结构体,就不知道要分配多少的内存了,所以结构体里可以包含该类型的结构体指针。
4.结构体内存对齐
我们已经掌握了结构体的基本使用了。
现在我们深入讨论一个话题:计算结构体的大小,也就是结构体的内存对齐。
对齐规则:
1. 结构体的第一个成员需对齐到结构体变量起始位置,即偏移量为 0 的地址处。
2.从第二个成员变量开始,对齐到某个对齐数的整数倍的地址处。
对齐数=编译器默认的的一个对齐数与该成员变量的大小的较小值
—vs中默认为8
—linux中gcc没有默认对齐数,对齐数就是该成员变量的大小。
3.结构体总大小为最大对齐数的整数倍
4.如果嵌套了结构体的情况,嵌套的结构体成员对齐到自己成员中最大对齐数的整数倍处,结构体整体的大小就是最大对齐数的整数倍。
修改默认对齐数:
#pragma这个预处理指令,可以改变编译器的默认对齐数
#pragma pack(1)//设置默认对齐数为1
5.结构体传参
传值调用: 传址调用:


传址调用更好,传值调用浪费空间和时间,地址只是4个或者8个字节。如果说怕改变数据的值,指针可以用const约束。
6.结构体实现位段
位段的声名和结构是类似的,有两个不同:
1.位段的成员必须是int、unsigned int或者signed int,在c99中位段的类型也可以选择其他整型家族类型,比如:char
2.位段的成员名后边有一个冒号和一个数字。
结构体:

位段(数字代表所占的二进制位的个数)

位段的内存分配
位段的空间是按照需要以4个字节或者一个字节的方式来开辟的。
位段涉及很多不确定性,位段是不跨平台的,注重可移植的程序应该避免使用位段。
在vs环境下:
1.分配到的内存是从右向左使用的(低位向高位)
2.剩余空间不够下一个成员使用的时候直接浪费。
注意事项:
位段的几个成员共有一个字节,有的成员的起始位置不是某个字节的起始位置,那么这些位置处是没有地址的。内存中每个字节分配一个地址,一个字节内部的bit位是没有地址的。所以不能对位段的成员使用&操作符,这一样就不能用scanf给位段成员输入值,只能先输入一个变量中,然后赋值给位段的成员。

二、联合体
1.联合体类型的声名
像结构体一样,联合体也是由一个或者多个成员构成,这些成员可以是不同的类型。联合体的关键字是union,但是编译器只为最大的成员分配足够的内存空间。联合体的特点是所有成员共用同一块内存空间。所以联合体也叫做:共用体。
给联合体其中一个成员赋值,其他成员的值也跟着变化。


2.联合体的特点
联合体的成员是共用同一块内存空间的,这样一个联合体变量的大小至少是最大成员的大小。
对比一下相同成员的结构体和联合体所占的内存情况。

当最大成员的大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。

现在我们要统计商品信息,共有三种商品,分别是图书、杯子、衬衫。这三种商品都需要统计库存量、价格和商品类型,图书还需要统计书名、作者、页数,杯子还需要统计设计,衬衫还需要统计设计、可选颜色、可选尺寸。
我们可以用结构体来统计
struct gift_list
{
//公共部分
int rest_num;
int price;
char type[20];
//非公共部分
char book_name[20];
char author[20];
int page;
char design[20];
char colour[20];
int size;
};但是会产生一个问题,就是会浪费很多空间,比如,我们在统计一本图书的信息的时候,我们不需要统计它的设计等内容。在这个时候我们就可以应用联合体来完成统计,这样会节省很多空间。
代码如下:
struct gift_list
{
//公共部分
int rest_num;
int price;
char type[20];
//非公共部分
union
{
struct
{
char book_name[20];
char author[20];
int page;
}book;
struct
{
char design[20];
}mug;
struct
{
char design[20];
char colour[20];
int size;
}shirt;
}item;
};联合的一个小练习,判断机器是大端还是小端

三、枚举类型
1.枚举类型的声名
枚举类型顾名思义就是一一列举,把可能的取值一一列举。
比如现实生活中:
一周的星期一到星期天可以一一列举。
性别有:男、女、保密可以一一列举。
月份可以一一列举。
以上三种数据就可以用枚举类型。
例子:

2.枚举类型的可能取值
若未定义值的话,默认第一个为0,剩下的值递增

若定义第一个选项的值的话,情况如下

定义中间元素的值,情况如下

我们可以用#define定义常量,为什么要使用枚举呢。
因为相比于#define,枚举有以下几个优点:
1.增加代码的可读性和可维护性
2.和#define定义的标识符比较枚举有类型检查,更加严谨。
3.便于调试,预处理阶段会删除#define定义的符号
4.使用方便,可以一次定义多个常量。
5.枚举常量是遵循作用域规则的,枚举声名在函数内,只能在函数内使用。
在c语言中可以拿整数给枚举变量赋值,但是在c++中不行,c++的类型检查比较严格。
总结
到此这篇关于c语言实现结构体、联合和枚举的文章就介绍到这了,更多相关c语言 结构体、联合和枚举内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论