结构体在内存中和普通变量存储没有太大的区别。
首先咱们看看,计算机如何读取普通变量:
普通变量例如int是占据4个字节,计算机读内存的时候会从起始地址开始读,读4个字节,按照int的规则将二进制转化为整形。因此读取普通变量咱们要知道起始地址和数据类型(占据长度,解读方式)。c++
再看看计算机如何读取结构体变量:
结构体是自定义变量,是由多个普通变量组成的。咱们读取结构体变量,其实是读取结构体包含的数据成员。例如结构体T包含三个数据成员:char var1,int var2,long var3。计算机若是读取结构体变量 t 的数据成员var1,计算机须要知道结构体变量的地址 &t,已知这个结构体变量占据16个字节,那么从起始地址开始日后16个字节,都存储告终构体变量的数据成员。若是咱们再知道数据成员var1相对于结构体起始地址的偏移,咱们就能够像读取普通变量同样读取结构体数据成员。shell
#include<stdio.h> #pragma pack() #define offset(type, name) (size_t)(&(((type *)0)->name)) typedef struct Test{ char var1; //1 int var2; //4 long var3; //8 char var4; //1 }Test_t; /* 64bit: Test_t: cxxx iiii //在char后面填充,使得后一个变量int从对齐参数的整数倍 llll llll cxxx xxxx //结构体总长度必须为对齐参数的整数倍,所以在结构体尾部填充。 32bit: Test_t: cxxx iiii llll cxxx */ int main(int argc, char** argv){ Test_t t1; t1.var1 = 'A'; t1.var2 = 99; t1.var3 = 999; printf("struct->var1: %ld \n", offset(Test_t, var1)); printf("struct->var2: %ld \n", offset(Test_t, var2)); printf("struct->var3: %ld \n", offset(Test_t, var3)); printf("struct->var4: %ld \n", offset(Test_t, var4)); printf("struct: %ld \n", sizeof(t1)); return 0; }
针对上述测试代码,我使用了GDB调试工具对程序内存进行查看。想看看结构体在内存中的表现形式是怎样的。函数
$ gcc -g -o struct_test struct_test.c $ gdb (gdb) file struct_test (gdb) start
编译,打开gdb,在gdb中加载程序,调用main函数,建立结构体变量,此时下一步是对结构体成员赋值。
咱们在赋值前查看一下此时结构体变量t1在内存的表现是怎样的。工具
(gdb) print &t1 #打印结构体变量t1的地址 (gdb) x/24xb &t1 #读取24次内存,每次读一个字节,以16进制的形式打印
使用print打印告终构体变量t1的地址,经过x/<n/f/u> <addr>的形式查看程序的内存,详情见如下博文。
咱们看到此时的内存杂乱无章。咱们步进程序,给结构体变量var1赋值,再查看一次内存。测试
(gdb) step #执行下一条语句 (gdb) x/24xb &t1 (gdb) x/24db &t1 #以十进制的形式打印内存
咱们很明显发如今0x7ffffffee370的值发生了改变,也就是结构体变量t1所在地址的第一个字节被修改了。结合咱们咱们的赋值语句:给结构体第一个char变量赋值,咱们很容易发现:结构体第一个char变量就在结构体变量t1地址0偏移的地方。十六进制的0x41转换为十进制就是65,正好是A的ascii码。
再次步进,给第二个结构体变量赋值,再次查看内存。.net
(gdb) step #执行下一条语句 (gdb) x/24xb &t1
对比上一次的内存,咱们发现不是第二个字节的值被修改,而是第五个字节的值被修改了。这是结构体存储时候作的字节对齐,给第一个char变量后填充了3个字节,避免第二个变量int横跨边界。十六进制的0x63就是十进制的99。咱们知道int变量占据4个字节,所以下一个long变量存储从0x7ffffffee378开始,长度为8个字节。显然存储采用了小端存储的方式:低字节存储在内存的低地址。
下一条语句是给变量3赋值999,十进制的999对于十六进制的0x3E7,所以咱们猜想下一次内存在0x7ffffffee378开始的前两个字节会被修改。调试
0x7ffffffee378 0xe7 0x03 0x00 0x00 ...
咱们继续步进,给变量3赋值,查看内存验证咱们的想法。code
(gdb) step #执行下一条语句 (gdb) x/24xb &t1
显然咱们的猜想是正确的!blog