结构体的使用与说明

上期我的结构体使用与说明并不全面,真的很对不起大家的期待呀,今天我闲来无事,再次更新一波更为详细的,希望能得到大家的支持,谢谢大家。


一.介绍

说到结构体,那我们今天就用一个例子来说明一下它的便捷性吧,假设你正在设计一个学生管理系统,需要存储每个学生的信息,如姓名呀、年龄和成绩,考虑到管理多个学生,在你没学结构体之前是否想到用变量来表示呢?

就像此代码这样:

#include<stdio.h>
int main()
{
	char student1_name[50];
	int student1_age;
	float student_gpa;

	char student2_name[50];
	int student2_age;
	float student2_gpa;

	return 0;
}

这样的方式是不是显的很冗长,也不方便管理。此时,结构体出现在舞台上,就像是一个数据容器,可以更有序地组织信息。结构体将姓名、年龄和成绩等信息打包在一起,就像是每个学生的档案。

问题产生:

此时可能会有一个思考:“如果有多个学生,是否要为每个学生分别创建这些变量?这样不是会变得复杂难以管理吗?” 这正是结构体发挥作用的地方。然而,在我们深入使用结构体之前,我们还需要了解一个重要概念。

二.内存对齐

理解内存对齐:

在计算机内存中,数据并不总是按照其实际大小占用空间。结构体的内存对齐是指为了提高访问速度,系统会对结构体的成员进行调整,使其在内存中按照一定的规则对齐。

对齐边界:

内存对齐涉及到对齐边界的概念。结构体成员的偏移量必须是其大小和对齐数的整数倍。对齐数通常由系统的硬件架构决定。

对齐规则:

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

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

3.在vs2022编译器中其默认值是8

影响因素:

 内存对齐的方式可能受到编译器、编译选项和目标平台的影响。这种对齐有时可以通过编译器的特定指令来控制。

结构体内存对齐的重要性:

性能影响:

正确理解和利用内存对齐可以提高程序的性能,因为它有助于减少内存访问的开销。未对齐的访问可能导致额外的处理,影响程序的效率。

储存优化:

 内存对齐也有助于最大程度地利用系统的内存,避免因为对齐不当而浪费存储空间。

结构体对齐:

成员的大小和对齐方式直接影响结构体的整体大小和布局。

由上所述了结构体的内存对齐与基本的介绍,那么我们便开始正式进入结构体的解释与说明吧!


结构体的定义与声明:

结构体到底是如何定义与声明的呢,我先将上面的一开始举例子的代码转化成结构体的方式给大家展示一下吧!

#include<stdio.h>
int main()
{
	struct student
	{
		char name[50];
		int age;
		float gpa;
	};//结构体的定义
	struct student;
	//结构体的声明
	return 0;
}

结构体的简便性:

通过上面的结构体的定义与声明是不是能看得出结构体的简便性了呢,只要通过一次的定义,便可以随意的声明其中的内容,随意更改里面的内容,不用再从根去重新定义与声明,可以减少不少的代码量。

结构体的成员访问:

通过点运算符(.),访问结构体的成员:

printf("Name: %s, Age: %d, GPA: %.2f\n", student1.name, student1.age, student1.gpa);
printf("Name: %s, Age: %d, GPA: %.2f\n", student2.name, student2.age, student2.gpa);

结构体的镶嵌:

结构体的镶嵌就是将一个结构体套在另外一个结构体当中,使得一个结构体可以内置多个结构体

struct Address {
    char city[50];
    char street[50];
};

struct Person {
    char name[50];
    struct Address address;
};

struct Person person1 = {"maker", {"Dong guan", "123 Main St"}};

结构体与数组:

在C语言中,结构体与数组之间存在一些有趣的关系。结构体可以包含数组作为其成员,而数组也可以用来存储结构体的集合。以下是一些详细说明:

结构体包含数组成员;
struct Point {
    int x;
    int y;
};

struct Rectangle {
    struct Point vertices[4];
};

在这个例子中,`Rectangle`结构体包含了一个由`Point`结构体组成的数组。每个`Point`结构体表示矩形的一个顶点。

. 数组存储结构体的集合

数组可以用来存储结构体的集合,这种方式很常见,特别是当我们需要处理多个相似类型的数据时:

struct Student {
    char name[50];
    int age;
    float gpa;
};

struct Student classA[5]; // 数组存储5个学生的信息

在这个例子中,`classA`是一个由`Student`结构体组成的数组,每个元素都表示一个学生的信息。

