C程序内存布局

304次阅读  |  发布于3年以前

作为计算机专业的来说,程序入门基本都是从C语言开始的,了解C程序中的内存布局,对我们了解整个程序运行,分析程序出错原因,会起到事半功倍的作用 。

C程序的内存布局包含五个段,分别是STACK(栈段),HEAP(堆段),BSS(以符号开头的块),DS(数据段)和TEXT(文本段)。

每个段都有自己的读取,写入和可执行权限。如果程序尝试以不允许的方式访问内存,则会发生段错误,也就是我们常说的coredump。

段错误是导致程序崩溃的常见问题。核心文件(核心转储文件)也与段错误相关联,开发人员使用该文件来查找崩溃的根本原因(段错误)。

下面我们将深入这五个段,更加详细的讲解每个段在程序开发或者运行中的作用。

High Addresses ---> .----------------------.
                    |      Environment     |
                    |----------------------|
                    |                      |   Functions and variable are declared
                    |         STACK        |   on the stack.
base pointer ->     | - - - - - - - - - - -|
                    |           |          |
                    |           v          |
                    :                      :
                    .                      .   The stack grows down into unused space
                    .         Empty        .   while the heap grows up. 
                    .                      .
                    .                      .   (other memory maps do occur here, such 
                    .                      .    as dynamic libraries, and different memory
                    :                      :    allocate)
                    |           ^          |
                    |           |          |
 brk point ->       | - - - - - - - - - - -|   Dynamic memory is declared on the heap
                    |          HEAP        |
                    |                      |
                    |----------------------|
                    |          BSS         |   Uninitialized data (BSS)
                    |----------------------|   
                    |          Data        |   Initialized data (DS)
                    |----------------------|
                    |          Text        |   Binary code
Low Addresses ----> '----------------------'

#include <stdio.h>
int main(void) {
    int data; // 局部变量,存储在栈上
    return 0;
}

#include <stdio.h>
int main(void) {
    char *pStr = malloc(sizeof(char)*4); //pStr指向堆地址
    return 0;
}

BSS(未初始化的数据块)

#include <stdio.h>
int data1; // 未初始化的全局变量存储在BSS段
int main(void) {
    static int data2;  // 未初始化的静态变量存储在BSS段
    return 0;
}

DS(初始化的数据块)

#include <stdio.h>
int data1 = 10 ; //初始化的全局变量存储在DS段
int main(void) {
    static int data2 = 3;  //初始化的静态变量存储在DS段
    return 0;
}

TEXT

深入

现在有一个简单的程序,代码如下:

#include <stdio.h> 

int main(void) { 
    return 0; 
}

我们通过如下命令进行编译

gcc -g a.cc -o a

然后通过size命令,可以看到各个段的大小

[root@build src]# gcc a.c -o a
[root@build src]# size a
   text    data     bss     dec     hex filename
   1040     484      16    1540     604 a

其中前三列分别为可执行程序a的text、data以及bss段的大小,第四列为该三段大小之和,第四列为该大小的十六进制表示,最后一列是文件名。

增加一个未初始化的静态变量

#include <stdio.h> 

int main(void) { 
    static int data;
    return 0; 
}

通过size命令

[root@build src]# size a
   text    data     bss     dec     hex fi

从上面可以看出,bss段size变大

增加一个初始化的静态变量

#include <stdio.h> 

int main(void) { 
    static int data = 10;
    return 0; 
}

通过size命令

[root@build src]# size a
   text    data     bss     dec     hex filename
   1040     488      16    1544     608 a

从上面可以看出,data段size变大

增加一个未初始化的全局变量

#include <stdio.h> 
int data;
int main(void) { 
    return 0; 
}

通过size命令

[root@build src]# size a
   text    data     bss     dec     hex filename
   1040     484      24    1548     60c a

从上面可以看出,bss段size变大

数据段的只读区域和读写区域

#include <stdio.h>
char str[]= "Hello world";
int main(void) {
    printf("%s\n",str);
    str[0]='K';
    printf("%s\n",str);
    return 0;
}

输出

Hello world
Kello world

可以看到上面的示例str是一个全局数组,因此它将进入数据段。还可以看到能够更改该值,因此它具有读取和写入权限。

现在查看其他示例代码

#include <stdio.h>
char *str= "Hello world";
int main(void) {
    str[0]='K';
    printf("%s\n",str);
    return 0;
}

在上面的示例中,我们无法更改数组字符是因为它是文字字符串。常量字符串不仅会出现在数据部分,而且所有类型的const全局数据都将进入该部分。

数据块只读部分,通常除了const变量和常量字符串外,程序的文本部分(通常是.rodata段)也存在于数据块的只读部分,因为通常无法通过程序进行修改。

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8