嗨,小左又来了呢!虽然C语言系统规定了很多数据类型,但是并不能描述完全所有的数据,因此就出现了自定义的一些数据类型。虽说是自定义,但是它也有自己的规则,另外很多时候这些类型都会帮上大忙,所以这篇文章给它们做个总结!欢迎小伙伴们来探讨~
一些常见的自定义类型
一、结构体
1、声明、定义、初始化
结构体是一个不同类型的值的集合,这些值被称为成员变量。下面看一下结构体声明:
//方法1:
struct tag
{
memberList;
}variableList;
//方法2:
struct tag
{
memberList;
};
一般都会使用方法2。因为结构体的声明一般是写在 main 函数之外,若是直接定义一些结构体变量,就额外创建了全局变量,这样不好而且也没必要,具体可以看一下这篇回答:为什么避免使用全局变量?
结构体变量的定义及初始化,这二者可以一起也可以分开,具体视情况而定:
//合在一起
struct tag varibaleName = {memberListValue};
//分开
struct tag varibaleName;//结构体变量定义
varibaleName = {memberListValue};//结构体变量初始化赋值
还有一个特殊情况,若是声明的时候没有 tag (变量名),结构体也是合法的,此时叫它匿名结构体。直接使用是没有任何意义的,所以一般也不会直接使用。使用时都是作为结构体成员变量被嵌套使用的。下面看具体声明:
struct
{
memberList;
}variableList;//定义的结构体变量只能使用1次,而且必须在定义的时候直接初始化
//例子:
struct
{
int a;
int b;
}s1 = { 10, 12 };//无意义
int main()
{
printf("%d", s1.a);
return 0;
}
2、结构体的自引用和结构体传参
-
结构体自引用
//错误方法,没有终止条件,会无限调用 struct s { int a; struct s next; }; //正确方法,使用结构体指针,只是完成指向下一个结构体的地址就结束。 struct s { int a; struct s* next; }; -
结构体传参
这部分在前面的讲解已经很完备,不再赘述。具体可点击查看->《系统梳理3》
3、结构体的内存对齐问题
- 对齐数
对 齐 数 = m i n ( 编 译 器 默 认 对 齐 数 , 成 员 变 量 大 小 ) 对齐数 = min(编译器默认对齐数,成员变量大小) 对齐数=min(编译器默认对齐数,成员变量大小)
VS中默认对齐数为8;Linux使用gcc编译器,没有默认对齐数,自身大小就是它的对齐数 - 结构体内存对齐规则
1、第一个结构体成员变量的地址在结构体变量偏移量为0处
2、其他成员变量要对齐到自身对齐数的整数倍的地址处(若嵌套了结构体,嵌套的结构体对齐到自己的最大对齐数的整数倍)
3、结构体总大小为最大对齐数的整数倍,此时结构体整体大小就是所有对齐数的整数倍
举例(成员变量注释均为偏移量):
//例1
struct S1
{
char c1;//0
int i;//4
char c2;//8
};
//offsetof计算的就是上面成员变量的偏移量,即注释内容。仅以例1为例
printf("%d\n", offsetof(struct S1,c1));
printf("%d\n", offsetof(struct S1, i));
printf("%d\n", offsetof(struct S1, c2));
printf("%d\n", sizeof(struct S1));//12,最大对齐数4
//练习2
struct S2
{
char c1;//0
char c2;//1
int i;//4
};
printf("%d\n", sizeof(struct S2));//8,最大对齐数4
//练习3
struct S3
{
double d;//0
char c;//8
int i;//12
};
printf("%d\n", sizeof(struct S3));//16,最大对齐数8
//练习4-结构体嵌套问题
struct S4
{
char c1;//0
struct S3 s3;//8,对齐数8
double d;//24
};
printf("%d\n", sizeof(struct S4));//32,最大对齐数8
偏移量的计算可以用 offsetof ( ) 宏去实现。下面大概介绍一下这个宏:
头文件:<stddef.h>,声明: " size_t offsetof( structName, memberName ); "
传入结构体名字和要计算的成员变量的名字,返回计算的偏移量起始位置。实例见上例1。
-
为什么存在内存对齐
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
数据结构(尤其是栈)应该尽可能地在自然边界上对齐。 原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。结构体的内存对齐是通过空间的浪费换取时间效率的提升,所以在设计结构体的时候,但是为了节省空间,尽量做到让占用内存小的成员集中在一起。
-
修改默认对齐数
一般修改的设置值都是2的幂次方数。
#pragma pack(设置值)
#pragma pack() //取消设置值,还原为默认
4、位段(位段式结构体)
位段的声明与结构体类似,但是有一些不同:
1.成员必须是 char 、int 、 unsigned int 、signed int
2.成员名字后还得加一个冒号和数字,数字表示这个变量占的内存字节位数,具体举一个例子看一下:
struct A {
int _a : 2;//第一个是int,开辟4字节,32个比特位,_a占2个比特位
int _b : 5;//剩余30个比特位,_b占5个比特位
int _c : 10;//剩余25个比特位,_b占10个比特位
int _d : 30;//剩余15个比特位,_c不够用,_c是int,所以再开辟4字节
};
printf("%d\n", sizeof(struct A));//8
-
位段的内存分配
位段的成员只能是 int unsigned int signed int 或者是 char (属于整形家族)类型。 所以它的内存空间是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。位段是不跨平台的(很多没有标准规定),注重可移植性的程序应该避免使用位段。、
位段目前的缺点——跨平台性差:int 位段被当成有符号数还是无符号数是不确定的;位段中最大位的数目不能确定(16位机器最大16,32位机器最大32,写成大于16的值,在16位机器会出问题;位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义;当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。
-
位段的总结及应用
对一般结构体做了优化,达到同样的效果的同时一定程度上节省了空间开销,但是不可跨平台,可移植性差。
应用:位段描述计算机网络协议,提高传输效率
二、枚举
枚举:把有限的可能取值一一列举。
1、枚举的结构声明
enum 枚举名 //枚举类型
{
该枚举类型的可能取值
};
对于枚举变量可以赋值也可以不赋。赋值的话,按照赋的值;否则,默认从0开始依次递增。下面看举例:
enum Sex//性别
{
MALE,
FEMALE
};
enum Color//颜色
{
RED = 1,
GREEN = 2,
BLUE = 3
};
2、枚举的优点
枚举的作用和#define 定义的常量基本一致,给常量一个“ 别名 ”,那么相较后者它的优势在哪里呢?
- 增加代码的可读性和可维护性
- 和#define定义的标识符比较枚举有类型检查,更加严谨。
- 防止了命名污染(封装)
- 便于调试
- 一次定义多个常量时,使用方便
3、枚举常量的使用
直接看例子:
enum Color//颜色
{
RED=1,
GREEN=2,
BLUE=4
};
enum Color clr = GREEN;//只能拿枚举常量给枚举变量赋值,才不会出现类型的差异。
clr = 5; //有问题!!
关于枚举常量提一点,赋值的时候只能拿枚举常量给枚举变量赋值,才不会出现类型的差异,否则其实是有问题的。问题在C语言其实对数据类型的灵敏度没有那么高,直接像赋值的第二行给它赋一个常量数值或许没问题,但是放到C++就不行了,它会因为类型不兼容,直接报错,所以小伙伴们,编程的时候一定要严谨喔~
三、联合(共同体)
联合也是一种特殊的自定义类型,它的特别之处在于它所包含的一系列成员变量的内存空间是共用的。老规矩,还是先看一下它的声明:
1、联合的声明
union 联合名
{
联合成员变量;
};
除了关键字变成了 union ,其余部分可以参考结构体,是完全一致的。
2、联合的特点
联合的成员变量是不会同时出现的一组变量值,它们共用同一块内存空间,所以这样一个联合变量的大小至少是最大成员的大小,且它们的地址都是一样的。
所以需要注意,使用了其中一个成员变量就不可以使用其它的了,如果还不理解可以看一下这个例子:
union Un
{
int i;
char c;
};
union Un un;
//下面输出的结果是什么?
un.i = 0x11223344;
un.c = 0x00;
//结果是11223300(VS小端存储)
printf("%x\n", un.i);
3、联合的内存大小计算
联合的大小至少是最大成员的大小。
当最大成员大小不是最大对齐数的整数倍的时候,要对齐到最大对齐数的整数倍。
最后再来看两个例子:
union Un1 //对齐数4
{
char c[5];//5*1 = 5
int i;//4
};
union Un2 //对齐数4
{
short c[7];//7*2 = 14
int i;//4
};
//下面输出的结果是什么?
printf("%d\n", sizeof(union Un1));//8
printf("%d\n", sizeof(union Un2));//16
over~~~😁😁😁
本文详细介绍了C语言中的自定义类型,包括结构体的声明、定义、初始化,内存对齐问题,位段的使用,枚举的声明、优点及使用,以及联合的声明、特点和内存大小计算。结构体的内存对齐旨在提高效率,位段节省空间但不跨平台,枚举增强代码可读性和类型安全性,联合则是一组成员变量共享内存空间。
381

被折叠的 条评论
为什么被折叠?



