一.结构体的初始化
结构体常用的案例 : 学生
1. 只构建结构体类型
***struct 结构体类型名字
{
成员1
成员2
..
成员n
} ;***
例 :
struct Student
{
char name[20];//注意长度
int age;
char sex[20];
struct score s;//分数
};
①. 用构建好的结构体类型创建变量
strut 结构体类型名字 变量名;
例 :
struct Student s1;
②. 用创建好的变量进行初始化
变量名 = {成员1的值, 成员2的值, … , 成员n的值}
例 :
s1 = {"nihao", 18, "男", 100};
2. 构建类型并创立变量
struct 结构体类型名字
{
成员1
成员2
…
成员n
} 结构体类型变量名 ;
例 :
struct Student
{
char name[20];//注意长度
int age;
char sex[20];
struct score s;//分数
}s1;
用创建好的变量进行初始化
变量名 = {成员1的值, 成员2的值, … , 成员n的值}
例 :
s1 = {"nihao", 18, "男", 100};
3. 构建类型并创立变量且初始化变量
struct 结构体类型名字
{
类型名 成员1
类型名 成员2
…
类型名 成员n
} 结构体类型变量名 = {成员1的值, 成员2的值 , … , 成员n的值} ;
例 :
struct Student
{
char name[20];
int age;
char sex[20];
struct score s;
} s1 = {"nihao", 18, "男", 100};
4. 特殊的声明 :
①. 完全声明
匿名结构体 , 没有自义定结构体类型名 , 但是有变量名, 且不同时构建的匿名自义定结构体变量是不通的, 哪怕成员相同
例 :
struct
{
int a;
char b;
float c;
} x ;
②. 声明且重命名
可以使用tepydef关键字进行结构体类型名的重命名
例 :
typedef struct Student
{
char name[20];//注意长度
int age;
char sex[20];
struct score s;//分数
} Stu;
该案例将类型 struct Student 重命名为 Stu
特点 : 结构体嵌套
其中结构体类型可以嵌套在其他结构体中,形成更复杂的数据结构, 也就是结构体嵌套
这种嵌套结构体可以用于表示更复杂的关系和组织
例 :
//链表
#include <iostream>
using namespace std;
struct Student
{
string name; // 姓名
int age; // 年龄
double score; // 分数
};//学生
struct Point
{
Student s1; // 学生1信息
struct Point* next; // 指向下一个节点的指针(自引用)
};// 链表节点结构体
// 遍历链表并输出学生信息
void PrintList(Point* head)
{
Point* p = head;//创造临时指针, 用于遍历
while (p != NULL)
{
cout << "姓名:" << p->s1.name << ",年龄:" << p->s1.age << ",分数:" <<
p->s1.score << endl;
p = p->next;
}
}
int main()
{
// 创建链表,添加节点
Point* head = new Point;
head->s1.name = "nihao";
head->s1.age = 18;
head->s1.score = 90.0;
head->next = NULL;
Point* p1 = new Point;
p1->s1.name = "good";
p1->s1.age = 19;
p1->s1.score = 0.0;
p1->next = NULL;
Point* p2 = new Point;
p2->s1.name = "hello";
p2->s1.age = 20;
p2->s1.score = 100.0;
p2->next = NULL;
head->next = p1;
p1->next = p2;
PrintList(head);// 遍历链表并输出学生信息
return 0;
}
二. 结构体的大小
1. 特点 : 结构体的内存对齐
2. 规则
①. 第一个成员在与结构体变量偏移量为0的地址处
②. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处(此处有可能浪费内存)
对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值
VS中默认的值为8(只有vs有默认对齐数, 其他编译器是自身大小)
③. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍(此处有可能浪费内存)
④. 如果嵌套了结构体的情况, 嵌套的结构体对齐到自己的最大对齐数的整数倍处, 结构体的整
体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍
offsetof计算成员的偏移量
语法 :
offsetof (type,member)
返回值是偏移量 Return member offset(偏移量)
例 :
struct S1
{
char c1;//1
int i;//4
char c2;//1
};
int main()
{
printf("%d\n", offsetof(struct S1, c1));//c1的位置
printf("%d\n", offsetof(struct S1, i));//i的位置
printf("%d\n", offsetof(struct S1, c2));//c2的位置(偏移量)
return 0;
}
计算例子 :
struct S3
{
double d;
char c;
int i;
};
struct S4
{
char c1;
struct S3 s3;//对齐到自己的最大对齐数的整数倍处, 此处最大对齐数时8,所以到8的整数倍
double d;
};
//内存不能直接按照成员本身的大小直接计算
3. 解决方法
①. 让占用内存空间小的成员放在一起, 避免浪费
②. 修改默认对齐数(vs中默认对齐数是8)
例 :
#pragma once
头文件中使用, 功能是:放置头文件被多次引用
#pragma pack(4)//设置默认对齐数为4
#pragma pack()//恢复默认
三. 结构体传参
//结构体传参
struct S
{
int data[1000];
int num;
};
void print1(struct S ss)
{
int i = 0;
for (i = 0; i < 3; i++)
{
printf("%d ", ss.data[i]);
}
printf("%d\n" ,ss.num);
}
void print2(const struct S* ps)
{
int i = 0;
for (i = 0; i < 3; i++)
{
printf("%d ", ps->data[i]);
}
printf("%d\n", ps->num);
}
int main()
{
struct S s = { {1,2,3}, 100 };
print1(s); //传值调用, 效率低, 但是值不影响原值
print2(&s); //传址调用, 效率高, 但是地址是可以改变原值, 所以const, 又安全又高效
return 0;
}
结构体传参的时候,要传结构体的地址
且为了使用指向该结构的指针访问结构的成员, 必须使用 ->运算符