结构体回顾
结构体
自定义的类型:结构体、联合体、枚举
结构是一些值的集合,这些值成为成员变量,结构的每个成员可以是不同类型的变量
//描述一本书:书名、作者、定价、书号 //结构体类型---类似于整型、浮点型 struct Book { char book_name[20];//书名 char author[20];//作者 float prince;//价格 char id[18];//书号 }; b4, b5, b6;//结构体变量’ //这里的结构体变量个下面的本质是一样的 //但是这里的是全局变量,而下面的是局部变量 int main() { //结构体需要用大括号进行初始化,因为结构体里面不只一个值 //初始化的时候我们是根据成员进行初始化的 //下面的初始化是按照成员顺序进行初始化的 struct Book b1 = {"鹏哥c语言","鹏哥",38.8f,"PG20240520"}; //如果不按照成员顺序进行初始化可以这么写 struct Book b2 = {.id="DG20240520",.book_name="蛋哥Linux",.author="蛋哥",.prince=55.5f}; //两个操作符. -> printf("%s %s %f %s\n", b1.book_name, b1.author, b1.prince, b1.id); printf("%s %s %f %s\n", b2.book_name, b2.author, b2.prince, b2.id); return 0; } //鹏哥c语言 鹏哥 38.799999 PG20240520 //蛋哥Linux 蛋哥 55.500000 DG20240520 //为什么这个数据会和我们设置的有差异呢? // //因为浮点数在内存中 有可能是不能进行精确保存的 /* 判断浮点数是否相等的方法 if(fabs(f-3.45)<0.00000001) { printf("相等"); } else { printf("不相等"); } */
结构体的声明
在声明结构体的时候,可以不完全的声明---把名字省略掉
struct //直接把结构体名字去掉 { char c; int i; double d; }s1;//直接在这里进行结构体变量的创建 //匿名结构类型创建变量 //创建变量只能用一次 int main() { //struct S s2;这种创建的方式就不行了 return 0; } struct //直接把结构体名字去掉 { char c; int i; double d; }* ps;//匿名结构类型的指针类型 //匿名结构体类型+ *就是匿名结构体类型指针 //这里的ps就是指针变量 int main() { ps = &s1; return 0; } //两个类型虽然成员是一样的,但是编译器会认为这两个匿名结构体的类型是不一样的 //所以结构体指针也是不一样的 //编译器会认为一种匿名结构体类型是一种类型,而另一种就是另一种类型 //反正是没有相同的匿名结构体类型的 //我们只有在仅仅只使用一次的情况下才会使用匿名结构体类型 //编译器会把两个匿名结构体类型当成两个不同类型的匿名结构体类型的
数据结构---数据结构其实是数据在内存中的组织结构
//struct Node //{ // int data;//存放数据 // struct Node next;//访问下一个节点---类似递归 // //但这中写法是错误的 //}; //结构体能自己找到同类型的下个节点,实现自引用,但是上面的方式是不对的 //那么在一个结构体中寻找下一个同类型的结构体的变量的方法 struct Node { int data;//存放数据----数据域 struct Node* next;//---指针域 //next存放的是下个节点的地址 //这样我们不仅能存储这个节点的数据,还能存储下个节点的地址 //这样我们就能使结构体能自己找到同类型的下个节点,实现自引用 //存放这个节点的数据,还能找到下个节点的数据,那么我们添加一个结构体指针就行了 }; int main() { return 0; }
/*这里的是一个结构体类型的 struct Node { int data;//存放数据----数据域 struct Node* next;//---指针域 };*/ //那么我们对这个结构体类型进行重命名Node typedef struct Node { int data;//存放数据----数据域 struct Node* next;//---指针域 //错误写法:Node* next;//---指针域 //我们是先创建这个结构体类型,再进行重名名的,所以在顺序上面,这个代码就是错的 //重命名是后来的,所以不能将里面的代码进行改变 }Node; int main() { return 0; }
2.结构体内存对齐
我们在涉及计算结构体的大小的问题的时候,就会面临结构体内存对其的问题了
offsetof-----宏--头文件stddef.h
计算结构体成员相较于结构体变量起始位置的偏移量
offsetof(type,member)
offsetof计算的是每个成员在内存中相较于起始位置的偏移量
//struct S1 //{ // char c1; // char c2; // int n; //}; //struct S2 //{ // //顺序不一样 // char c1; // int n; // char c2; //}; //int main() //{ // printf("%zd\n", sizeof(struct S1));//占8个字节 // printf("%zd\n", sizeof(struct S2));//占12个字节 // return 0; //} //为什么输出的大小一个是8一个是12呢? //那么这里就涉及到了结构体的对齐问题了 //结构体的成员在内存中是存在对齐现象的 //结构体内对齐 //offsetof-----宏 //计算结构体成员相较于结构体变量起始位置的偏移量 struct S1 { char c1; char c2; int n; }; struct S2 { //顺序不一样 char c1; int n; char c2; }; int main() { //printf("%zd\n", sizeof(struct S1));//占8个字节 //printf("%zd\n", sizeof(struct S2));//占12个字节 struct S1 s1 = { 0 }; //计算偏移量 /*printf("%zd\n", offsetof(struct S1, c1));//0 printf("%zd\n", offsetof(struct S1, c2));//1 printf("%zd\n", offsetof(struct S1, n));*///4 printf("%zd\n", offsetof(struct S2, c1));//0 printf("%zd\n", offsetof(struct S2, n));//4 printf("%zd\n", offsetof(struct S2, c2));//8 return 0; } //第一个字节给c1,第二个字节给c2,后面的两个字节空着了, // 因为n是从第4个字节开始的,占了4个字节 //offsetof计算的是每个成员在内存中相较于起始位置的偏移量 //画图: /* 第一行是c1占的字节 第二行是c2占的字节 第二三行是空的 n是从第四行到第七行的 总共算下来就是8个字节 */ //printf("%zd\n", offsetof(struct S2, c1));//0 //printf("%zd\n", offsetof(struct S2, n));//4 //printf("%zd\n", offsetof(struct S2, c2));//8 //对s2进行计算得到的就是0 4 8 /* 第0行是c1 1 2 3行是空的 4 5 6 7放的是n,因为n占4个字节,并且李起始点有4个字节 第8行方的是c2 */ //但是为什么之前计算的是S2占12个字节呢? //不管是S1还是S2,他们的成员在内存中不是一个放完就放另一个的 //其实是存在对齐现象的
C语言---自定义类型:结构体(2)https://developer.aliyun.com/article/1544451