当前位置:   article > 正文

自定义类型之结构体详解——struct_struct结构体

struct结构体

目录

前言

一、结构体是什么

二、结构体的声明

三、匿名结构体

四、结构体的自引用

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

六、结构体内存对齐

七、修改默认对齐数

八、结构体传参

总结


前言

结构体是一种自定义数据类型,可以将不同数据类型的变量组合成一个结构体,方便进行管理和操作。


一、结构体是什么

结构体是一种在编程语言中定义自定义数据类型的方式。它能够将不同的数据类型组合在一起,并以单个单位的形式表示它们。例如,一个人的结构体可以包括姓名、年龄、性别等信息。可以使用结构体来创建具有自定义特性的新数据类型,从而增强程序的灵活性和可读性。在C、C++、Java等编程语言中,结构体通常用关键字 struct 定义。

二、结构体的声明

在C语言中,可以使用struct关键字声明结构体。结构体声明的一般形式如下:

  1. struct 结构体名 {
  2. 数据类型 成员1;
  3. 数据类型 成员2;
  4. 数据类型 成员3;
  5. //...
  6. };

其中,结构体名是该结构体类型的名称,成员可以是任何数据类型(包括基本数据类型和其他结构体类型),用于描述该类型的数据,可以包含多个成员变量,成员变量之间用分号隔开。

例如,以下是一个表示学生信息的结构体声明:

  1. struct Student {
  2. char name[20];
  3. int age;
  4. float score;
  5. };

这个结构体包含三个成员变量:一个字符串类型的name、一个整型的age、一个浮点型的score,表示学生的名字、年龄和成绩。

三、匿名结构体

匿名结构体是指在定义一个结构体变量时,不指定结构体名称,只定义结构体的字段。这种结构体不需要使用类型名就可以直接访问结构体字段,也称为无名结构体。

示例:

  1. int main()
  2. {
  3. struct
  4. {
  5. char name[20];
  6. int age;
  7. }person = { "zhangsan", 19 };
  8. printf("%s\n", person.name);
  9. printf("%d\n", person.age);
  10. return 0;
  11. }

在此示例中,我们定义了一个无名结构体类型,包含两个字段:name和age。然后我们创建一个名为person的变量,该变量类型为我们定义的无名结构体类型,并初始化name和age字段。最后,我们可以通过person变量访问该结构体的字段值。

四、结构体的自引用

结构体的自引用指的是结构体类型中包含一个指向自身类型的指针成员变量。这种自引用的结构体类型在树形结构、链表等数据结构的实现中经常用到。要声明一个包含自引用的结构体类型,需要在结构体定义中使用结构体名作为指针成员变量的类型,代码示例如下:

  1. struct TreeNode {
  2. int val;
  3. struct TreeNode *left;
  4. struct TreeNode *right;
  5. };

在上述示例代码中,TreeNode结构体定义了一个整型变量val和两个指向自身类型的指针成员变量leftright。这个结构体类型可以用于构造树形结构数据。

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

下面是结构体变量的定义和初始化示例:

  1. struct Point
  2. {
  3. int x;
  4. int y;
  5. }p1; //声明类型的同时定义变量p1
  6. struct Point p2; //定义结构体变量p2
  7. //初始化:定义变量的同时赋初值。
  8. struct Point p3 = {x, y};
  9. struct Node
  10. {
  11. int data;
  12. struct Point p;
  13. struct Node* next;
  14. }n1 = {10, {4,5}, NULL}; //结构体嵌套初始化
  15. struct Node n2 = {20, {5, 6}, NULL};//结构体嵌套初始化

六、结构体内存对齐

我们已经掌握了结构体的基本使用了。 现在我们深入讨论一个问题:计算结构体的大小。

例如:

  1. struct S1
  2. {
  3. char c1;
  4. int i;
  5. char c2;
  6. };
  7. int main()
  8. {
  9. printf("%zd\n", sizeof(struct S1));
  10. return 0;
  11. }

在这个代码中,可以打印出S1的大小为12,怎么得来的呢,接下来就需要另一个知识点:结构体内存对齐。

结构体内存对齐是为了使结构体中的每个数据成员能够被高效地访问。当结构体的数据成员在内存中被存储时,通常不是按照声明的顺序排列的,还会考虑一些对齐规则。对齐规则通常由编译器或操作系统决定,但也可以通过指令控制。

