C语言总结5:自定义类型(结构体,枚举,联合)

本文详细介绍了C语言中的结构体、枚举和联合体。结构体包括声明、自引用、内存对齐及其原理,枚举涉及默认赋值和特殊情况,联合体讲解了其特性及大小计算。内容涵盖了位段、内存对齐策略以及在实际应用中的考量,帮助读者深入理解C语言中的自定义类型。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.结构体
  • 结构体声明
    例如描述一个学生:
struct stu
{
	char name[20];
	short age;
	char sex[3];
};

特殊声明:

struct
{
	int a;
	int b;
	int c;
	char d[20];
}x;
struct
{
	int a;
	int b;
	int c;
	char d[20];
}a[10],*p;

注意:上面两个结构体省略了结构体标签,编译器会把上面两个声明当成两个完全不同的两个类型,因此p=&x是非法的

  • 结构体自引用
struct Node
{
   int data;//数据域
   struct Node* next;//指针域
};

注意:

typedef struct
{
	int data;
}Node;//这是结构体类型,不用开辟空间
struct
{
	int data;
}Node;//这是结构体变量,需要开辟空间
  • 结构体变量和初始化
struct point//类型声明
{
	int x;
	int y;
}p1;//声明类型的同时声明变量

struct point p2;//声明结构体变量p2

struct stu
{
	char name[20];//名字
	int age;//年龄
};
struct stu s = { "lisi", 20 };//初始化

struct Node
{
	int data;
	struct point p;
	struct Node* next;
}n1 = { 10, { 1, 2 }, NULL };//结构体嵌套初始化
  • 结构体内存对齐
    首先,我们要了解为什么要进行内存对齐?
    1、平台原因,并不是所有的硬件平台都能访问任意地址上的任意数据,在一些硬件平台上,只能在某些地址处取特定类型的数据,否则出现硬件异常
    2、性能原因:数据结构(尤其是栈)应当尽可能在自然边界上对齐,因为为了访问未对齐的内存,处理器需要进行两次内存访问,而对齐的内存访问仅需要一次。
    对齐原则
    (1)基本类型自身的对齐值
    (2)自定义类型的对齐值(自身最大值)
    (3)程序指定的对齐值(#pragma a pack(n))
    (4)有效对齐(自身和程序指定的较小的一个)
    例如:
typedef struct Test
{
	short a;//2+6
	struct
	{
		int b[100];//400
		double c;//8
		char d;//1+7
	};
	long e;//4+4
}Test;

结果为432(vs或者vc平台下)
但若是这样:

typedef struct Test
{
	short a;
	struct t
	{
		int b[100];
		double c;
		char d;
	};
	long e;
}Test;

在vc平台下就为8,因为在struct后面加上t,表示为类型,不开辟空间,所以没有大小,但两者在vs平台下都为432(编译平台原因)
__ 如何让结构体按照指定的对齐参数进行对齐__:

#pragma pack(n)

强制使其对齐模数为n
如何知道结构体中某个成员相对于结构体起始位置的偏移量?

size_t offsetof(structname,membername);

直接使用offsetof宏,如何模拟实现呢?

   #define my_offsetof(structName,memName)\
	(size_t)&(((structName*)0)->memName)//'\'为续行符

分析:((structName*)0)表示有一个指向结构体的指针,它的值为0,&(((structName*)0)->memName)表示取结构体中的成员,由于这个类的基址是0,结构体成员的地址就是其在结构体中的偏移量。

  • 结构体传参
    函数传参的时候,参数需要压栈,会有时间和空间上的系统开销,如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销比较大,所以会导致性能的下降,因此,结构体传参的时候,要传结构体指针。
  • 位段
    位段的成员可以是int unsigned int signed int char类型
    位段的大小计算
    例如:
struct A
{
	int a : 2;
	int b : 5;
	int c : 10;
	int d : 30;
};

分析:它的大小为8,’:'后面的为bit位,a需要2个bit位,int类型表示开辟4个字节,为32个bit位,还剩30个bit位,b需要5个bit位,剩25个bit位,c需要10个bit位,剩15个bit位,d需要30个bit位,不够,重新开辟一个int类型的空间,用掉30个bit位,剩2个,一共8个字节。
再例如:

#pragma pack(1)
struct tag
{
	unsigned char a : 1;
	unsigned char b : 2;
	unsigned char c : 6;
	unsigned char d : 4;
	unsigned char e;
	unsigned char f : 4;
	unsigned char g;
};

分析:它的大小为6,先开辟一个char类型的空间,为1个字节,即8个bit位,a需要1个bit位,剩7个bit位,b需要2个bit位,剩5个bit位,c需要6个bit位,不够,重新开辟一个字节,用掉6个bit位,剩2个bit位,d需要4个bit位,不够,再开辟一个字节,用掉4个bit位,剩4个bit位,e占用一个字节,再开辟一个字节,f需要4个bit位,剩4个bit位,g占用一个字节,一共开辟了6个字节,在以1对齐时,为6,以4对齐时,按照内存对齐第四条原则,还是6.
例如:

typedef struct Test
{
	char a : 1;
	int b : 2;
}Test;

分析:结果为8,存在对齐
结论:位域有两个限制条件:
1>不能跨字节存储
2>不能跨类型存储

2.枚举
  • 类型定义
    例如:
enum color
{
	red,
	green, 
	blue
};

成员变量没有赋值,默认从零开始依次递增1,若赋值也遵循递增1,例如:

enum ENUM_A
{
	x1,
	y1,
	z1=255,
	a1,
	b1
};
enum ENUM_A enumA = y1;
enum ENUM_A enumB = b1;
int main()
{
	printf("%d\n", enumA);
	printf("%d\n", enumB);
	return 0;
}

分析:x1为0,y1为1,z1赋值为255,则a1为256,b1为257,结果为1和257

3.联合体
  • 联合体声明
union Un
{
	char c;
	int i;
};
  • 联合体特点
    共用一块空间,一个联合变量的大小为最大变量的大小
union Un
{
	char c;
	int i;
};
union Un un;
	printf("%d\n", sizeof(un));

大小为4

  • 经典应用:当前计算机大小端存储
int check()
{
	union Un 
	{
		int i;
		char c;
	}un;
	un.i = 1;
	return un.c;
}
int main()
{
	int ret = check();
	if (ret == 1)
	{
		printf("小端\n");
	}
	else
		printf("大端\n");
	return 0;
}
  • 联合大小计算
    1> 联合大小至少为最大成员大小
    2> 当最大成员大小不是最大对齐数整数倍时,就要对齐到最大对齐数的整数倍
    例如:
union un1
{
	char arr[5];
	int i;
};
union un2
{
	short c[7];
	int i;
};
int main()
{
	printf("%d\n", sizeof(union un1));
	printf("%d\n", sizeof(union un2));
	return 0;
}

分析:第一个大小为8,arr占5个字节,int占4个字节,对齐到最大数的整数倍,为8,第二个大小为16,分析同上。

  • 重要的例子
unsigned short *arr[10][10];
typedef union un
{
	unsigned long a;
	unsigned short b[7];
	unsigned char c;
}un1;
un1 st, *pst;
int main()
{
	printf("%d\n", sizeof(arr));//400
	printf("%d\n", sizeof(st));//16
	printf("%d\n", sizeof(pst));//4
	printf("%d\n", sizeof(*pst));//16
	system("pause");
	return 0;
}

分析:arr为指针数组,大小为整个数组大小为400,st为联合体变量,联合大小为16,pst为联合体指针,指针大小为4,*pst为联合体指针解引用,为联合大小16。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值