首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >结构体学不明白,小代老师带你深入理解结构体

结构体学不明白,小代老师带你深入理解结构体

作者头像
用户11319080
发布2024-10-17 18:58:24
发布2024-10-17 18:58:24
3010
举报
文章被收录于专栏:学习学习

1.结构体的概念

C语⾔已经提供了内置类型,如:char、short、int、long、float、double等,但是只有这些内置类 型还是不够的,假设我想描述学⽣,描述⼀本书,这时单⼀的内置类型是不⾏的。 描述⼀个学⽣需要名字、年龄、学号、⾝⾼、体重等; 描述⼀本书需要作者、出版社、定价等。C语⾔为了解决这个问题,增加了结构体这种⾃定义的数据类 型,让程序员可以⾃⼰创造适合的类型。

📌 结构是⼀些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量,如: 标量、数组、指针,甚⾄是其他结构体。 对比下: 数组:是1个或着多个相同类型元素的集合 结构体:是1个或者多个相同或者不同类型的变量集合。

1.1结构体的声明

代码语言:javascript
复制
struct tag //自定义
{
      member-list;//成员列表
}variable-list;//变量列表       

举个例子写一个学生类型:

代码语言:javascript
复制
struct stu
{
	char name;
	int age;
	float score;
}

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

代码语言:javascript
复制
struct stu
{
	char name;
	int age;
	float score;
}s1, s2;//	全局变量(定义)
int main()
{
	struct stu s4 = { "wangsan",12,88.0 };//局部变量  顺序初始化
	struct stu s5 = { .age = 12,.name="li",.score=88.8 };//指定顺序初始化
	return 0;
}

结构体嵌套

代码语言:javascript
复制
struct stu
{
	int name;
	struct point n1;
	
}n1 = { 1,{1,2} };

1.3结构体访问操作符

1.3.1结构体成员直接访问

结构体成员的直接访问 结构体成员的直接访问是通过点操作符(.)访问的。点操作符接受两个操作数。如下所⽰:

代码语言:javascript
复制
#include <stdio.h>
struct Point
{
 int x;
 int y;
}p = {1,2};
int main()
{
 printf("x: %d y: %d\n", p.x, p.y);
 return 0;

使⽤⽅式:结构体变量.成员名

1.3.2结构体间接访问操作符

9.2.2 结构体成员的间接访问 有时候我们得到的不是⼀个结构体变量,⽽是得到了⼀个指向结构体的指针。如下所⽰:

代码语言:javascript
复制
#include <stdio.h>
struct Point
{
 int x;
 int y;
};
int main()
{
 struct Point p = {3, 4};
 struct Point *ptr = &p;
 ptr->x = 10;
 ptr->y = 20;
 printf("x = %d y = %d\n", ptr->x, ptr->y);
 return 0;
}

使⽤⽅式:结构体指针->成员名 比喻下:

2.结构体的特殊声明

在声明结构的时候,可以不完全的声明。 ⽐如:

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

上⾯的两个结构在声明的时候省略掉了结构体标签(tag)。 那么问题来了? //在上⾯代码的基础上,下⾯的代码合法吗?

代码语言:javascript
复制
p = &x;

警告: 编译器会把上⾯的两个声明当成完全不同的两个类型,所以是⾮法的。 匿名的结构体类型,如果没有对结构体类型重命名的话,基本上只能使⽤⼀次。

3.结构体的自引用

1.3 结构的⾃引⽤ 结构体自引用和链表很像

在结构中包含⼀个类型为该结构本⾝的成员是否可以呢? ⽐如,定义⼀个链表的节点:

代码语言:javascript
复制
struct Node
{
 int data;
 struct Node next;
};

上述代码正确吗?如果正确,那 sizeof(struct Node) 是多少? 仔细分析,其实是不⾏的,因为⼀个结构体中再包含⼀个同类型的结构体变量,这样结构体变量的⼤ ⼩就会⽆穷的⼤,是不合理的。 正确的⾃引⽤⽅式:

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

在结构体⾃引⽤使⽤的过程中,夹杂了 typedef 对匿名结构体类型重命名,也容易引⼊问题,看看 下⾯的代码,可⾏吗?

代码语言:javascript
复制
typedef struct
{
 int data;
 Node* next;//
}Node;

答案是不⾏的,因为Node是对前⾯的匿名结构体类型的重命名产⽣的,但是在匿名结构体内部提前使 ⽤Node类型来创建成员变量,这是不⾏的。 解决⽅案如下:定义结构体不要使⽤匿名结构体了 typedef struct Node { int data; struct Node* next; }Node;

4结构体对齐规则

我们已经掌握了结构体的基本使⽤了。 现在我们深⼊讨论⼀个问题:计算结构体的⼤⼩。 这也是⼀个特别热⻔的考点: 结构体内存对⻬

4.1 对⻬规则

⾸先得掌握结构体的对⻬规则:

  1. 结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处
  2. 其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。 3.对⻬数 = 编译器默认的⼀个对⻬数 与 该成员变量⼤⼩的较⼩值。
  • VS 中默认的值为 8
  • Linux中 gcc 没有默认对⻬数,对⻬数就是成员⾃⾝的⼤⼩
  1. 结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的 整数倍。
  2. 如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构 体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。 这里先介绍一个操作符:offsetof

4.2 为什么存在内存对⻬?

⼤部分的参考资料都是这样说的:

  1. 平台原因 (移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定 类型的数据,否则抛出硬件异常。
  2. 性能原因: 数据结构(尤其是栈)应该尽可能地在⾃然边界上对⻬。原因在于,为了访问未对⻬的内存,处理器需要 作两次内存访问;⽽对⻬的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节,则地 址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对⻬成8的倍数,那么就可以 ⽤⼀个内存操作来读或者写值了。否则,我们可能需要执⾏两次内存访问,因为对象可能被分放在两 个8字节内存块中。 总体来说:结构体的内存对⻬是拿空间来换取时间的做法。 那在设计结构体的时候,我们既要满⾜对⻬,⼜要节省空间,如何做到: 让占⽤空间⼩的成员尽量集中在⼀起

4.3修改默认对⻬数

#pragma 这个预处理指令,可以改变编译器的默认对⻬数。

代码语言:javascript
复制
#include <stdio.h>
#pragma pack(1)//设置默认对⻬数为1
struct S
{
 char c1;
 int i;
 char c2;
};
#pragma pack()//取消设置的对⻬数,还原为默认
int main()
{
 //输出的结果是什么?
 printf("%d\n", sizeof(struct S));
 return 0;
}

结构体在对⻬⽅式不合适的时候,我们可以⾃⼰更改默认对⻬数

5 结构体传参

代码语言:javascript
复制
```c
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); //传地址
 return 0;
}
代码语言:javascript
复制
上⾯的 print1 和 print2 函数哪个好些?
答案是:⾸选print2函数。
原因:
函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。
如果传递⼀个结构体对象的时候,结构体过⼤,参数压栈的的系统开销⽐较⼤,所以会导致性能的下
降。
**结论:结构体传参的时候,要传结构体的地址。**
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-06-08,如有侵权请联系 cloudcommunity@tencent.com 删除

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.结构体的概念
    • 1.1结构体的声明
    • 1.2结构体变量的定义和初始化
    • 1.3结构体访问操作符
      • 1.3.1结构体成员直接访问
    • 1.3.2结构体间接访问操作符
  • 2.结构体的特殊声明
  • 3.结构体的自引用
  • 4结构体对齐规则
    • 4.1 对⻬规则
    • 4.2 为什么存在内存对⻬?
    • 4.3修改默认对⻬数
  • 5 结构体传参
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档