【C】自定义数据类型

本文详细介绍了C语言中的结构体、位段、联合和枚举的概念与使用方法。结构体用于组合多种类型的数据,位段在有限空间内高效利用内存,联合则允许成员共享同一块内存。枚举提供了类型安全的常量定义方式,相比#define有更多优势。文章还探讨了这些概念在内存对齐、跨平台性、空间效率等方面的特点和注意事项。
摘要由CSDN通过智能技术生成

目录

一、结构体

        结构体声明

        特殊声明

        结构体创建变量

        结构体自引用

        结构体重定义

        结构体变量初始化

        结构体内存对齐

        offsetof

        修改最大对齐数

        结构体传参

二、位段

        位段的内存分配

        位段的跨平台问题

        位段的应用

三、联合

        联合内成员的空间占用

        联合的大小计算

四、枚举

        枚举内成员值

        枚举的使用

        枚举相对于 define 的优点


一、结构体

结构体:由变量组成的数据类型。

        结构体声明

//创建「人」的数据类型
struct People //「struct People」为数据类型名,其中「struct」固定,「People」自定义
{
	char name[10];//姓名
	int age;//年龄
};

        特殊声明(不加结构体标签)

struct
{
	char name[10];
	int age;
};

当用特殊声明创建多个结构体时,编译器将全部视为不同类型。

例:用特殊声明创建 2 个一样的结构体,创建变量后进行赋值。

        结构体创建变量

  (1)主函数内创建变量(局部变量)

struct People p1, p2;
struct People* p1, *p2;//指针变量

(2)声明结构体时创建变量(全局变量)

struct People
{
	char name[10];
	int age;
}p1,p2;//创建「p1」「p2」两个「struct People」类型的变量
struct People
{
	char name[10];
	int age;
}*p1,*p2;//创建「p1」「p2」两个「struct People」类型的指针变量

        结构体自引用

错误:

struct People
{
	char name[10];
	int age;
	struct People p;
};

当 People 中创建了 p 时,p 里面还会包含一个 p,不断循环下去,导致计算不出占用内存大小,所以编译器报错。

正确:

struct People
{
	char name[10];
	int age;
	struct People *p;
};

当用指针的方式进行自引用,只需提供一个固定的指针类型大小就可以了。

特殊声明不可进行自引用

 

 将 struct 进行重定义之后,编译器还是会将创建出来的不同变量视为不同类型。

        结构体重定义

typedef struct People
{
    char name[10];
    int age;
}P;
//将「struct People」重定义为「P」

        结构体变量初始化

 (1)一般初始化

struct People
{
	char name[10];
	int age;
};

struct People p = { "张三",18 };

struct People
{
	char name[10];
	int age;
}p = { "张三",18 };

(2)嵌套初始化

struct A
{
	int b;
};

struct People
{
	char name[10];
	int age;
	struct A a;
};

struct People p = { "张三",18 ,{0} };
//因为p中的a是结构体,所以也需要使用{}

        结构体内存对齐

 用空间换取时间

作用:

(1)提高可移植性,某些平台只能在特定地址取出特定数据类型。

(2)提高性能:有时访问未对齐内存可能要比对齐内存多访问一次,因为内存对齐后,系统就知道从哪开始访问获取该数据。

 内存对齐规则:

(1)所有成员须对其到对齐数的整数倍偏移量处

(2)结构体总大小为成员最大对齐数的整数倍

(3)数组或嵌套结构体的偏移量为自身内变量的最大偏移量

对齐数:该成员占用字节数,VS 下最大不超过8

偏移量:内存单位与起始位置的距离

集中占用空间小的成员可以节约空间

例 1:

struct X
{
	char c1;
	int i;
	char c2;
}x;

struct Y
{
	char c1;
	char c2;
	int i;
}y;

int main()
{
	sizeof(x);
	sizeof(y);

	return 0;
}

答案:12、8

  例 2:

struct X
{
	char c1;
	int i;
	char c2;
};

struct Y
{
	char c1;
	char c2;
	struct X x;
    char c3;
}y;

int main()
{
	sizeof(y);

	return 0;
}

答案:20

        offsetof

 使用 offsetof 可查看结构体类型中变量所在偏移处,需包含头文件 < stddef.h>

例:

struct X
{
	char c1;
	int i;
	char c2;
}x;

struct Y
{
	char c1;
	char c2;
	struct X x;
	char c3;
}y;

