结构体(含结构体内存对齐的讲解)

结构体的声明和初始化

一个结构体内部可以包含多种类型的数据,这样可以灵活的描述一个多元对象,如形态、颜色各异的车。需要注意的是:声明一种结构体类型时不要同时对里面的结构体成员初始化,这是不允许的。

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);
}

出现的原因是内存存储结构体的一种特殊规则:内存对齐。

  1. 第一个结构体成员的地址对齐到和结构体变量起始位置偏移量为0的位置。
  2. 其他结构体成员的地址对齐偏移量为距起始位置的某个数字(对齐数)的整数倍位置。对齐数=编译器的默认对齐数和该成员大小相比,数值较小的那个。 这里的测试在VS2022里进行的,默认对齐数为8。
  3. 整个结构体的大小必须为结构体成员里的最大对齐数的整数倍。
  4. 若结构体里面嵌套了结构体,里面的嵌套结构体的大小为该结构体成员的最大对齐数。

以下示例:

struct name {//成员大小   默认对齐数   对齐数
    char a; //        1                     8                1
    short int b;//   2                     8                2

    char 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                1

    char c;//         1                     1                1

     int d;//           4                     1                1

     char e;//        1                     1                1
}s;

#pragma pack()

void main() {
    printf("%zd", sizeof(s));
}

内存对齐的优点有:提高可移植性,提高访问内存的效率,节省了读取时间。

完。 

  • 20
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值