结构体、结构体内存对齐

1、结构体

1.1、概述

在C语言中,除了常见的基本数据类型(整数类型short、int、long和浮点类型float、double)外,还有派生类型,如指针类型、数组类型、结构类型、共用体类型等。
结构体是基本数据类型不能满足需求时,用户自己指定的一种数据结构,由不同类型的数据组合成一个整体,以便引用,这些组合在一个整体中的数据是互相联系的,这样的数据结构称为结构体。

声明一个结构休类型的一般形式如下:

struct 结构体名
{成员列表};

结构体名,用作结构体类型的标志,它又称 结构体标记,大括号内是该结构体中的各个成员,由它们组成一个结构体,对各成员都应进行类型声明如:

类型名 成员名;

也可以成员列表称为域表,第一个成员也称为结构体中的一个域。成员名定名规则与定义变量名规则相同。

struct student
{
    int num;
    char name[20];
    char sex;
    int age;
    float score;
    char addr[30];
};

1.2、定义结构体类型变量的方法

前面只是指定了一个结构体类型,它相当于一个模型,但其中并无具体数据,系统对之也不分配实际内存单元,为了能在程序中使用结构类型的数据,应当定义结构体类型的变量,并在其中存放具体的数据,可以采取以下3种方法定义结构体类型变量。

(1)先声明结构体类型再定义变量名

如上面已定义了一个结构体类型 struct student,可以用它来定义变量。如:

struct student{  //结构体类型名
    ...
    ...
    ...
}student1, student2 //结构体变量名

定义了 student1, student2 为 struct student 类型的变量。

(2)在声明类型的同时定义变量

例如:

struct student
{
    int num;
    char name[20];
    char sex;
    int age;
    float score;
    char addr[30];
}student1, student2;

它的作用与第一种方法相同,即定义了两个 struct student 类型的变量 student1, student2 这种形式的定义的一般形式为

struct 结构体名
{
    成员表列
}变量名表列;

(3)直接定义结构类型变量

其一般形式为:

struct
{
    成员表列
}变量名表列;

即不出现结构体名。

关于结构体类型,有几点要说明:

  1. 类型与变量是不同的概念,不是混同,只能对变量赋值,存取或运算,而不能对一个类型赋值,存取或运算。在编译时,对类型是不分配空间的,只对变量分配空间。
  2. 对结构体中的成员(即 域)可以单元使用,它的作用与地位相当于普通变量。
  3. 成员也可以是一个结构体变量。如:
struct date // 声明一个结构体类型
{
    int month;
    int day;
    int year;
}

struct student
{
    int num;
    char name[20];
    char sex;
    int age;
    struct date birthday;
    char addr[30];
}student1, student2;

先声明一个 struct date 类型,它代表 日期 包括3个成员 month, day, year。然后在声明 struct student 类型时,将成员 birthday 指定为 struct date 类型。

  1. 成员名可以与程序中的变量名相同,二者不代表同一对象。

1.3、结构体变量的引用

1.4、结构体变量的初始化

1.5、结构体数组

1.6、指向结构体类型数据的指针

一个结构体变量的指针就是该变量所占据的内存段的起始地址,可以设一个指针变量,用来指向一个结构体变量,此时该指针变量的值是结构体变量的起始地址。指针变量也可以用来指向结构体数组中的元素。

2、结构体内存对齐

2.1、简述

有没有想过一个问题,某些时候我想4字节对齐,有些时候我又想1字节或者8字节对齐,那么怎么解决这个问题呢?
此时,#pragma pack(push) 和#pragma pack(pop) 以及#pragma pack()应运而生。
#pragma pack(push)
#pragma pack(n) //自定义按n字节对齐
……
……//结构体
……
#pragma pack() //取消自定义的对齐方式,恢复默认对齐。
或者使用
#pragma pack(pop) //pop会让编译器回到push之前的对齐方式

注意,#pragma pack() 取消自定义对齐方式,恢复默认方式,而push之后pop是回到push指令之前的对齐方式。

2.5、#pragma pack()语法

语法:

#pragma pack( [show] | [push | pop] [, identifier], n )

说明:
1,pack提供数据声明级别的控制,对定义不起作用;
2,调用pack时不指定参数,n将被设成默认值;
3,一旦改变数据类型的alignment,直接效果就是占用memory的减少,但是performance会下降;

语法具体分析:
1,show:可选参数;显示当前packing aligment的字节数,以warning message的形式被显示;
2,push:可选参数;将当前指定的packing alignment数值进行压栈操作,这里的栈是the internal compiler stack,同时设置当前的packing alignment为n;如果n没有指定,则将当前的packing alignment数值压栈;
3,pop:可选参数;从internal compiler stack中删除最顶端的record;如果没有指定n,则当前栈顶record即为新的packing alignment数值;如果指定了n,则n将成为新的packing aligment数值;如果指定了identifier,则internal compiler stack中的record都将被pop直到identifier被找到,然后pop出identitier,同时设置packing alignment数值为当前栈顶的record;如果指定的identifier并不存在于internal compiler stack,则pop操作被忽略;
4,identifier:可选参数;当同push一起使用时,赋予当前被压入栈中的record一个名称;当同pop一起使用时,从internal compiler stack中pop出所有的record直到identifier被pop出,如果identifier没有被找到,则忽略pop操作;
5,n:可选参数;指定packing的数值,以字节为单位;

注意:

#pragma pack(push) 
#pragma pack(4)	
//上面两句代码等价于下面这句代码:
#pragma pack(push,4) 

3、结构体经典面试题

(1)、什么是结构体?
(2)、一般在什么情况下用到结构体?
(3)、什么是结构体内存对齐?为什么要对齐?怎样对齐?
(4)、对齐参数如何设置?可以设置为按照任意字节数对齐吗?
(5)、如何知道结构体某个成员相对于结构体起始位置的偏移量?

参考资料:
http://www.runoob.com/w3cnote/c-structures-intro.html
https://blog.csdn.net/dai_wen/article/details/78304568
https://www.cnblogs.com/yangguang-it/p/7392726.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值