程序的内存分配—-变量在可执行文件中的内存区分配

 

一、 预备知识—程序的内存分配

  一个由c/C++编译的程序占用的内存分为以下几个部分:

    1. 程序代码区(.text)    —    存放函数体的二进制代码  
    2. 文字常量区(.rodata)   —   常量字符串就是放在这里的, 程序结束后由系统释放。
    3. 全局区(静态区)(static)—  全局变量 静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域(.data),未初始化的全局变量和未初始化的静态变量在相邻的另一块区域(.bss)。 - 程序结束后由系统释放。
    4. 堆区(heap)—  一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意: 它与数据结构中的堆是两回事,分配方式倒                                         是类似于链表。
    5. 栈区(stack)—  由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。

 

二、 例子程序


        //main.cpp
        int a = 0;                // 全局初始化区
        char *p1;                // 全局未初始化区
        main()
        {
           int b;                  // 栈区
           char s[] = "abc";     // 栈区
           char *p2;        // 栈区
           char *p3 = "123456";     // p3在栈区;   "123456\0" 在常量区, 

           static int c =0;           // 全局(静态)初始化区 
           p1 = (char *)malloc(10);
           p2 = (char *)malloc(20); // 分配得来的10和20字节的区域就在堆区 
           strcpy(p1, "123456");    // "123456\0" 放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。
        }

 

 =================================================================================

  C语言变量分配的实例分析:
   

  在可执行文件中,变量会被分配在哪些区里?这里以可执行文件为例子,可执行文件有固定的内存加载地址,符号(函数/变量的名字)将来在内存里的地址连接器是可以提前确定的。
   源程序编译连接的结果是形成一堆汇编指令代码,大致分为.text .data .bss等几个section。

  对于.exe文件和.so文件

  全局和静态变量都放在.data 或.bss段 (gas把源文件从头到尾扫描1遍,才知道一个变量的全部情况:是否定义;类型;是否初始化。然后把初始化的变量在.data段里分配位置和空间,把没初始化的变量在.bss段里分配位置和空间,没定义的变量分配在.undef段)。

  汇编指令代码里全局变量表现为一个内存地址(全局变量在目标文件里是一个偏移值,加载进内存里是一个内存地址)。临时变量在汇编代码里变成ebp/esp+n,表现为一个堆栈地址,化为程序正文(.text)的一部分。有些变量的最终内存地址在加载进内存之前还不能确定,需要加载进内存才可以计算出来.
    全局变量 作用域是跨越多个源程序的。因此全局变量不能重名。静态变量作用域是位于单个源程序内。多个源程序可以有同名的全局静态变量。本例中,为了区分多个同名的静态变量,gcc 用 c444和c444.0 来加以区别。

 

[test@redhat]# more aaa.c
       # include <stdio.h>
        int a111 = 0;                  # 全局变量 已初始化
        char *p111 = "654321";       # 全局指针变量 已经初始化
        static int c444 = 9;           # 静态全局变量 已经初始化
        static int c555;               # 静态全局变量 未初始化
        main()
        { 
            int b222;                  # 局部变量
            char s333[] = "abc";    # 局部变量
            char *p222;                # 局部变量
            char *p333 = "123456";     # 局部变量
            static int c444 =0;         # 已初始化静态局部变量,与前面静态全局变量重名
            p111 = (char *)malloc(10); 
            p222 = (char *)malloc(20);
            strcpy(p111, "123456");
        }

 

----------------------------------------------------------------------------------------------------------------

文件./aaa加载进内存后,再看看变量的地址以及所在的区: (其中的符号,变量,函数 见下面列表)
[test@redhat]# gdb ./aaa
                                             GNU gdb 6.1.1
                                             (gdb) disassemble main
                                             Dump of assembler code for function main:
0x0804838c <main+0> :     push   %ebp
0x0804838d <main+1>:      mov    %esp,%ebp
0x0804838f <main+3> :     sub    $0x18,%esp
0x08048392 <main+6>:     and    $0xfffffff0,%esp
0x08048395 <main+9> :    mov    $0x0,%eax
0x0804839a <main+14>:   sub    %eax,%esp

