前言
c语言提供了内置类型,如char,short,int,long,long long,float,double,Bool类型。但当表示复杂的时候显得有些无力。为了表示复杂对象C语言就引进了新的类型即结构体类型。
一、结构是什么?
结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。
二、结构体类型
1.结构声明
基本的结构如下
struct 结构体标签 {
成员列表;
}结构体类型;//(全局变量)
struct是关键字无法修改
结构体标签是可修改的可以按自己的需求来自行定义
成员列表中可以定义各种不同的类型int型,float型,double型,都可以一起在一起使用。
而且其有可能是数组、指针,甚至是其他结构体。
结构体类型在括号内的时候表示的是局部变量,而当在括号外的时候表示的是全局变量
struct student {
char name[20];
int price;
char author[];
}s;
2.结构体初始化
初始化变量的方式有俩种,一种是如上面的一样 这时表示的是全局变量
struct student {
char name[20];
int price;
char author[];
}s;
第二种是在括号内写出变量,这时表示的是局部变量
struct student {
char name[20];
int price;
char author[];
};
int main()
{
struct student s;
return 0;
}
完整的可以写成
#include<stdio.h>
struct student {
char name[20];
int price;
char author[];
};//s={"cyuyan",100,"bian"}全局变量
int main()
{
struct student s = { "cyuyan",100,"bian" };//局部变量
printf("%s %d %s", s.name, s.price, s.author);
return 0;
}
3.特殊的声明(不完全声明)
当结构体标签不存在即没有名字的时候,我们称它为匿名结构体类型
struct
{
int a;
char b;
float c;
}s;
必须得用全局变量,因为如果使用局部变量的话因为没有名字无法初始化。(如 struct sty s={}
可是不知道结构体名啊,即无法用局部变量实现)而且,这结构体每次仅能用一次。
4.自引用类型
数据结构中提过当用1234来排序,顺序表可以将这些一个一个排列在一起。而链表可以一个一个串连在一起,1到2 2到3 …这种过程。这跟结构体很类似。
回归正题
当想要自引用操作时
struct Node
{
int data;
struct Node next;
};//错误
这种方法看起来可行,但是我们无法知道node的大小以这种形式下去会发现,会进入一种循环自己里面是自己自己里面又是自己,又是自己无限的套娃下去,并且不知道sizeof(struct Node)大小,因此当要找同类型的节点完成自引用操作时无法实现。
如上面的图。我们可以采用链表的方式,我们可以把这个分成俩部分一个是数据域另一个是指针域,以找到下一个节点的形式来找到同类型的下一个节点来实现自引用操作
struct Node
{
int data;
struct Node* next;
};
5.结构体初始化
结构体初始化时可以在全局变量中实现初始化,又可以在局部变量中来实现初始化
1.全局变量中初始化
struct student {
char name[20];
int price;
char author[];
}s = { "jiegouti",100,"bian" };
2.在局部变量中初始化
int main()
{
struct student s = { "cyuyan",100,"bian" };
printf("%s %d %s", s.name, s.price, s.author);
return 0;
}
成功实现
再难一点当要实现嵌套时可以是这样
#include<stdio.h >
struct node {
int a;
char name[50];
char who[10];
};
struct student {
char name[20];
int price;
char author[30];
struct node ss;
}s = { "jiegouti",100,"bian",{10000,"quan","bian"}};
int main()
{
//struct student s = { "cyuyan",100,"bian" };
printf("%s %d %s %d %s %s", s.name, s.price, s.author,s.ss.a,s.ss.name,s.ss.who);
return 0;
}
成功
结构体对齐
我们已经掌握了结构体的基本使用。现在我们深入讨论一个问题:计算结构体的大小
struct S2
{
char c1;
int i;
char c2;
};
printf("%d\n", sizeof(struct S2));
你是否能猜得出答案是什么
比起这个我们先看一下结构体对齐的规则:
- 第一个成员在与结构体变量偏移量为0的地址处。
- 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
VS中默认的值为8 - 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
char c1 1/8 //第一个成员 char的字节数是一个字节因此占用一个字节 在这占用了0
int i 4/8 4 //对齐数是 4 因此得找与4整除的地址 然后从4开始占用4个字节,上面的3个会浪费掉
char c2 1/8 //对齐数是 1 即在下面占用一个字节,因1能与任何数,整除
在这里最大对其数是4 即总大小(包括浪费的空间)必须为4的整数倍,即12是结构体的大小
4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整
体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
``
struct s3 s3 { double 8/8 8 因此 8就是最大对齐数 因此应该从8的整数倍开始计算
char c 1/8 1
int i 4/8 4
} //struct s3 总共占用 16个字节
然后依次与上面的方法计算即可,获得结果32.但注意结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍的这要求
修改默认对齐数
#pragma pack(1)//设置默认对齐数为1
struct S2
{
char c1;
int i;
char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
用完后得还原对齐数吗,所以得恢复
结构体传参
struct S
{
int data[1000];
int num;
};
struct S s = {{1,2,3,4}, 1000};
//结构体传参
void print1(struct S s)
{
pprintf("%d\n", s.num);
}
结构体地址传参
void print2(struct S* ps)
{
printf("%d\n", ps->num);
}
int main()
{
print1(s); //传结构体
print2(&s); //传地址
return 0;
}
总结
写了结构体让我对结构体有了更多的理解和巩固。之前消极了一段时间,没有好好学习,现在已经都想通了,想继续努力了,这也是我第一篇博客,写的有些不好,如果哪里出现了问题请告诉我谢谢