一.什么是结构体以及结构体的声明
1.1什么是结构体?
C语言提供了许多变量的类型,例如:char,short,long,int,float,double,等等,但是如果我想描述一个学生,一本书的时候,单一的内置变量是不够的。因为描述一个学生需要他的名字,年龄,学号,身高,体重等个人信息;要描述一本书的话则需要书名,书的作者,出版社,价格等信息。C语言为了解决这种问题,增加了结构体这种自定义的数据类型。让编程者可以自己定义想要的类型。总的来说,结构体就是一些值的集合,这些值成为成员变量。结构的每个成员可以是不同的类型,如:变量,数组,指针,甚至是其他结构体。
1.2结构体的声明
(1)普通声明:
struct Stu
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
}; //分号不能丢
(2)匿名结构体
struct
{
int a;
char b;
float c;
}x;
注意:定名结构体只能用一次!所以最好不用!
(3)结构体的自引用
在数据结构中有线性结构和树形结构,在线性结构中有一种数据结构叫做链表,与顺序表连续存储的方式不同,它采用的是链式存储,当我们在链表中想让从一个节点找到另一个节点是,我们就要用到结构体的自引用。
在结构体中包含一个类型为结构体本身的成员是否可以呢?
比如,定义一个链表节点:
struct Node
{
int data;
struct Node next;
};
答案是不行,因为如果一个结构体内包含一个相同类型的结构体,那么这个结构体的类型将会是无限大。
所以,正确的写法应该是:
struct Node
{
int data;
struct Node* next;
};
二.结构体的访问
2.1直接访问:结构体变量.成员名。
#include <stdio.h>
struct Point
{
int x;
int y;
};
int main()
{
struct Point a = { 1, 2 };
printf("x: %d y: %d\n", a.x, a.y);
return 0;
}
2.2间接访问:结构体指针-> 成员名
#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;
}
三.内存对齐问题(计算结构体大小)
3.1对齐规则
(1)结构体的第一个成员对其齐到和结构体变量起始位置偏移量为0的地址处。
(2)从第二个成员变量开始,都要对齐到某个对齐数的整数倍地址处。
对齐数 = 编译器中默认的一个对齐数与该成员变量大小的较小值。
vs中默认值为8,linux中gcc没有默认对齐数,对齐数就是变量成员自身的大小。
(3)结构体总大小为最大对齐数的整数倍。
(4)如果嵌套了结构体的情况,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体中成员的对齐数)的整数倍。
3.2总的来说结构体内存对齐是那时间换空间的做法。
3.3修改默认对齐数
#pragma这个预处理指令可以帮助我们修改默认对齐数。
3.4结构体传参:结构体传参最好用指针传参!
include <stdio.h>
// 定义一个简单的结构体
struct Point {
int x;
int y;
};
// 方式1:值传递(拷贝一份,不影响原数据)
void printPoint(struct Point p) {
printf("x=%d, y=%d\n", p.x, p.y);
}
// 方式2:指针传递(可以修改原数据)
void movePoint(struct Point* p) {
p->x += 10; // 修改原数据
p->y += 10;
}
int main() {
struct Point p1 = { 3, 5 };
// 值传递
printPoint(p1); // 输出: x=3, y=5
// 指针传递(修改原数据)
movePoint(&p1); // 传入地址
printPoint(p1); // 输出: x=13, y=15
return 0;
}
如果有错误的地方,欢迎大家批评指正!