------------------------------------------------------------------------------------
                                        # char s333[] = "abc";
0x0804839c <main+16>:   mov    0x80484df,     %eax                     # 0x80484df处为"abc",位于.rodata
0x080483a1 <main+21>:   mov    %eax,          0xfffffff8(%ebp)        # 0xfffffff8(%ebp) 为局部变量 char s333[]

------------------------------------------------------------------------------------
                                        # char *p333 = "123456";
0x080483a4 <main+24>:   movl   $0x80484e3,  0xfffffff0(%ebp)       # 0x80484e3处为"123456/0",位于.rodata; 0xfffffff0(%ebp) 为局部

                                                                                                    变量 char *p333

------------------------------------------------------------------------------------
                                        # p111 = (char *)malloc(10);
0x080483ab <main+31>:   sub    $0xc,       %esp
0x080483ae <main+34>:   push   $0xa                                         # 0xa=10 ; push $0xa后,此时堆栈esp值又减去4字节,相当于sub 、

                                                                                                                $0x10,%esp
0x080483b0 <main+36>:   call   0x804829c <malloc>
0x080483b5 <main+41>:   add   $0x10,      %esp
0x080483b8 <main+44>:   mov   %eax,      0x8049500                   # 0x8049500 为全局变量p111,位于.data

------------------------------------------------------------------------------------
                                        # p222 = (char *)malloc(20);
0x080483bd <main+49>:   sub    $0xc,        %esp
0x080483c0 <main+52>:   push   $0x14                                        # 0x14=20 ; push $0xa后,此时堆栈esp值又减去4字节,相当于sub

                                                                                                                  $0x10,%esp
0x080483c2 <main+54>:   call     0x804829c <malloc>
0x080483c7 <main+59>:   add    $0x10,       %esp
0x080483ca <main+62>:   mov    %eax,       0xfffffff4(%ebp)          # 0xfffffff4(%ebp) 为局部变量p222

------------------------------------------------------------------------------------
                                        # strcpy(p111, "123456");
0x080483cd <main+65>:   sub    $0x8,   %esp
0x080483d0 <main+68>:   push   $0x80484e3                               # 0x80484e3处内容为"123456/0",位于.rodata;
0x080483d5 <main+73>:   pushl  0x8049500                                 # 0x8049500 为全局变量p111,位于.data
0x080483db <main+79>:   call     0x80482bc <strcpy>
0x080483e0 <main+84>:   add    $0x10,   %esp

------------------------------------------------------------------------------------
0x080483e3 <main+87>:   leave 
0x080483e4 <main+88>:   ret   
0x080483e5 <main+89>:   nop   
0x080483e6 <main+90>:   nop   
0x080483e7 <main+91>:   nop   
0x080483e8 <main+92>:   nop   
0x080483e9 <main+93>:   nop   
0x080483ea <main+94>:   nop   
0x080483eb <main+95>:   nop   
0x080483ec <main+96>:   nop   
0x080483ed <main+97>:   nop   
0x080483ee <main+98>:   nop   
0x080483ef <main+99>:   nop   
                                        End of assembler dump.
(gdb) q
[test@redhat]#

---------------------------------------------------------------------------------------------------------------------------

[test@redhat]# readelf -a ./aaa

ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
  Class:                               ELF32
  Data:                                2's complement, little endian
  Version:                              1 (current)
  OS/ABI:                             UNIX - System V
  ABI Version:                        0
  Type:                                EXEC (Executable file)
  Machine:                             Intel 80386
  Version:                            0x1
  Entry point address:               0x80482d0
  Start of program headers:       52 (bytes into file)
  Start of section headers:         2040 (bytes into file)
  Flags:                               0x0
  Size of this header:                52 (bytes)
  Size of program headers:         32 (bytes)
  Number of program headers:    7
  Size of section headers:          40 (bytes)
  Number of section headers:     27
  Section header string table index: 24

Section Headers:   Addr是文件加载进内存时,每个section在内存中的虚拟地址
  [Nr] Name                 Type               Addr         Off         Size       ES     Flg   Lk  Inf  Al

