在C语言中,内存通常分为几个不同的数据区,每个数据区有不同的用途。以下是C语言中的主要数据区:

  1. 代码区(Text Segment)
  • 存储程序的机器代码,即编译后的指令。代码区通常是只读的,防止程序意外修改自己的指令。
  • 示例作用:存储函数定义。
  1. 数据区(Data Segment)
  • 又分为静态数据区(静态数据段)和BSS段。
  • 静态数据区(.data segment):存储已初始化的全局变量和静态变量。
  • BSS段(.bss segment):存储未初始化的全局变量和静态变量,系统在程序开始运行时将这些变量初始化为零。
  1. 堆区(Heap Segment)
  • 用于动态分配内存,由程序在运行时显式地分配和释放(如使用malloc、free)。
  • 示例作用:存储动态分配的对象。
  1. 栈区(Stack Segment)
  • 用于存储局部变量、函数参数、返回地址等。栈区由系统自动分配和释放,通常用于函数调用。
  • 示例作用:存储函数的局部变量和参数。

以下是一个示例代码展示了这些数据区的作用:

#include <stdio.h>
#include <stdlib.h>

// 全局变量 - BSS段(未初始化)
int global_uninit_var;

// 全局变量 - 数据段(已初始化)
int global_init_var = 100;

// 静态变量 - 数据段(已初始化)
static int static_init_var = 200;

// 函数定义 - 代码区
void function() {
    // 局部变量 - 栈区
    int local_var = 10;
    printf("Local variable: %d\n", local_var);

    // 动态分配的内存 - 堆区
    int *heap_var = (int *)malloc(sizeof(int));
    *heap_var = 20;
    printf("Heap variable: %d\n", *heap_var);

    // 释放动态分配的内存
    free(heap_var);
}

int main() {
    // 打印全局变量和静态变量
    printf("Global uninitialized variable: %d\n", global_uninit_var);
    printf("Global initialized variable: %d\n", global_init_var);
    printf("Static initialized variable: %d\n", static_init_var);

    // 调用函数
    function();

    return 0;
}

在这段代码中:

  • global_uninit_var 是一个未初始化的全局变量,存储在BSS段。
  • global_init_var 是一个已初始化的全局变量,存储在数据段。
  • static_init_var 是一个已初始化的静态变量,存储在数据段。
  • function 是一个函数定义,存储在代码区。
  • local_var 是一个局部变量,存储在栈区。
  • heap_var 是一个动态分配的内存块,存储在堆区。

通过这个示例,可以清晰地看到C语言程序中各个数据区的使用和作用。


nm命令可以列出目标文件(如可执行文件、库文件等)中的符号,并显示每个符号所在的段和其他信息。使用nm命令可以帮助我们查看C语言程序中的不同数据区。以下是一个具体的例子,演示如何通过nm命令查看各个数据区。

示例代码

#include <stdio.h>
#include <stdlib.h>

// 全局变量 - BSS段(未初始化)
int global_uninit_var;

// 全局变量 - 数据段(已初始化)
int global_init_var = 100;

// 静态变量 - 数据段(已初始化)
static int static_init_var = 200;

// 函数定义 - 代码区
void function() {
    // 局部变量 - 栈区
    int local_var = 10;
    printf("Local variable: %d\n", local_var);

    // 动态分配的内存 - 堆区
    int *heap_var = (int *)malloc(sizeof(int));
    *heap_var = 20;
    printf("Heap variable: %d\n", *heap_var);

    // 释放动态分配的内存
    free(heap_var);
}

int main() {
    // 打印全局变量和静态变量
    printf("Global uninitialized variable: %d\n", global_uninit_var);
    printf("Global initialized variable: %d\n", global_init_var);
    printf("Static initialized variable: %d\n", static_init_var);

    // 调用函数
    function();

    return 0;
}

编译并使用nm命令

  1. 编译代码
gcc -o example example.c
  1. 使用nm命令
nm example

nm命令输出示例

0000000000401050 B global_uninit_var
0000000000401060 D global_init_var
0000000000401064 b static_init_var
0000000000401040 T function
0000000000401080 T main

解释nm命令的输出

  • BSS段
  • 符号:global_uninit_var
  • 类型:B
  • 地址:0000000000401050
  • 描述:未初始化的全局变量存储在BSS段,符号类型为B
  • 数据段
  • 符号:global_init_var
  • 类型:D
  • 地址:0000000000401060
  • 描述:已初始化的全局变量存储在数据段,符号类型为D
  • 符号:static_init_var
  • 类型:b
  • 地址:0000000000401064
  • 描述:已初始化的静态变量存储在数据段,符号类型为b
  • 代码段(文本段)
  • 符号:function
  • 类型:T
  • 地址:0000000000401040
  • 描述:函数定义存储在代码段,符号类型为T
  • 符号:main
  • 类型:T
  • 地址:0000000000401080
  • 描述:main函数存储在代码段,符号类型为T

总结

通过nm命令,我们可以看到每个符号的类型和地址,从而了解它们位于哪一个数据区。不同符号类型表示不同的数据区:

  • Bb:BSS段
  • Dd:数据段
  • Tt:代码段(文本段)

局部变量和动态分配的内存不会出现在nm命令的输出中,因为它们在运行时分配,不是符号表的一部分。