结构体的声明和初始化
一个结构体内部可以包含多种类型的数据,这样可以灵活的描述一个多元对象,如形态、颜色各异的车。需要注意的是:声明一种结构体类型时不要同时对里面的结构体成员初始化,这是不允许的。
struct name{
char a='s';//错误的!!
/*.......*/};
以实例讲解结构体的结构组成:
struct name{
char a1;
int b1;
/*.......*/}s;
其中struct是结构体关键字,name是结构体名称(结构体类型名),s是结构体变量,a1和b1都是结构体成员变量,结构体成员可以是不同类型的,例如int、char、float、double甚至结构体类型等。 为方便理解,可以将类型名name类比为整型里的int,同理,int a(创建一个int类型的变量a),这里的s也类比为变量a。所以同赋值a一样需要同样的int型数据,赋值结构体变量s也需要相对应的数据。下面有四种种声明方法及其匹配的初始化方法:
为减少行数方便阅读,下面的main函数返回类型都用的void,因此不用写return 0;
声明1:声明一种结构体类型,没有同时创建结构体变量
struct name{
char a1;
int b1;
};
void main(){ //在main里创建结构体变量s1,初始化
struct name s1={'h', 5};
}
声明2:声明一种结构体类型,同时创建一个结构体变量s1
struct name2{
char a2;
int b2;
}s2,ss2;
struct name ss2 = { 'r',4 };//选择在main外对ss2初始化
void main(){ //选择在main里对体变量s2初始化
struct name s2={'h', 5};
}
声明3:声明一种结构体类型,同时创建一个结构体变量s1和对s1初始化,一步到位
struct name3{
char a3;
int b3;
}s3={'h', 5};
void main(){ //可以在main里创建一个同类型的结构体变量
struct name s={'g', 9};//变量s和s3都属于类型name3
}
不完全声明4:未赋予该结构体类型名称,但有结构体变量。若后续不对其重命名,仅能使用该类型的结构体一次。
struct {
char a;
int b;
}s4 = { 'a' ,5};
//可以对这个属与不知道叫什么名字类型的结构体变量正常初始化
//但无法再创建属于这种类型的结构体变量,因为无类型,不知道怎么引用,
//如struct s5,都不知道s5是什么类型的。
struct {
char a;
int b;
}s5;//因为没有结构体类型名,所以编译器会认为s4和s5是不同类型的。
操作符.和->
.:结构体的成员访问操作符,可以直接对结构体变量里的结构体成员操作(访问、赋值)。因此上面的
struct name s={'j', 6};等同于struct name s={ .b=6, .a='j'};后者的初始化顺序可以颠倒,但二者不能同时出现,就好比int a=1;又int a=2;,多次初始化了,语法错误。
->:指向结构体的指针的成员访问操作符。应用场景多在传参结构体指针里。
struct name {
char a;
int b;
}s = { .a = 'r', .b = 4 };
void main(){
struct name* p = &s;//接受结构体变量的指针的类型也必须是同类型的
printf("%c %d", p->a, p->b);//打印r 4
}
结构体内存对齐
当我们计算这个结构体的大小时,变量s的大小不是5个字节而是8个:
struct name {
char a;
int b;
}s = { .b = 4, .a = 'r', };
void main(){
size_t x=sizeof(s);
}
出现的原因是内存存储结构体的一种特殊规则:内存对齐。
- 第一个结构体成员的地址对齐到和结构体变量起始位置偏移量为0的位置。
- 其他结构体成员的地址对齐偏移量为距起始位置的某个数字(对齐数)的整数倍位置。对齐数=编译器的默认对齐数和该成员大小相比,数值较小的那个。 这里的测试在VS2022里进行的,默认对齐数为8。
- 整个结构体的大小必须为结构体成员里的最大对齐数的整数倍。
- 若结构体里面嵌套了结构体,里面的嵌套结构体的大小为该结构体成员的最大对齐数。
以下示例:
struct name {//成员大小 默认对齐数 对齐数
char a; // 1 8 1
short int b;// 2 8 2char c;// 1 8 1
int d;// 4 8 4
char e;// 1 8 1
}s;void main() {
printf("%zd", sizeof(s));
}
无论第一个成员a的大小为多少个字节,总是对齐到偏移量为0的地方,b的对齐数为2,对齐到2的整数倍地址处,因此必须同a间隔一格才能满足条件,后面的c、d的存放方法也一样,e填在13的位置满足e的对齐规则,但整个结构体的大小必须为成员最大对齐数的整数倍,最大对齐数为4,所以应是4的倍数。s的大小为16个字节。
若要修改默认对齐数可以通过#pragma pack(n)将默认对齐数设置为n,通过#pragma pack()恢复默认对齐数。
#pragma pack(1)
struct name {//成员大小 默认对齐数 对齐数
char a; // 1 1 1
short int b;// 2 1 1char c;// 1 1 1
int d;// 4 1 1
char e;// 1 1 1
}s;#pragma pack()
void main() {
printf("%zd", sizeof(s));
}
内存对齐的优点有:提高可移植性,提高访问内存的效率,节省了读取时间。
完。