-------------------------------------------------------------------------------------------------------------------------
  [ 0]                         NULL             00000000   000000   000000   00            0    0   0
  [ 1] .interp             PROGBITS        08048114     000114      000013     00      A    0    0   1
  [ 2] .note.ABI-tag     NOTE              08048128       000128      000020     00      A    0    0   4
  [ 3] .hash                  HASH              08048148   000148   00002c    04    A    4    0   4
  [ 4] .dynsym            DYNSYM          08048174   000174   000060    10  A    5    1   4
  [ 5] .dynstr              STRTAB           080481d4   0001d4   000053   00     A    0    0   1
  [ 6] .gnu.version         VERSYM          08048228   000228   00000c   02     A    4    0   2
  [ 7] .gnu.version_r      VERNEED         08048234   000234   000020   00    A    5     1   4
  [ 8] .rel.dyn            REL               08048254    000254   000008   08   A    4    0   4
  [ 9] .rel.plt             REL               0804825c    00025c     000018   08     A    4    b   4
  [10] .init                PROGBITS        08048274    000274     000017    00     AX   0   0   4
  [11] .plt                 PROGBITS        0804828c      00028c       000040      04     AX   0   0  4
  [12] .text               PROGBITS        080482d0    0002d0       0001e4    00     AX   0   0  16
  [13] .fini                PROGBITS        080484b4    0004b4      00001b    00    AX   0   0  4
  [14] .rodata             PROGBITS        080484d0    0004d0    00001a   00    A     0   0  4
  [15] .eh_frame           PROGBITS        080484ec   0004ec    000004   00    A     0   0  4
  [16] .data                PROGBITS        080494f0   0004f0     00001c       00   WA    0   0  4
  [17] .dynamic             DYNAMIC         0804950c     00050c      0000c8   08   WA   5   0  4
  [18] .ctors              PROGBITS        080495d4   0005d4    000008    00   WA   0   0  4
  [19] .dtors              PROGBITS        080495dc   0005dc    000008   00   WA   0   0  4
  [20] .jcr                 PROGBITS        080495e4   0005e4    000004   00   WA   0   0  4
  [21] .got                PROGBITS        080495e8   0005e8    00001c   04   WA   0   0  4
  [22] .bss                NOBITS           08049604   000604     000008   00   WA   0   0  4
  [23] .comment            PROGBITS       00000000   000604     000126   00         0   0  1
  [24] .shstrtab           STRTAB           00000000   00072a     0000ce   00        0   0  1
  [25] .symtab             SYMTAB          00000000   000c30     0004c0   10       26  2f  4
  [26] .strtab             STRTAB           00000000   0010f0     000275   00        0   0  1

------------------------------------------------------------------------------------------------------------------------

Program Headers:

------------------------------------------------------------------------------------------------------------------------
  Type               Offset            VirtAddr              PhysAddr         FileSiz          MemSiz     Flg       Align
  PHDR            0x000034        0x08048034      0x08048034      0x000e0        0x000e0      R E       0x4
  INTERP          0x000114        0x08048114      0x08048114     0x00013         0x00013      R         0x1

      [Requesting program interpreter: /lib/ld-linux.so.2]
  LOAD            0x000000        0x08048000       0x08048000      0x004f0        0x004f0       R E      0x1000
  LOAD            0x0004f0         0x080494f0       0x080494f0       0x00114       0x0011c       RW      0x1000
  DYNAMIC       0x00050c        0x0804950c       0x0804950c       0x000c8       0x000c8       RW      0x4
  NOTE            0x000128        0x08048128       0x08048128      0x00020       0x00020       R         0x4
  STACK           0x000000        0x00000000       0x00000000      0x00000       0x00000       RW      0x4

------------------------------------------------------------------------------------------------------------------------
 Symbol table '.symtab' contains 76 entries:
对于.exe文件,符号的Value 是将来加载进内存中的真实地址;对于.so文件Value需要重定位.
   Num:    Value        Size    Type      Bind             Vis         Ndx              Name