常见的对齐规则是按照数据类型的大小进行对齐,在结构体中,如果一个数据成员的大小小于对齐值,则会在该数据成员后填充一定的字节,使得下一个数据成员能够按照对齐值对齐。

对齐规则:

1. 第一个成员在与结构体变量偏移量为0的地址处。

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

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

  • VS中默认的值为8

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

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

例如,考虑下面这个结构体:

  1. struct example
  2. {
  3. char c; // 占用1个字节
  4. int i; // 占用4个字节
  5. short s; // 占用2个字节
  6. double d; // 占用8个字节
  7. };

如果按照对齐规则,结构体的布局可能是这样的:

  1. struct example
  2. {
  3. char c; // 占用1个字节
  4. char padding1[3]; // 补齐3个字节
  5. int i; // 占用4个字节
  6. short s; // 占用2个字节
  7. char padding2[6]; // 补齐6个字节
  8. double d; // 占用8个字节
  9. };

使用内存对齐可以减小结构体的内存占用,提高读写效率。但需要注意的是,对齐会增加一定的开销,例如填充字节需要占用内存空间,所以如果结构体中数据成员的大小很小,对齐带来的好处可能会被抵消。此外,如果结构体中的数据成员顺序被重新排列,可能导致对齐效果变差。

七、修改默认对齐数

在 C/C++ 中,结构体内存对齐可以通过使用 #pragma pack 指令来修改默认对齐数。

默认情况下,结构体成员会按照其自然对齐方式排列,而结构体本身的大小会按照默认对齐数(通常为 4 字节或 8 字节)和对齐规则进行对齐。如果需要调整结构体的对齐方式,可以使用 #pragma pack 指令来实现。

例如,如果需要将默认对齐数改为 1 字节,可以使用以下代码:

  1. #pragma pack(1)
  2. struct MyStruct
  3. {
  4. int a;
  5. char b;
  6. short c;
  7. };

在这个例子中,#pragma pack(1) 指令告诉编译器使用 1 字节对齐方式,因此结构体 MyStruct 中的成员将按照 1 字节对齐方式排列,而结构体本身也会按照 1 字节对齐方式进行对齐。

需要注意的是,修改默认对齐数可能会影响代码的性能,因为它会增加内存访问的负载。因此,只有在必要的情况下才应该使用 #pragma pack 指令来调整对齐方式。

八、结构体传参

在 C/C++ 中,结构体可以作为函数的参数传递。在传递结构体时,可以使用值传递或指针传递两种方式。

值传递是指将整个结构体作为参数传递给函数,这样函数会复制一份结构体,并在函数内部使用。这种方式比较简单,但对于大型结构体来说可能会产生较大的开销,因为需要进行结构体的复制。示例如下:

  1. #include<stdio.h>
  2. struct Point
  3. {
  4. int x;
  5. int y;
  6. };
  7. void printPoint(struct Point p)
  8. {
  9. printf("x = %d, y = %d\n", p.x, p.y);
  10. }
  11. int main()
  12. {
  13. struct Point p = { 1, 2 };
  14. printPoint(p);
  15. return 0;
  16. }

指针传递是指将结构体的指针作为参数传递给函数,并在函数内部通过指针来访问和修改结构体的成员。这种方式可以避免结构体的复制,但需要注意在函数内部对指针进行检查和处理,以避免空指针和野指针等问题。示例如下:

  1. #include<stdio.h>
  2. struct Point
  3. {
  4. int x;
  5. int y;
  6. };
  7. void printPoint(struct Point* p)
  8. {
  9. if (p != NULL)
  10. {
  11. printf("x = %d, y = %d\n", p->x, p->y);
  12. }
  13. }
  14. int main()
  15. {
  16. struct Point p = { 1, 2 };
  17. printPoint(&p);
  18. return 0;
  19. }

需要注意的是,在使用指针传递结构体时,需要确保结构体在函数外部的生命周期不会比函数调用的时间短,否则可能会引发未定义行为。


总结

以上就是所有的内容,本文详细介绍了结构体的各种使用,并对结构体在内存中的存储进行了深入的探讨。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/142971
推荐阅读
相关标签
  

闽ICP备14008679号