有关自定义类型:结构体、枚举、联合

一、结构体

1.概念

结构体:是一种聚合类型,可以将不同类型的变量放在一起。

数组:也是一种聚合类型,但是将相同类型的变量放在一起。

2.结构体声明

struct tag
{
	member-list;
}variable-list;


tag:是一个标签,名称.  : (1) 见名知意 (2) 可以省略,但不建议省略。

member-list:  (1) 可以放任意类型的变量,可以套结构体。 (2) 不可以为空(c语言)

variable-list:  (1) 变量列表,可定义多个,用逗号隔开  (2)可以省略,建议省略。

每个结构体都是一种类型。和内部元素类型是否一样没关系。

3.结构体成员

可以是标量、数组、指针、和其他结构体

4.结构体成员的访问

(1)结构体的地址和首元素的地址在数值上是一样的  

(2) 地址是递增的

  (3)通过两个操作进行访问:(1).  操作    (2)有时候我们得到的不是一个结构体变量,而是一个结构体指针,struct print (struct *p) 此时通过:(*p).name 或 p->name

5.结构体自引用

引用的是自身类型的指针,此时明确类型。

typedef:结构体类型重定义。(先定义后使用)

6.结构体的不完整声明

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

初始化:定义变量的同时赋初值,这点和数组是一样。

struct stu p1-->定义变量

struct stu p2={"abcd",12};   -->初始化

8.结构体的内存对齐

内存对齐的原因:

(1)  平台原因(移植原因):不是所有的引荐平台都可以访问任意地址的任意数据

(2)  性能原因:处理器需作出两次访问

对齐规则:

(1) 第一个成员在与结构体变量偏移量为0的地址处。所以第一个元素无需对齐,默认就为对齐。

(2)其他成员变量要对齐到某个数字(对齐数)的整数倍地址处。  偏移量要能整除自己的对齐数。 对齐数:编译器默认的对齐数与该成员对齐数的最小值。  vs=8,linux=4

(3)结构体大小为最大对齐数的整数倍。

(4)如果嵌套了结构体,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体大小就是所有的最大对齐数的整数倍

总体说:结构体的你内存对齐实际就是用空间来换时间

我们要尽量将占用空间小的成员集中的一起。

struct s1
{
	char c1;  // 1
	int i;    //4->1+3+4
	char c2;  //1->8+1
};
int main(){

	printf("%d\n", sizeof(struct s1));
		system("pause");
		return 0;
}   // 结果为12

9.结构体传参

不会发生降维,不要传结构体变量,要传就传结构体指针。

以免造成系统开销过大。

二、位段

位段:节省空间 ,用时间换空间 内存填充(第一个变量内存空间能放则放,放不下在开辟)
与结构体的两个不同:
1.位段的成员必须全为是int、unsigned int、signed int 或char
按照需要以1字节或者4字节开辟
2.位段成员后面必须有冒号
struct A
{
	int _a : 2;  2个bit位
	int _b : 5;  5个bit位
	int _c : 10;  ...
	int _d : 30;  ...
};

3.移植性差。

int被当做有符号无符号不确定。

最大位数目不确定

位段中是从左到右分配还是从右向左分配不确定。

当一个结构包含两个位段,第二个成员较大无法容纳第一个位段的剩余位,是舍弃剩余位还是利用剩余位不确定。

4.应用:网络协议、报头设计(可以理解为相当于快递盒子)。

总结:和结构体比较,位段能够达到相同的效果,而且可以很好的节省空间,但是跨平台问题存在。

三、枚举

概念:把可能取得值一一列举

enum day
{
	Mon,
	Tues,
	Wed,
	Thur,
};

这里定义的enum Day就为枚举类型

{}里面的可能取值叫枚举常量,这些值都是默认从0开始一次递增1,当然在定义的时候也可以赋初值。

优点:

1.增加代码可读性和可维护性。

2.和define相比更加严谨,因为预处理会进行语法处理。

3.使用方便,可一次定义多大常量。

注意!

只能拿枚举常量给枚举变量赋值。且只可以赋此枚举类型里面的枚举常量。

四、联合(共用体)

特征:所有成员公用同一块空间,这样联合体的大小至少是最大成员的大小。

union un
{
	char c;
	int i;
};

我们可以利用联合体的特征来判断当前计算机的大小端存储

union node
{
	char c;
	int num;
};
int main(){
	union node p;
	p.num = 0x12345678;
	if (p.c == 0x78){
		printf("little");
	}
	else{
		printf("big");
	}
	system("pause");
	return 0;
}

在这里我提供一种判断计算机大小的方法:

int check_eye()
{
	int num = 1;
    char *p= (char *)&num  // 只看第一个字节(最低地址)所以强转
	if (*p == 1)
		return 0;
	else
		return 1;
}
int main()
{
	int ret = check_eye();
	if (ret == 0)
		printf("little");
	else
		printf("big");
	system("pause");
	return 0;
}

联合体大小的计算

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

union un
{
	char c[5];  //1->5
	int i;    //4->4
};
int main(){

	printf("%d\n", sizeof(union un));
		system("pause");
		return 0;
}  //结果为8

使用:

可以将整形类型的IP地址转为点分十进制表示形式。

union ip_addr
{
	unsigned long addr;
	struct{
		unsigned char c1;
		unsigned char c2;
		unsigned char c3;
		unsigned char c4;
	}ip;
};
int main(){
	union ip_addr my_ip;
	my_ip.addr = 176238749;

	printf("%d.%d.%d.%d\n", my_ip.ip.c4, my_ip.ip.c3, my_ip.ip.c2, my_ip.ip.c1 );
		system("pause");
		return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值