欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

结构体的定义声明、内存对齐

程序员文章站 2024-03-23 10:28:04
...

什么是结构体?结构体怎么进行定义声明?
结构体是一种数据结构,可以被声明为数组、指针和变量,结构体内部通常是由多个相同或不同类型的变量组成,举几个例子来说明理解它的定义声明,如下:

1.struct student{ 
//struct是结构体关键字,student是结构体标志
    int age;
    char name[20];
    double a;
    float b; //这四个变量为结构体成员,
    }s1; //s1是结构体声明的变量

2.struct student{ 
    int age;
    char name[20];
    double a;
    float b;
    };//只有标签无声明变量
struct student t1,t2;//用结构体标签声明t1、t2两个变量

3.typedef struct simple{ 
    int age;
    char name[20];
    }s1;//使用typedef创建结构体
 s1 t1;
 s1 *head;
 //结构体变量s1定义变量t1和指针head

结构体中也可以包含别的结构体:

struct complex{
     char number[20];
     struct student t1;
     };

结构体中包含指向自己的指针:

struct complex{
     char number[20];
     struct complex *head;
     };

结构体间互相包含时,需要对其中之一进行不完整声明:

struct B; 
struct A {     
    int a;     
    struct B* pb; 
    }; 
struct B {     
    int _b;     
    struct A* pa; 
    }; 

在程序中正确合适地使用结构体会方便很多;
接下来说一说计算结构体大小的问题,这就必须得掌握结构体的内存对齐规则,看下面:

1.首先,为什么会存在内存对齐问题呢?
在我看到的资料上是这样的解释:
a)平台原因(移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常;
b)性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐,原因在于为了访问未对齐的内存,处理器需要作两次内存访问,而对齐的内存访问仅需要一次访问。
2.看看内存对齐的规则是怎样描述的:
第一种理解如下:
a) 第一个成员在与结构体变量偏移量为0的地址处;
b) 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处;
对齐数 = 编译器默认的一个对齐数与该成员大小的较小值;
VS对齐数是8,Linux是4
c)结构体总大小为最大对齐数(每个成员变量除了第一个成员都有一个对齐数)的整数倍。
如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
(2)第二种:
a) 结构体第一个成员的地址和结构体的首地址相同;
b) 结构体每个成员地址相对于结构体首地址的偏移量是该成员大小的整数倍,如果不是则编译器会在成员之间添加填充字节;
c) 结构体总的大小要是其成员中最大size的整数倍,如果不是编译器会在其末尾添加填充字节。

现在举几个例子看看结构体大小是怎么计算的(在vs下):

struct S1 {     
      char c1;     
      int i; 
      char c2;    
};

第一个成员‘char c1’,大小是1字节,偏移量是0;
‘int i’大小是4字节,偏移量为4,所以在其之前填充3字节;‘
‘char c2’大小1字节,不需要填充。
1+3+4+1=9,因为结构体大小要是成员中的最大字节的整数倍,所以结构体大小为12

struct S2 {     
    char c1;     
    char c2;     
    int i; 
    }; 

‘char c1’是1字节;
‘char c2’是1字节;
‘int i’是4字节,偏移量要为其整数倍,所以其之前填充2字节;
1+1+2+4=8,恰好为int类型的整数倍,所以结构体大小为8

struct s3{
    char ch;
    int a;
    double b;
    char c1;
};

‘char ch’为1字节;
‘int a’是4字节,偏移量为4,则填充3字节;
‘double b ‘是8字节,因为其之前是int类型,所以不需要填充字节了;
‘char c1’是1字节;
1+3+4+8+1=17,不是double类型大小的整数倍,所以在最后填充7个字节,17+7=24,结构体大小为24

struct s4{
    char ch;
    int a;
    double b;
};

相比上一个例子,成员少了最后的’char c1’,则:1+3+4+8=16,即结构体大小为16

经过上面4个例子的计算,可知道即使两个结构体内部成员相同,但如果它们的先后顺序不一样,结构体的大小也是会有区别的;而且计算时一定要注意偏移量是否计算正确了;
那么在设计结构体的时候,我们既要满足对齐,又要节省空间,可以通过让结构体内部占用空间小的成员尽量放在一起。