结构体数组的初始化:
struct Point {
    int x;
    int y;
};

struct Point points[3] = {{1, 2}, {3, 4}, {5, 6}};

在这里,`points`是一个包含3个`Point`结构体的数组,每个结构体表示一个点的坐标。

结构体数组的访问

访问结构体数组的元素与访问普通数组相似,使用索引即可:

printf("First point: (%d, %d)\n", points[0].x, points[0].y);

在这个例子中,我们访问了`points`数组的第一个元素的`x`和`y`成员。

结构体数组与指针

结构体数组与指针的关系也是密切的。可以使用指针来遍历结构体数组:

struct Student {
    char name[50];
    int age;
    float gpa;
};

struct Student classB[3]; // 假设有3个学生

// 使用指针遍历结构体数组
struct Student *ptr = classB;
for (int i = 0; i < 3; ++i) {
    printf("Student %d: %s, %d years old, GPA: %.2f\n", i + 1, ptr[i].name, ptr[i].age, ptr[i].gpa);
}

这里,`ptr`是一个指向`classB`数组的指针。逐个访问数组元素可以通过`ptr[i]`的形式实现。

结构体与数组的这些组合方式提供了灵活的数据组织和访问方式,尤其在处理多个相关数据时非常有用。

错误与调试

结构体成员访问错误

在使用结构体时,可能会遇到一些常见的错误,以及一些建议的调试方法:

 // 错误示例
  printf("Name: %s, Age: %d, GPA: %.2f\n", student1.nam, student1.age, student1.gpa);

调试建议:检查结构体成员的拼写和大小写。确保使用了正确的成员名称。

数组越界

  // 错误示例
  struct Student students[3];
  students[3] = {"John", 25, 3.5};

调试建议: 确保数组索引在合法范围内。在C中,数组索引是从0开始的,所以合法索引范围是0到数组长度减1。

结构体嵌套访问错误

 printf("City: %s, Street: %s\n", person1.address.cityy, person1.address.street);

调试建议:同样,检查结构体嵌套成员的拼写和大小写,确保使用了正确的嵌套成员名称。

内存对齐引发的问题

 struct Example {
      char c;    // 1字节
      double d;  // 8字节
      int i;     // 4字节
  };

调试建议:理解结构体内存对齐的原则。在这个例子中,由于`double`通常要求8字节对齐,编译器可能会在`char`和`int`之间插入填充字节。


以上便是我对结构体说明与使用的全部内容啦,谢谢大家的观看希望能对大家的代码之路提供一些建议

platform_driver 结构体是 Linux 内核中用于设备驱动程序的一种抽象,它允许驱动程序开发者更容易地与 Linux 的 platform device 框架集成。platform_driver 结构体中包含了驱动程序的注册和注销函数,以及与平台设备匹配所需的信息。 一个典型的 platform_driver 结构体定义如下: ```c #include <linux/platform_device.h> static int my_driver_probe(struct platform_device *pdev) { // 设备探测逻辑 return 0; } static int my_driver_remove(struct platform_device *pdev) { // 设备移除逻辑 return 0; } static const struct of_device_id my_driver_dt_ids[] = { { .compatible = "vendor,my-driver", }, { /* sentinel */ } }; static struct platform_driver my_driver = { .probe = my_driver_probe, .remove = my_driver_remove, .driver = { .name = "my_driver", .owner = THIS_MODULE, .of_match_table = my_driver_dt_ids, }, }; module_platform_driver(my_driver); ``` 使用说明: 1. 注册驱动:首先定义一个 platform_driver 结构体,其中包含 probe 和 remove 函数指针,这些函数将由内核在设备匹配成功时调用。同时定义一个 driver 成员,它是一个 device_driver 结构体,其中包含 driver_name、owner 和 of_match_table 等成员,用于指定驱动名称、拥有者和设备树兼容性表。 2. 设备匹配:内核使用 of_match_table 中的兼容性字符串来匹配设备树中的 platform_device 设备节点。如果匹配成功,probe 函数将被调用。 3. 探测和移除函数:probe 函数负责初始化驱动和设备,remove 函数负责清理在 probe 期间分配的资源。这些函数会在设备添加或移除时被内核调用。 4. 注销驱动:通过调用 platform_driver_register 函数来注册驱动,或者使用 module_platform_driver 宏来简化注册过程。注销驱动可以通过调用 platform_driver_unregister 宏或者模块卸载来完成。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Maker-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值