实验:使⽤GDB查看结构体在内存中的存储⽅式
结构体在内存中的表⽰形式是怎么样的?
sizeof结构体大小结构体在内存中和普通变量存储没有太⼤的区别。
⾸先我们看看,计算机如何读取普通变量:
  普通变量例如int是占据4个字节,计算机读内存的时候会从起始地址开始读,读4个字节,按照int的规则将⼆进制转化为整形。所以读取普通变量我们要知道起始地址和数据类型(占据长度,解读⽅式)。
再看看计算机如何读取结构体变量:
  结构体是⾃定义变量,是由多个普通变量组成的。我们读取结构体变量,实际上是读取结构体包含的数据成员。例如结构体T包含三个数据成员:char var1,int var2,long var3。计算机如果读取结构体变量 t 的数据成员var1,计算机需要知道结构体变量的地址 &t,已知这个结构体变量占据16个字节,那么从起始地址开始往后16个字节,都存储了结构体变量的数据成员。如果我们再知道数据成员var1相对于结构体起始地址的偏移,我们就可以像读取普通变量⼀样读取结构体数据成员。
#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码。
再次步进,给第⼆个结构体变量赋值,再次查看内存。
(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赋值,查看内存验证我们的想法。
(gdb) step #执⾏下⼀条语句
(gdb) x/24xb &t1
显然我们的猜测是正确的!

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。