自定义结构体类型:结构体,枚举,联合

1 篇文章 0 订阅
1 篇文章 0 订阅
本文详细介绍了C语言中的结构体,包括声明、初始化、内存对齐以及自引用的正确做法。同时,讨论了位段的声明规则及其内存占用特性,枚举的使用方式,以及联合体如何在内存中存储多个不同类型的数据。此外,还提到了结构体在函数传参中的注意事项,以及为何首选地址传参的原因。
摘要由CSDN通过智能技术生成

结构体

结构体的声明

结构体是一些值的集合,这些值称为成员变量。结构体的每个成员可以是不同类型的变量。
声明方式如下:

struct tag
{
	member-list;   //成员变量
}variable-list;     //变量列表,即可定义多个变量。

//举个例子
struct stu
{
	char name[20];
	int age;
	char sex;
	char id[10];
}stu1,*p;         //定义了两个不同类型的变量

尽量不要匿名声明,即使不报错(因为c是弱类型语言),但不能复制,因此不建议这样做。

结构体的自引用

错误示例

struct Node          
//要求sizeof()大小,就要先求出sizeof(next),而next是struct Node此类型的,因此不能求出
{
	int data;
	struct Node next;         //next变量是未定义的,大小也未知
}

正确示例

struct Node
{
	int data;
	struct Node*next;   //此时定义指针指向此类型,sizeof(next)大小是4字节(32位平台下)
}

结构体的定义和初始化

//结构体类型声明
struct stu
{
	char name[15];
	int age;
}
//声明的同时定义结构体变量
struct Point
{
	int x;
	int y;
}p1;
//定义结构体变量
struct Point p2;
//定义的同时初始化结构体变量,整体初始化只能在定义时进行
struct Point p3={2,3};
p3->x=4;
//结构体嵌套初始化
struct Node
{
	int data;
	struct Point p;
	struct Node* next;
}n1={10,{4,5},NULL};
struct Node n2={20,{5,6},NULL};

结构体内存对齐

内存对齐主要应用在求结构体的大小

  • 对其规则如下:
  1. 第一个成员在与结构体变量偏移量为0的地址处。

  2. 其他成员变量要对齐到对齐数的整数倍地址处。
    对齐数=编译器默认的对齐数与该成员大小的较小值(vs默认8)。

  3. 结构体总大小为成员变量的最大对齐数的整数倍。

  4. 如果嵌套了结构体,同理,子结构体与变量情况相同。

  • 为什么存在内存对齐
  1. 平台原因(移植原因):不是所有硬件平台都能访问任意地址上的任意数据的,某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
  2. 性能原因:数据结构尤其是栈,应该尽可能地自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要两次访问内存,而对齐的内存仅需访问一次。

总体来说,结构体的内存对齐是拿空间来换取时间的做法。那么在设计结构体的时候,就既要满足对齐,又要节省空间,尽量让占用空间小的成员集中在一起。

//两个结构体成员变量相同,但排列顺序不同,造成结构体大小不同。
struct s1
{
	char c1;  //默认8,本身1,取最小为1 ,偏移量0
	int i;    //默认8,本身4,取最小为4,偏移量4-7(满足4的整数倍)
	char c2;  //默认8,本身1, 取最小为1,偏移8
/*成员最大对齐数为4,整体大小为4的倍数,0-8大小为9,
最接近4的倍数为12,因此该结构体大小为12字节。*/
}
struct s2
{
	char c1;  //默认8,本身1,取最小为1 ,偏移量0
	char c2;  //默认8,本身1, 取最小为1,偏移量1
	int i;    //默认8,本身4,取最小为4,偏移量4-7(满足4的整数倍)	 
/*成员最大对齐数为4,整体大小为4的倍数,0-7大小为8,
刚好是4的倍数为,因此该结构体大小为8字节。*/
}

结构体传参

struct S
{
	int data[1000];
	int num;
};
struct S s1={{1,2,3,4},1000};
//结构体传参
void printf(struct S s)
{
	printf("%d",s.num);
}
//结构体地址传参
void printf2(struct S* s)
{
	printf("%d",s->sum);
}
int main()
{
	printf1(s1);    //传结构体
	printf2(&s1);    //传地址
	return 0;
}

首选地址传参,因为函数传参时,参数需要压栈,会有时间上和空间上的系统开销。如果传递一个结构体对象,结构体过大,会导致系统性能下降。

位段

位段的声明和结构体是类似的,有两个不同:

  1. 位段的成员必须是int、unsigned int、signed int、char
  2. 位段的成员名后边有一个冒号和一个数字。
struct S
{
	int a:2;      //开辟4个字节,占一个字节的其中两位
	int b:5;      //在a的后边占5位,仍然只占了1个字节
	int c:10;    //在b的后边占第二个字节的8位,再占第三个字节的2位
	int d:30;   ///再开辟4个字节,共32位,够放下30位
	
};    //因此sizeof(struct S)为8,S就是一个位段类型    

枚举

enum Day
{
	Mon;
	Tues;
	Weds;
	Thur;
	Fri;
	Sat;
	Sun;
};
enum Sex
{
	MALE;
	FAMALE;
};
enum Color
{
	RED;
	GREEN;
	BLACK;
};

以上定义的enum Day,enum Sex,enum Color都是枚举类型,{}中的内容是枚举类型的可能取值,叫枚举常量,这些的取值从0开始,依次递增1,当然也可以在定义的时候赋初值。

联合体

联合体也是一种特殊的自定义类型,这种类型定义的变量也包含一系列的成员,特征就是这些成员共用一块内存空间。这样一个联合体的大小,至少是最大成员的大小。

union Un     //联合体的声明
{
	int i;
	char c;
};             //sizeof(union Un)为4
union Un un;    //联合体的定义
printf("%d",&(un.i));     
printf("%d",&(un.c));     //输出内容相同
un.i=0x11223344;
un.c=0x55;
printf("%d",un.i);     //输出0x11223355,体现了计算机的小端存储模式,这个也可以应用在判断大小端存储上

联合体大小的计算和结构体类似,只是共用内存

union Un1
{
	char c[5];     //0-4,大小为5,对齐数1
	int i;         //0-3,对齐数4
};               //整体大小为4的倍数,因此大小为8
union Un2
{
	short c[7];     //0-13,大小为14,对齐数2
	int i;         //0-3,对齐数4
};                 //整体大小为4的倍数,因此大小为16
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值