日期:2014-05-16  浏览次数:20768 次

《coredump问题原理探究》Linux x86版5.5节C风格数据结构内存布局之基本数据类型构成的结构体

在C语言里,结构体是不同类型数据结构的集合。由于结构体比较复杂,分为几种情况来讨论:

1. 成员全是由基本数据类型构成

2. 成员有基本数据类型,也有其它结构体构成,构成成员的结构体是1,2两种情况。

3. 结构体构成的数组


这一节先讨论第一种情况.要了解这种类型结构体的内存布局,首先要知道成员在内存里是如何排序,及各个成员在内存占的大小。先看一个例子

 #include <stdio.h>
 struct xuzhina_dump_c05_s3_stru
 {
     char a;
     short b;
     int c;
     long d;
     float e;
     double f;
     void* g;
 };
 
 int main()
 {
     struct xuzhina_dump_c05_s3_stru test;
     printf( "addr:%x, 1st:%x, 2nd:%x, 3rd:%x, 4th:%x, 5th:%x, 6th:%x, 7th:%x
    ",
             &test, &test.a, &test.b, &test.c, &test.d, &test.e, &test.f, &te
    st.g );
     return 0;
 }

运行结果:

addr:bf909eb4, 1st:bf909eb4, 2nd:bf909eb6, 3rd:bf909eb8, 4th:bf909ebc, 5th:bf909ec0, 6th:bf909ec4, 7th:bf909ecc

可得这样的结论:

1.        结构体对象的地址和第一个成员地址一样。也就是所谓的基地址

2.        第一个成员a和第二个成员b分别是char,short,不足dword大小,为了内存对齐,挤一个dword里。int, long, float, double,指针由于大小分别是4,4,4,8,4,直接按照4字节进行内存对齐,也分别占4,4,4,8,4个字节的空间.

3.        各个成员还是按照结构体的声明顺序由低到高排列。

4.        每个成员的地址都是它前面所有成员的大小(包括内存对齐)和基地址的累加。像test.d的地址就是bf909eb4+0x8 = bf909ebc.它前面的成员a ,b在一个dword, c占一个dword,刚好是8.

可能成员类型会由于内存对齐而分辨不出来。其实,用5.1节的方法是可以分辨得出来。有兴趣可以尝试一下。

那么一个结构体如何从汇编里识别出来呢,它究竟有什么特征?

先看一下这个例子。

 struct xuzhina_dump_c05_s3_stru
 {
     char a;
     short b;
     int c;
 };
 
 bool equal( struct xuzhina_dump_c05_s3_stru* first,
         struct xuzhina_dump_c05_s3_stru* second )
 {
     if ( first->a != second->a )
     {
         return false;
     }
     if ( first->b != second->b )
     {
         return false;
     }
     if ( first->c != second->c )
     {
         return false;
     }
     return true;
 }
 
 int main()
 {
     struct xuzhina_dump_c05_s3_stru first = { 'a', 10, 20 };
     struct xuzhina_dump_c05_s3_stru second = {'a', 10, 21 };

     if ( equal( &first, &second ) )
     {
         return 0;
     }
 
     return 1;
 }

汇编代码

(gdb) disassemble equal                                                       
Dump of assembler code for function _Z5equalP24xuzhina_dump_c05_s3_struS0_:
   0x08048470 <+0>:     push   %ebp
   0x08048471 <+1>:     mov    %esp,%ebp
   0x08048473 <+3>:     mov    0x8(%ebp),%eax
   0x08048476 <+6>:     movzbl (%eax),%edx
   0x08048479 <+9>:     mov    0xc(%ebp),%eax
   0x0804847c <+12>:    movzbl (%eax),%eax
   0x0804847f <+15>:    cmp    %al,%dl

   0x08048481 <+17>:    je     0x804848a <_Z5equalP24xuzhina_dump_c05_s3_struS0_+26>

   0x08048483 <+19>:    mov    $0x0,%eax
   0x08048488 <+24>:    jmp    0x80484c0 <_Z5equalP24xuzhina_dump_c05_s3_struS0_+80>

   0x0804848a <+26>:    mov    0x8(%ebp),%eax

   0x0804848d <+29>:    movzwl 0x2(%eax),%edx
   0x08048491 <+33>:    mov    0xc(%ebp),%eax
   0x08048494 <+36>:    movzwl 0x2(%eax),%eax
   0x08048498 <+40>:    cmp    %ax,%dx
   0x0804849b <+43>:    je     0x80484a4 <_Z5equalP24xuzhina_dump_c05_s3_struS0_+52>
   0x0804849d <+45>:    mov    $0x0,%eax
   0x080484a2 <+50>:    jmp    0x80484c0 <_Z5equalP24xuzhina_dump_c05_s3_struS0_+80>

   0x080484a4 <+52>:    mov    0x8(%ebp),%eax

   0x080484a7 <+55>:    mov    0x4(%eax),%edx
   0x080484aa <+58>:    mov    0xc(%ebp),%eax
   0x080484ad <+61>:    mov    0x4(%eax),%eax
   0x080484b0 <+64>:    cmp    %eax,%edx

   0x080484b2 <+66>:    je     0x80484bb <_Z5equalP24xuzhina_dump_c05_s3_struS0_+75>

   0x080484b4 <+68>:    mov