----------------------------------------------------------------------------------------------------------------
     0:     00000000     0     NOTYPE      LOCAL     DEFAULT  UND
     1:     08048114     0     SECTION     LOCAL     DEFAULT    1
     2:     08048128     0     SECTION     LOCAL     DEFAULT    2
     3:     08048148     0     SECTION     LOCAL     DEFAULT    3
     4:     08048174     0     SECTION     LOCAL     DEFAULT    4
     5:     080481d4     0     SECTION     LOCAL     DEFAULT    5
     6:     08048228     0     SECTION     LOCAL     DEFAULT    6
     7:     08048234     0     SECTION     LOCAL     DEFAULT    7
     8:     08048254     0     SECTION     LOCAL     DEFAULT    8
     9:     0804825c     0     SECTION     LOCAL     DEFAULT    9
    10:    08048274     0     SECTION     LOCAL     DEFAULT   10
    11:    0804828c     0     SECTION     LOCAL     DEFAULT   11
    12:    080482d0     0     SECTION     LOCAL     DEFAULT   12
    13:    080484b4     0     SECTION     LOCAL     DEFAULT   13
    14:    080484d0     0     SECTION     LOCAL     DEFAULT   14
    15:    080484ec     0     SECTION     LOCAL     DEFAULT   15
    16:    080494f0     0     SECTION     LOCAL     DEFAULT   16
    17:    0804950c     0     SECTION     LOCAL     DEFAULT   17
    18:    080495d4     0     SECTION     LOCAL     DEFAULT   18
    19:    080495dc     0     SECTION     LOCAL     DEFAULT   19
    20:    080495e4     0     SECTION     LOCAL     DEFAULT   20
    21:    080495e8     0     SECTION     LOCAL     DEFAULT   21
    22:    08049604     0     SECTION     LOCAL     DEFAULT   22
    23:    00000000     0     SECTION     LOCAL     DEFAULT   23
    24:    00000000     0     SECTION     LOCAL     DEFAULT   24
    25:    00000000     0     SECTION     LOCAL     DEFAULT   25
    26:    00000000     0     SECTION     LOCAL     DEFAULT   26
    27:    080482f4      0     FUNC         LOCAL      DEFAULT   12        call_gmon_start
    28:    00000000     0     FILE           LOCAL      DEFAULT  ABS       crtstuff.c
    29:    080495d4     0     OBJECT       LOCAL     DEFAULT   18        __CTOR_LIST__
    30:    080495dc     0     OBJECT       LOCAL     DEFAULT   19        __DTOR_LIST__
    31:    080484ec     0     OBJECT       LOCAL     DEFAULT   15        __EH_FRAME_BEGIN__
    32:    080495e4     0     OBJECT       LOCAL     DEFAULT   20        __JCR_LIST__
    33:    080494f8     0     OBJECT        LOCAL     DEFAULT   16        p.0
    34:    08049604     1     OBJECT      LOCAL      DEFAULT   22        completed.1
    35:    08048320     0     FUNC          LOCAL     DEFAULT   12        __do_global_dtors_aux
    36:    08048360     0     FUNC          LOCAL     DEFAULT   12        frame_dummy
    37:    00000000     0     FILE           LOCAL     DEFAULT  ABS        crtstuff.c
    38:    080495d8     0     OBJECT       LOCAL     DEFAULT   18        __CTOR_END__
    39:    080495e0     0     OBJECT       LOCAL     DEFAULT   19        __DTOR_END__
    40:    080484ec     0     OBJECT       LOCAL     DEFAULT   15        __FRAME_END__
    41:    080495e4     0     OBJECT       LOCAL     DEFAULT   20        __JCR_END__
    42:    08048490     0     FUNC          LOCAL     DEFAULT   12        __do_global_ctors_aux
    43:    00000000     0     FILE           LOCAL     DEFAULT  ABS        aaa.c
    44:    08049504     4     OBJECT       LOCAL     DEFAULT   16        c444   # static变量为LOCAL绑定属性(也即作用域) 已初始化静态变量存放

                                                                                                            在.data
    45:    08049508     4     OBJECT       LOCAL     DEFAULT   16        c444.0 # 已初始化静态变量存放在.data (多个源文件可以定义同名的静态

                                                                                                             变量)
    46:    08049608     4     OBJECT       LOCAL     DEFAULT   22        c555   # 未初始化静态变量存放在.bss
    47:    080494fc      4     OBJECT       GLOBAL   DEFAULT   16        a111   # 全局变量为GLOBAL绑定属性  已初始全局变量存放在.data
    48:    0804950c     0     OBJECT       GLOBAL   DEFAULT   17        _DYNAMIC
    49:    080484d0     4     OBJECT       GLOBAL   DEFAULT   14        _fp_hw
    50:    080494f0     0     NOTYPE      GLOBAL    HIDDEN  ABS        __fini_array_end
    51:    080494f4     0     OBJECT       GLOBAL    HIDDEN   16        __dso_handle
    52:    08048440    66    FUNC         GLOBAL    DEFAULT   12        __libc_csu_fini
    53:    08048274     0     FUNC         GLOBAL    DEFAULT   10        _init
    54:    0804829c   427    FUNC         GLOBAL    DEFAULT  UND       malloc@@GLIBC_2.0    

    55:    080482d0     0    FUNC          GLOBAL    DEFAULT   12        _start
    56:    080494f0     0     NOTYPE      GLOBAL    HIDDEN  ABS        __fini_array_start
    57:    080483f0    71     FUNC         GLOBAL    DEFAULT   12        __libc_csu_init
    58:    08049500     4     OBJECT       GLOBAL    DEFAULT   16        p111
    59:    08049604     0     NOTYPE      GLOBAL    DEFAULT  ABS        __bss_start
    60:    0804838c    89     FUNC         GLOBAL    DEFAULT   12        main
    61:    080482ac   251    FUNC         GLOBAL    DEFAULT  UND        __libc_start_main@@GLIBC _
    62:    080494f0     0     NOTYPE      GLOBAL   HIDDEN    ABS        __init_array_end
    63:    080494f0     0     NOTYPE      WEAK      DEFAULT   16        data_start
    64:    080484b4     0     FUNC         GLOBAL   DEFAULT   13        _fini
    65:    080494f0     0     NOTYPE      GLOBAL   HIDDEN   ABS        __preinit_array_end
    66:    08049604     0     NOTYPE     GLOBAL   DEFAULT  ABS        _edata
    67:    080495e8     0     OBJECT      GLOBAL   DEFAULT   21        _GLOBAL_OFFSET_TABLE_
    68:    0804960c     0     NOTYPE      GLOBAL   DEFAULT  ABS        _end
    69:    080494f0     0     NOTYPE      GLOBAL   HIDDEN  ABS        __init_array_start
    70:    080484d4     4     OBJECT      GLOBAL   DEFAULT   14        _IO_stdin_used
    71:    080494f0     0     NOTYPE      GLOBAL   DEFAULT   16        __data_start
    72:    00000000     0     NOTYPE      WEAK     DEFAULT  UND        _Jv_RegisterClasses
    73:    080494f0     0     NOTYPE      GLOBAL   HIDDEN   ABS        __preinit_array_start
    74:    00000000     0     NOTYPE      WEAK      DEFAULT  UND        __gmon_start__
    75:    080482bc    48     FUNC         GLOBAL   DEFAULT  UND        strcpy@@GLIBC_2.0

 

==================================Q & A===========================================

  1. static全局变量与普通的全局变量有什么区别 ?

  全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。

  全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。 这两者在存储方式上并无不同。

  这两者的区别在于非静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。 

  static全局变量只初使化一次,防止在其他文件单元中被引用;   

 

  2.  static局部变量和普通局部变量有什么区别 ?

   把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。         

  static局部变量只被初始化一次,下一次依据上一次结果值;  

 

  3.  static函数与普通函数有什么区别?

   static函数与普通函数作用域不同,仅在本文件。只在当前源文件中使用的函数应该说明为内部函数(static),内部函数应该在当前源文件中说明和定义。对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件.

  static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝 

 

 

 

 

 

  

  

  

 

 

 

原文链接: https://www.cnblogs.com/yaozhongxiao/archive/2010/11/10/1873615.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

    程序的内存分配----变量在可执行文件中的内存区分配

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/17216

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月7日 下午5:45
下一篇 2023年2月7日 下午5:46

相关推荐