结构体类型的声明
结构体的声明
struct tag
{
member-list;//成员
}variable-list;//变量名
例如描述一个学生
struct Stu {
char name[20];
int age;
char sex[5];
char id[20];
};
结构体变量的创建和初始化
#include <stdio.h>
struct Stu {
char name[20];
int age;
char sex[5];
char id[20];
};
int main()
{
//按照结构体成员的顺序初始化
struct Stu s = { "张三",20,"男","2153321" };
printf("name: %s\n", s.name);
printf("age : %d\n", s.age);
printf("sex : %s\n", s.sex);
printf("id : %s\n", s.id);
//按照指定的顺序初始化
struct Stu s2 = { .age = 19,.id = "128973324",.name = "lisi",.sex = "女" };
printf("name: %s\n", s2.name);
printf("age : %d\n", s2.age);
printf("sex : %s\n", s2.sex);
printf("id : %s\n", s2.id);
return 0;
}
结构的特殊声明
在声明结构的时候,可以不完全的声明。
比如:
struct {
int a;
float b;
char c;
}x;
struct {
int a;
float b;
char c;
}a[20],*p;
上⾯的两个结构在声明的时候省略掉了结构体标签(tag)。
那么在上述情境下,如下代码是否合法呢?
p = &x;
警告:
编译器会把上面的两个声明当成完全不同的两个类型,所以是非法的。
匿名的结构体类型,如果没有对结构体类型重命名的话,基本上只能使用一次。
结构体的自引用
在结构中包含一个类型为该结构本身的成员是否可以呢?
比如,定义一个链表的节点:
struct Node {
int date;
struct Node next;
};
仔细分析,其实是不行的,因为⼀个结构体中再包含⼀个同类型的结构体变量,这样结构体变量的大小就会无穷大,是不合理的。
正确的自引用方式:
struct Node {
int date;
struct Node* next;
};
在结构体自引用使用的过程中,夹杂了typedef
对匿名结构体类型重命名,也容易引入问题。
请看代码:
typedef struct{
int date;
struct Node* next;
}Node;
这样的定义就会出问题,因为Node是对前面的匿名结构体类型的重命名产生的,但是在匿名结构体内部提前使用Node类型来创建成员变量,这是不行的。
解决方案:定义结构体避免使用匿名结构体
typedef struct Node{
int date;
struct Node* next;
}Node;
结构体内存对齐
现在我们讨论⼀个问题:计算结构体的大小。
这时就要考虑结构体的内存对齐。
对齐规则
1.结构体的第一个成员对齐到和结构体变量起始位置偏移量为0的地址处
2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
(对齐数=编译器默认的一个对齐数与该成员变量大小的较小值,VS中默认为8,Linux中gcc没有默认对齐数,对齐数就是成员自身的大小)
3.结构体总大小为最大对齐数(结构体中每个成员变量都有⼀个对齐数,所有对齐数中最大的)的整数倍。
4.如果嵌套了结构体的情况,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处,结构
体的整体大小就是所有最大对齐数(含嵌套结构体中成员的对齐数)的整数倍。
修改默认对齐数
#pragma
这个预处理指令,可以改变编译器的默认对⻬数。
#include<stdio.h>
#pragma pack(1)//设置默认对其数为1
struct S {
char c1;
int i;
char c2;
};
int main()
{
printf("%d\n", sizeof(struct S));
return 0;
}
结构体在对齐方式不合适的时候,我们可以自己更改默认对齐数。
结构体传参
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()
{
printf1(S);//传结构体
printf2(&S);//传地址
return 0;
}
传结构体和传地址都可以实现功能,但实际应用时多采用传地址的方式。
结论:结构体传参的时候,要传结构体的地址。