前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C语言自定义类型之结构体

C语言自定义类型之结构体

作者头像
用户11289931
发布2024-09-24 16:29:45
1040
发布2024-09-24 16:29:45
举报
文章被收录于专栏:学习

结构体的类型

代码语言:javascript
复制
struct tag//结构体名
{
	member - list;//成员列表
}variable - list;//变量列表

举个例子

代码语言:javascript
复制
struct Stu
{
	char name[20];//名字 
	int age;//年龄 
	char sex[5];//性别 
	char id[20];//学号 
}; //分号不能丢 

结构体变量的创建与初始化

代码语言:javascript
复制
#include <stdio.h>
struct Stu
{
	char name[20];//名字 
	int age;//年龄 
	char sex[5];//性别 
	char id[20];//学号 
};
int main()
{
	//按照结构体成员的顺序初始化 
	struct Stu s = { "张三", 20, "男", "20230818001" };
	printf("name: %s\n", s.name);
	printf("age : %d\n", s.age);
	printf("sex : %s\n", s.sex);
	printf("id : %s\n", s.id);

	//按照指定的顺序初始化 
	struct Stu s2 = { .age = 18, .name = "lisi", .id = "20230818002", .sex =
   "⼥" };
	printf("name: %s\n", s2.name);
	printf("age : %d\n", s2.age);
	printf("sex : %s\n", s2.sex);
	printf("id : %s\n", s2.id);
	return 0;
}

特别的,打印结构体一般有两种方式,分别对应‘.’与‘->’操作符。下面我们来举个例子

代码语言:javascript
复制
struct Stu
{
	char name[20];
	int age;
};

int main()
{
	struct Stu s = { "zhangsan",20 };
	printf("%s %d\n", s.name, s.age);//使用变量指向

	struct Stu* ps = &s;
	printf("%s %d\n", ps->name, ps->age);//使用指针指向
	return 0;
}

可以看到它们打印的结果是一致的:

095236956bf942fcbe33502c1abe9647.png
095236956bf942fcbe33502c1abe9647.png

结构的特殊声明

在声明结构时,可以不完全声明

比如:

代码语言:javascript
复制
//匿名结构体类型 
struct
{
	int a;
	char b;
	float c;
}x;
struct
{
	int a;
	char b;
	float c;
}a[20], * p;

上面两个结构体在声明是省略了结构体标签(tag)

那么问题来了

代码语言:javascript
复制
//在上⾯代码的基础上,下⾯的代码合法吗? 
p = &x;

答案是否定的,编译器会把上⾯的两个声明当成完全不同的两个类型,所以是⾮法的。

匿名的结构体类型,如果没有对结构体类型重命名的话,基本上只能使⽤⼀次。

结构的自引用

在结构中包含一个类型为该结构本身的成员应注意什么呢?

例如,定义一个链表的节点时,应确保链表内使用指针变量来指向下一目标地址的指针

代码语言:javascript
复制
struct Node
{
	int data;//-- 数据域
	struct Node *next;//--指针域
};

typedef 对匿名结构体类型重命名,也应注意定义的先后顺序,避免混淆

代码语言:javascript
复制
typedef struct
{
	int data;
	Node* next;//因为Node是对前⾯的匿名结构体类型的重命名产⽣的,但是在匿名结构体内部提前使
               //⽤Node类型来创建成员变量,这是不⾏的。
}Node;
代码语言:javascript
复制
typedef struct Node
{
 int data;
 struct Node* next;
}Node;

结构中的内存对齐

首先我们来看两组代码,思考一下它们的结果为何不同?

baec8e699dd84a49813131632b130e5c.png
baec8e699dd84a49813131632b130e5c.png
757a84c680aa4514b9430de12a6f4fdb.png
757a84c680aa4514b9430de12a6f4fdb.png

这就是结构内存对齐导致的差异,下面我们先来了解一下它的基本概念:

结构体内存的对齐规则

1.结构体的第⼀个成员对齐到和结构体变量起始位置偏移量为0的地址处

 2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。

对齐数=编译器默认的⼀个对齐数与该成员变量大小的较小值 

  -VS 中默认的值为 8

  -Linux中gcc没有默认对齐数,对齐数就是成员自身的大小。

3.结构体总大小为最大对齐数(结构体中每个成员变量都有⼀个对齐数,所有对齐数中最大的)的 整数倍。

4.如果嵌套了结构体的情况,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处,结构 体的整体大小就是所有最大对齐数(含嵌套结构体中成员的对齐数)的整数倍。

先来补充一下offsetof这个的知识:

它是用来计算结构体成员相较于结构体变量起始位置的偏移量。

偏移量

我们用一张图来理解

a15f2a0e7113442ca2208fc44f332362.png
a15f2a0e7113442ca2208fc44f332362.png

这样便能解释为什么上面的代码存在不同了

3d586e3008e44f53b59fc680f245bd6a.png
3d586e3008e44f53b59fc680f245bd6a.png
26be4dc8d2644876b54c456bfd7b8d9c.png
26be4dc8d2644876b54c456bfd7b8d9c.png

同样的,对于内置的结构体,例如

代码语言:javascript
复制
struct S3
{
	double d;
	char c;
	int i;
};
struct S4
{
	char c1;
	struct S3 s3;
	double d;
};

int main()
{
	struct S4 s4;
	/*struct S s2;*/
	printf("%zd\n", sizeof(s4));
	return 0;
}

代码的结果为

709411988f324befa4c23b5006845300.png
709411988f324befa4c23b5006845300.png

根据内存对齐,我们很容易得到s3的大小为16,如下图

169fa99db82d4c7e820d0930cd91ae7a.png
169fa99db82d4c7e820d0930cd91ae7a.png

故能得到struct S4 的大小

7c7a28a9bdbf4cf79e93e071275bd2cb.png
7c7a28a9bdbf4cf79e93e071275bd2cb.png

以上便是结构体的相关内容,欢迎大家在评论取与我交流,点出我的不足,我们共同进步!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-09-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 结构体的类型
  • 结构体变量的创建与初始化
    • 结构的特殊声明
      • 结构的自引用
      • 结构中的内存对齐
        • 结构体内存的对齐规则
          • 偏移量
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档