int main()
{
	printf("%d\n", sizeof(y));
	printf("%d\n", offsetof(struct Y, c1));
	printf("%d\n", offsetof(struct Y, c2));
	printf("%d\n", offsetof(struct Y, x));
	printf("%d\n", offsetof(struct Y, c3));

	return 0;
}

        修改最大对齐数

 例:

#pragma pack(1)//将最大对齐数改为1
struct X
{
	char c1;
	int i;
	char c2;
}x;
#pragma pack()//将最大对齐数改回默认值

        结构体传参

(1)值传递

struct A
{
	int i;
};

void print(struct A a)
{
	printf("%d", a.i);
}

int main()
{
	struct A a = { 1 };
	print(a);

	return 0;
}

(2)地址传递

struct A
{
	int i;
};

void print(struct A* a)
{
	printf("%d", a->i);
}

int main()
{
    struct A a = { 1 };
	print(&a);

	return 0;
}

函数传参时需要进行压栈,如果参数过大会导致不必要的时间、空间消耗,所以结构体传参时,地址传递优于值传递。

二、位段

声明结构体时,在其中的成员后加上为该成员分配的内存(以位为单位),该结构体成员称为位段。

 例:

struct S
{
	int a : 3;//给a分配3个比特位
	int b : 4;//给b分配4个比特位
	int c : 5;//给c分配5个比特位
	int d : 4;//给d分配4个比特位
};

        位段的内存分配

 (1)位段只能是整型

(2)位段以不同整型大小按需开辟空间

例:

struct S
{
	int a : 3;
	int b : 4;
	int c : 5;
	int d : 4;
};

int main()
{
	struct S s = { 0 };
	s.a = 10;
	s.b = 12;
	s.c = 3;
	s.d = 4;

	return 0;
}

        位段的跨平台问题

位段不可跨平台

(1)int 类型被当做有符号或无符号不确定

(2)位数不确定(16 为机器下 int 为 2 字节)

(3)内存从左向右分配或相反,尚未确定

(4)开辟新空间时,旧空间利用或舍弃未确定

        位段的应用

位段应用在数据报文中,因为报文中每个字段所需空间极小。

三、联合

所有成员共用同一片空间的数据类型叫联合。

联合的声明、变量创建同结构体一样,只需将结构体中的「struct」替换为「union」

        联合内成员的空间占用

 例:

union U
{
	int i;
	char c;
}u;


int main()
{
	printf("%d\n", &(u.i));
	printf("%d\n", &(u.c));//(1)
	
	u.i = 0x11223344;
	u.c = 0x55;
	printf("%x\n", u.i);//(2)(小端环境下)

	return 0;
}

结果:

(1)2 个地址相同

(2)44332200

        联合的大小计算

联合的大小为最大成员大小且为成员内最大对齐数的倍数

例 1:

union U
{
	char c[5];
	int i;
}u;
int main()
{
	printf("%d\n", sizeof(u));
	return 0;
}

答案:8

最大成员大小为 5(char [5]),最大对齐数为 4(int)

例 2:

union U
{
	short c[7];
	int i;
}u;
int main()
{
	printf("%d\n", sizeof(u));
	return 0;
}

答案:16

最大成员大小为 5(short [7]),最大对齐数为 4(int)

四、枚举

 枚举的作用类似 define,但是枚举将定义的常量有类型且以类型分组。

例:

enum Colour//颜色
{
	BLUE,
	GREEN,
	RED
};

enum Sex//性别
{
	MAN,
	WOMAN
};

        枚举内成员值

枚举内成员的值默认从 0 开始递增,可自定义。

例 1:

enum Colour
{
	BLUE,
	GREEN,
	RED
};

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

结果:0、1、2

例 2:

enum Colour
{
	BLUE=1,
	GREEN,
	RED=5
};

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

结果:1、2、5

        枚举的使用

例:

enum Colour
{
	BLUE=,
	GREEN,
	RED=
};

int main()
{
	enum Colour c;
	c = GREEN;
	return 0;
}

错误用法:

enum Colour
{
	BLUE,
	GREEN,
	RED
};

int main()
{
	enum Colour c;
	c = 1;
	return 0;
}

因为 1 是整型,而 c 是 enum Colour 类型,不同类型不能进行赋值。

        枚举相对于 define 的优点

(1)可一次性定义多个常量

(2)有类型,更严谨

(3)便于调试,程序执行前的预编译会进行宏替换,从而不能观察到 define 定义的值

(4)增加代码可读性

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值