目录
什么是结构体
结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。
结构体的声明
我举个例子:
struct Student//声明一个学生的结构体
{
char id[20];//学号
char name[20];//姓名
char sex[10];//性别
int age;//年龄
};
其实结构体的声明可以分为三种方式
1. 比较好的方式:
#include <stdio.h>
struct student //结构体类型的说明与定义分开声明
{
int age; //年龄
float score; //分数
char sex; //性别
};
int main ()
{
struct student a={ 20,79,'f'}; //定义
printf("年龄:%d 分数:%.2f 性别:%c\n", a.age, a.score, a.sex );
return 0;
}
2. 比较浪费的方式:
#include <stdio.h>
struct student //声明时直接定义
{
int age; //年龄
float score; //分数
char sex; //性别
//这种方式比较浪费,只能用一次
} a={21,80,'n'};
int main ()
{
printf("年龄:%d 分数:%.2f 性别:%c\n", a.age, a.score, a.sex );
}
3. 避免使用的方式:
#include <stdio.h>
struct //直接定义结构体变量,没有结构体类型名。这种方式最差
{
int age;
float score;
char sex;
} t={21,79,'f'};
int main ()
{
printf("年龄:%d 分数:%f 性别:%c\n", t.age, t.score, t.sex);
return 0;
}
结构体变量的创建和初始化
我就以上面这个结构体声明为例:
```c
struct Student//声明一个学生的结构体
{
char id[20];//学号
char name[20];//姓名
char sex[10];//性别
int age;//年龄
};
int main()
{
//按照结构体成员的顺序初始化
struct Stu s = { "张三", 20, "男", "20230818001" };
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 = 18, .name = "lisi", .id = "20230818002", .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;
char b;
float c;
}x;
struct
{
int a;
char b;
float c;
}a[20], * p;
匿名结构体也称为未命名结构体,由于没有名称,因此不会创建它们的直接对象(或变量),通常我们在嵌套结构或联合中使用它们。
值得注意的是,匿名结构体类型可以说是“一次性用品”,用了一次以后再也用不了了。
struct
{
int a;
char b;
float c;
}x;
struct
{
int a;
char b;
float c;
}a[20], * p;
int main()
{
p = &x;
return 0;
}
如上代码是错误的,虽然两个匿名结构体,看上去它们的成员都是一样的,表面上是同一类型,但实际上他们是两种不同的类型。因此,因为在定义的时候没有写结构体类型名称,编译器会把它们当做两种不同的类型,然后报错。
结构体自引用
结构体内部的成员不可以是该结构体本身。
struct Node
{
int data;
struct Node next;
};
这种类型的自引用是非法的,因为成员next是另一个结构体,类型是struct Node它的内部还要包含自己的成员next。
这个next还将包括自己的成员next,这样重复下去,永无止境,像是一个永远不会终止的递归程序。
那么应该如何正确的自引用呢?
struct Node
{
int data;
struct Node* next;
};
如果在结构体自引用使用的过程中,夹杂了 typedef 对匿名结构体类型重命名,也容易发生问题。
typedef struct
{
int data;
Node* next;
}Node
在使用typedef创建Node并且其结构成员可以自引用,但是上述示例在定义next时,Node并没有创建,所以在结构体内部定义next时,结构体类型并没有创建,所以不合法。
那么解决方法也很简单——定义结构体就不要使用匿名结构体了
typedef struct Node
{
int data;
struct Node* next;
}Node;
结构体内存对齐
我们首先要了解结构体的对齐规则:
- 第一个成员在与结构体变量偏移量为0的地址处。
- 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
- 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
- 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
举例:
这里我用vs进行演示,vs的默认对齐数是8.
1.
struct S1
{
char c1;
int i;
char c2;
};
printf("%d\n", sizeof(struct S1));
S1实际上只需要6个字节,但是因为对齐规则,浪费了6个字节,所以用了12个字节。
2.
struct S2
{
char c1;
char c2;
int i;
};
printf("%d\n", sizeof(struct S2));
8个字节符合第三条对齐规则。
3.
struct S3
{
double d;
char c;
int i;
};
printf("%d\n", sizeof(struct S3));
这里的最大对齐数是8,16个字节符合对齐规则
4.
struct S3
{
double d;
char c;
int i;
};
struct S4
{
char c1;
struct S3 s3;
double d;
};
printf("%d\n", sizeof(struct S4));
根据第四条对齐规则:
结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
S3的最大对齐数是8,S4的最大对齐数也是8,所以32个字节符合规则。
结构体传参
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;
}
上面两种方式相比,printf2会更好,因为函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。
如果传递⼀个结构体对象的时候,结构体过⼤,参数压栈的的系统开销⽐较⼤,所以会导致性能的下
降。
第二种同样可以达到第一种的效果,而且效率更高。
结构体实现位段
什么是位段?位表示的是二进制位。
注意点:
1.位段的成员必须是int、unsigned int 或者signed int、char。
2.位段的成员名后面有一个冒号和一个数字。
我举个例子:
struct S
{
int _a : 2;
int _b : 5;
int _c : 10;
int _d : 30;
};
如果结构体中的成员a中放了0,1,2,3这几个数字,其实用两个bit位就足够用了,所以位段可以很大程度上的节省空间。
2+5+10+30=47(bit),如果照这样来说,那么6(48bit)个字节就足够了,我们来验证一下。
虽然和我所想不一样,但是还是节省了一半的空间,达到了我想要的效果。
要想知道为什么,我们就得了解一下位段的内存分配:
- 位段的成员可以是 int unsigned int signed int 或者是 char 等类型
- 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的⽅式来开辟的。
- 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使⽤位段。
4.一个位段必须储存在同一个储存单元中,不能跨两个储存单元
一样还是举个例子:
struct S
{
char a : 3;
char b : 4;
char c : 5;
char d : 4;
};
int main()
{
printf("%zd", sizeof(struct S));
return 0;
}
那具体是怎么存放的呢?我再来演示一下:
#include <stdio.h>
struct S
{
char a : 3;
char b : 4;
char c : 5;
char d : 4;
};
int main()
{
struct S s = { 0 };
s.a = 10;
s.b = 12;
s.c = 3;
s.d = 4;
printf("%zd", sizeof(s));
return 0;
}
我们调试一下看对不对:
希望这篇博客对你有所帮助!!!!!