(一)结构体的概念:
什么是结构体?
结构体是由一批数据组合而成的一种新的数据类型。组成结构型数据的每个数据称为结构型数据的“成员”。
为什么需要结构体?
C语言中有许多内置类型比如:整形(int,long,char等),浮点型(flaot,double),还有数组,指针,字符串等。但是在实际问题中只有这些数据类型是不够的,比如一个学生的信息就需要学号(字符串),姓名(字符串),年龄(整形)等等,如果零散的定义多个不同类型的变量,来描述一个学生的话使用起来就会很乱。所以我们就需要一个新的数据类型。——结构体,他就将不同类型的数据存放在一起,作为一个整体进行处理。
(二)结构体内存对齐:
(1)什么是结构体内存对齐?
内存地址对齐,是结构体在内存中排列数据(表现为变量的地址)、访问数据(表现为CPU读取数据)的一种方式,包含了两种相互独立又相互关联的部分:结构体成员对齐和结构体整体对齐 。
结构体成员对齐:
对于结构的各个成员,第一个成员位于偏移为0的位置,以后每个数据成员的偏移量必须是min( 默认对齐数,这个数据成员的自身长度 ) 的整数倍。
结构体整体对齐:
在结构体数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,结构体最终的大小是 min(默认对齐数,最大的结构体成员类型长度) 的整数倍。
默认对齐数和编译器有关,VS的默认对齐数 是 8.
举例说明:
struct S1
{
char c1;
int i;
char c2;
};
S1结构体的大小计算: (环境:vs win32默认对齐数 是8)
(1)每个成员对齐:c1是第一个成员,假设地址是x,c1的大小是1个字节。如果不考虑内存对齐,下一个成员从x + 1开始存储。第二个成员i 的大小是 4字节,默认对齐数是8字节, min(sizeof(i) = 4,默认对齐数) = 4。所以i从x + 4的地址开始存储直到 x + 7。第三个成员 c2,min(sizeof(c2), 4) = 1;所以直接存到 x + 8的位置。
(2)结构体整体对齐:经过结构体成员对齐后, 结构体的大小是(地址x ~ 地址x + 8)的内存块 = 9字节;min(8, 最大数据类型成员的大小 = 4); 9 不是 4的整数倍 所以补三个字节。最终结构体的大小是12。
(2)为什么需要结构对齐?
(1)为了平台的可移植性,不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
(2)为了性能:在我们看来,内存的是一个字节一个字节组成的,但是CPU对内存的存取操作是以块为最小单位的。快的大小可以是2,4,8。也就是2的整数次幂。假如块的大小是4. 我们看下面的结构体。
struct S2{
char c;
int a;
}
将a变量取到寄存器中对应的操作:
如果不考虑内存对齐的话,设c的地址是 0。a变量占的地址空间是1234.而CPU要取出 a的值,需要两次访问内存,分别将第一块 (0123) 和第二块 (4567)的数据都取出来。然后将(1234)合并得到a的值。
如果考虑内存对齐的话,a对应的内存空间是(4567),cpu只需要访问一次内存空间,便可直接取出a的值。这样做,虽然浪费了3个字节的空间,但是提高了效率。所以 内存对齐就是一种用空间换时间的过程。
(3)求结构体成员相对于起始位置的偏移量。
求结构体某成员相对起始位置的偏移量,可以用 offsetof(TYPE, MEMBER) 宏来实现。
例如:
struct S1
{
char c1;
char c2;
double i;
};
int main(){
cout << offsetof(struct S1, i) << endl;
return 0;
}
上边的代码输出结果是8.
offsetof(TYPE, MEMBER)是如何实现的?
#define offsetof(TYPE, MEMBER) (size_t)& ( (TYPE*) 0)->MEMBER
这段代码的含义是将,内存从零开始的一段数据 当做struct S1来处理。相应的找到MEMBER成员的位置。也就是他的偏移量。