C语言 | 结构体类型(声明、初始化、成员访问、内存对齐、传参)

本文介绍了C语言中的结构体类型,包括结构体的声明、初始化、成员访问、内存对齐原理及计算方法,以及结构体作为参数在函数调用中的传递方式。讲解了内存对齐对于性能的影响,并提供了结构体内存对齐的规则,同时提醒在实际编程中,为提高性能,通常传递结构体时使用指针。

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


一、结构体类型的引入

  C语言中的内置类型不能表示所有的场景,有时候我们需要其中的几种一起来修饰某个变量,例如一个学生的信息就需要学号(字符串),姓名(字符串),年龄(整形)等等,这些数据类型都不同但是他们又是表示一个整体,要存在联系,那么我们就需要一个新的数据类型。
  结构体类型:结构体是一些值的集合,这些值称为成员变量。结构体的每个成员可以是不同类型的变量。简单来说,结构体是具有相同或不同元素类型的集合。

二、结构体的声明

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

例如描述一个学生:

struct Stu
{
	char name[20]; //名字
	int age;//年龄
	char sex[5]; //性别
	char id[20]; //学号
};
typedef struct stu
{
	char name[20]; //名字
	int age;//年龄
	char sex[5]; //性别
	char id[20]; //学号
}Stu;

结构体的成员可以是标量、数组、指针,甚至是其他结构体。

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

struct Point{
	int x;
	int y;
}p1;//声明类型的同时定义变量p1
struct Point p2;//定义结构体变量p2
struct Point p3 = { 10, 20 };//初始化,定义变量的同时赋初值

struct Stu//类型声明
{
	char name[15];//名字
	int age;//年龄
};
struct Stu s = { "Zhangsan", 20 };//初始化

struct Node
{
	int data;
	struct Point p;
	struct Node* next;
}n1 = { 10, {4,5},NULL};//结构体嵌套初始化
struct Node n2 = { 20, { 5, 6 }, NULL };//结构体嵌套初始化

四、结构体成员的访问

  • 结构体变量访问成员:结构体变量的成员是通过点(.)操作符访问的。
struct Stu
{
	char name[20];
	int age;
};
struct Stu s;
strcpy(s.name, "Zhangsan");//使用.访问name成员
s.age = 20;//使用.访问age成员
  • 结构体指针访问指向变量的成员:有时候我们得到的不是一个结构体变量,而是指向一个结构体题的指针。
struct Stu
{
   char name[20];
   int age;
};
void print(struct Stu* ps){
   printf("name = %s  age = %d\n", (*ps).name, (*ps).age);
   //使用结构体指针访问指向变量的成员
   printf("name = %s  age = %d\n", ps->name, ps->age);
}
int main()
{
   struct Stu s = {"Zhangsan",20};
   print(&s);
   system("pause");
   return 0;
}

五、结构体内存对齐

1.内存对齐是什么?
内存对齐本质是以牺牲空间的方式来换取时间,提升效率的策略。
2.为什么存在内存对齐?
  硬件寻址的限制可能会导致CPU寻址效率降低

  • 平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的,某些硬件平台只能在某些地址处取某些特定的数据,否则抛出硬件异常

  • 性能原因:数据结构(尤其是栈)应该尽可能的在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要做两次内存访问,而对齐的内存仅需要一次访问。

3.如何计算内存对齐?
对齐:起始偏移量能整除对齐数即为对齐;
结构体的对齐规则:
1> 第一个成员在与结构体变量偏移量为0的地址处;
2> 其他成员变量要对齐到对齐数的整数倍的地址处;
对齐数=编译器默认的一个对齐数与该成员大小的较小值(VS中的默认值为8)—— 默认自身对齐;
3> 结构体的总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍;
4> 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍;
5> 数组的对齐数就是它的基本类型大小,和数组的元素个数无关。

练习:

struct S
{
	double d;//8
	char c;//1
	int i;//3+4
};
struct S1
{
	char c1;//1
	struct S s;//7+16
	double d;//8
};
struct S2
{
	char x;//1
	char *cp;//3+4
	char a[3];//3
	double *b[5];//1+20
	struct S1 *s;//4   ---->36
	struct S1 d[3];//4+32*3  ---->136
	char e;//1   ---->137
};//137->8:144
int main()
{
	printf("%d\n", sizeof(struct S));//16
	printf("%d\n", sizeof(struct S1));//32
	printf("%d\n", sizeof(struct S2));//144
	system("pause");
	return 0;
}
  • 修改默认对齐数
#pragma pack(8)//设置默认对齐数为8
#pragma pack()//取消设置的默认对齐数,还原为默认
#pragma pack(1)//设置默认对齐数为1
#pragma pack()//取消设置的默认对齐数,还原为默认

六、结构体传参

结构体传参的时候,要传结构体的地址
原因:函数传参的时候,参数是需要压栈的,会有时间和空间上的系统开销。如果传递一个结构体对象的时候,结构体过大参数压栈的系统开销比较大,所以会导致性能的下降。

struct S
{
	int data[1000];
	int num;
};
struct S s = { { 1, 2, 3, 4 }, 1000 };
//结构体传参
void print1(struct S s){
	printf("%d\n", s.num);
}
//结构体地址传参
void print2(struct S* ps){
	printf("%d\n", ps->num);
}
int main()
{
	print1(s);//传结构体
	print2(&s);//传地址
	system("pause");
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值