C语言中有许多内置类型,例如int、float、short、char等等,这些是系统已经帮我们规定好的东西,但是在实现操作中我们会发现仅仅只有这些类型是不够的,因此就产生了自定义类型,最知名的自定义类型就是结构体、联合体、枚举体
结构体定义
结构体就是把一组不同类型的元素放在一个集合里,这些元素构成的集合叫做结构体
结构体声明
struct tag
{};//注意分号
tag作为结构体的标签,在后续结构体的修改赋值操作中起到重要作用,当然你也可以忽略tag创建一个匿名结构体,但是它在后续中无法修改(只能调用一次)
例:
struct stu
{
char name[20];
int age;
int height;
}student={"xxx",18,170};
//or
struct stu
{
char name[20];
int age;
int height;
};
int main()
{
struct stu student={"xxx",18,170};
return 0;
}
建立了一个标签位stu的结构体,结构体中包含name、age、height三个信息,并且创建并初始化了student结构体变量
结构自我调用
struct stu
{
int i;
struct stu next;
}; //①
struct stu
{
int i;
struct stu* next;
}; //②
上述代码,①报错②正常,①式中我们知道意义是在结构体中装入int和本身,显而易见肯定是不行的,相当于一个房子里放一个相同大小的房子,这怎么可能呢,但是如果我们放入的是房子的地址牌,那是不成问题的。
②式同理,对于指针来说,无非就是4字节or8字节。
结论:结构体不能装入自己
结构体访问
常见的结构体访问有两种,直接访问和指针访问,对应得操作符也不同
直接访问:
struct tag
{
char name[20];
int age;
float height;
}stu;
int main()
{
struct tag stu={"xxx",18,170.0};
printf("%s ",stu.name);
printf("%d ",stu.age);
printf("%f ",stu.height);
return 0;
}
变量名 . 元素名
指针访问
struct tag
{
char name[20];
int age;
float height;
}stu;
int main()
{
struct tag stu={"xxx",18,170.0};
struct tag* ptr=&stu;
printf("%s ",ptr->name);
printf("%d ",ptr->age);
printf("%f ",ptr->height);
return 0;
}
结构体内存对齐
*⾸先得掌握结构体的对⻬规则:
- 结构体的第⼀个成员对⻬到相对结构体变量起始位置偏移量为0的地址处
- 其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。 对⻬数 = 编译器默认的⼀个对⻬数 与 该成员变量⼤⼩的较⼩值。 - VS中默认的值为8 - Linux中没有默认对⻬数,对⻬数就是成员⾃⾝的⼤⼩
- 结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的 整数倍。
- 如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构 体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。*
例:
struct S1
{
char c1;
int i;
char c2;
};
int main()
{
printf("%zd\n", sizeof(struct S1));
return 0;
}
下意识得觉得输出为6,实际上其实是12,下列我们来图解一下
结构体传参
struct S
{
int data[1000];
int num;
};
struct S s = {{1,2,3,4}, 1000};
void print1(struct S s)
{
printf("%d\n", s.num);
}
void print2(struct S* ps)
{
printf("%d\n", ps->num);
}
int main()
{
print1(s); //传结构体
print2(&s); //传地址
return 0;
}
两种方法在语法层面都是允许的,但是如果考率时间空间问题我们要优先使用地址传参,
结构体在内存中是存储在堆区,我们去调用结构体是需要去压栈开辟空间,如果直接调用,在结构体本身非常大的情况下需要开辟大量空间,而地址不一样,不是4B就是8B,空间开辟效率高,带来的结果就是空间省,时间短。