【C语言】自定义类型

1. 结构体

1.1 结构体基本概念

结构体是一些值的集合,这些值称为成员变量,结构的每个成员可以是不同类型的变量。

1.2 结构体的声明

struct tag //自定义结构体名
{
    member-list; //成员列表
}variable-list; //变量列表

1.3 特殊的结构体声明

声明结构体类型时,可以不完全声明。

#include <stdio.h>

struct //匿名结构体类型
{
    char a;
    int b;
    float f;
}x;

struct
{
    char a;
    int b;
    float f;
}*p;

int main()
{
    p = &x;
    return 0;
}

上面代码定义了两个一样的匿名结构体类型,分别声明了两个变量x和*p,但是运行main函数时,却会出现以下问题:

虽然两个结构体成员一样,但是编译器任然认为这是两个结构体,一般情况下匿名结构体只使用一次。当希望一个结构体只使用一次时,就可以使用匿名结构体。

1.4 结构体的自引用

在结构体中包含一个类型为该结构本身的成员。

//代码1
struct Node
{
    int a;
    struct Node next;
};

//代码2
struct Node
{
    int a;
    struct Node* next;
};

代码1运行,但是sizeof(struct Node)计算结构体大小时,struct Node中又包含了struct Node,会一直套娃,系统也不知道要怎么才能算出大小。代码2的话,把自引用换成指针,通过指针指向的地址找引用的结构体。

1.5 结构体变量的定义和初始化

struct Stu
{
    char name[20];
    int age;
    char sex[5];
}S1; //全局变量


int main()
{
    struct Stu S2 = { "zhangsan", 20, "nan" }; //局部变量
    return 0;
}

结构体变量可以分为两种,一种是定义结构体完毕后就定义变量,这种属于全局变量;一种是在需要的地方定义结构体变量,这种是局部变量。初始化在一对大括号 { } 里面。

1.6 结构体内存对齐

结构体对齐规则:

  1. 第一个成员在与结构体变量偏移量为0的地址处。
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。对齐数 = 编译器默认的一个对齐数与该成员大小的较小值
  3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整倍数。
  4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自身的最大对齐数的整倍数处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
  • VS中默认的值为8
  • Linux中没有默认对齐数,对齐数就是成员自身的大小
struct tag1
{
    char c1;
    int a;
    char c2;
};

struct tag2
{
    char c1;
    char c2;
    int a;
};

struct tag3
{
    char c1;
    struct tag1 S;
    int a;
};


int main()
{
    printf("%zd\n", sizeof(struct tag1));
    printf("%zd\n", sizeof(struct tag2));
    printf("%zd\n", sizeof(struct tag3));
    return 0;
}

当上面代码运行打印tag1和tag2所占字节大小时,我们首先算其中成员大小,两个char类型,一个int类型,加起来应该是6字节。但实际上运行打印出来的却是:

这是因为对齐数的规则在里面处理了数据的存放。

tag1把c1放进去,再放a时,a的本身是4,对齐数是8,比较之后a的对齐数是4,存储4根据规则需要4的整倍数,1、2、3都不是,但是空间被浪费了,到4时把a按照自身大小存储后,再把c2存储。但是此时tag1总字节为9,结构体总大小是最大对齐数整倍数,最大对齐数是4,9不是4的整倍数,再往下浪费3个字节空间到12时停止。

tag2存放c1,c2后找到4,其中浪费2个字节,再把a放进去,总大小为8,是最大对齐数的整倍数,停止。

设计结构体时尽量把小字节的成员放在一起,可以有效的节省空间,避免浪费。

当tag3嵌套了结构体时,先把自身c1放进去,然后看嵌套的结构体最大对齐数是多少,按照最大对齐数整数倍开始存放,存放规则与tag1相同,存放完再存放自身a。存放完毕后看总大小是否为最大对齐数整数倍,嵌套的结构体总体大小虽然占12个字节,但是最大对齐数是4,按最大对齐数来算。此时总大小20字节,是最大对齐数的倍数。

1.7 修改默认对齐数

#pragma pack(1) //修改默认对齐数为1
struct tag1
{
    char c1;
    int a;
    char c2;
};
#pragma pack() //还原为默认对齐数
struct tag2
{
    char c1;
    int a;
    char c2;
};

int main()
{
    printf("%zd\n", sizeof(struct tag1));
    printf("%zd\n", sizeof(struct tag2));
    return 0;
}

默认对齐数可以通过#pragma pack()来进行修改。

2. 枚举

枚举就是把可能的值一 一列举,比如性别:男,女,季节:春,夏,秋,冬。

2.1 枚举类型的定义

enum Color
{
	RED,
	GREEN = 2,
	BLUE
};

enum Sex
{
	MALE,
	FEMALE
};

int main()
{
	printf("%d\n", RED);
	printf("%d\n", GREEN);
	printf("%d\n", BLUE);
	return 0;
}

枚举类型通过enum关键字创建, { } 中可能的取值都叫做枚举常量,其中的常量都是有值的,默认从0开始,依次往下加1,也可以指定值的大小,接下来的值会从被指定的值的基础上开始加1。

2.2 枚举的用法

enum Color
{
	RED,
	GREEN,
	BLUE
};

int main()
{
    enum Color C = GREEN;
    C = 5;
    return 0;
}

枚举类型变量只能使用枚举常量赋值才不会出现类型差异。赋值后改变变量的值不会影响枚举常量的值。

3. 联合(共用体)

3.1 联合类型的定义

联合是一种特殊的自定义类型

这种类型也包含了一系列成员,但是这些成员共用同一块空间(联合也叫共用体)

union Un
{
	int a;
	char b;
};

int main()
{
	union Un un;
	printf("%zd\n", sizeof(un));
    printf("%p\n", &un.a);
    printf("%p\n", &un.b);
	return 0;
}

从代码运行打印的信息可以看到两个变量地址是一样的。

3.2 联合的特点

联合的成员共用同一块空间,那么以一个联合的总体大小,至少也是最大成员的大小(最少要保证能够容纳最大的成员)。

3.3 联合大小的计算

  • 联合的大小最少是最大成员的大小
  • 联合最大成员不是最大对齐数的整数倍时,要对齐到最大整数倍
union Un
{
	int a;
	char b[5];
};

int main()
{
	union Un un;
	printf("%zd\n", sizeof(un));
	return 0;
}

  • 24
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值