在仔细研究这个问题之前,我认为 C 程序在内存中只有代码段,堆和栈三部分构成。前几天面试被问到了这个问题,才发现自己的印象是不完全的。

在本文中通过解析析一个 C 程序中变量和函数的地址来分析 C 程序在内存中的布局。

首先简单介绍一下Linux上C程序的内存分布。

一般情况下从低地址到高地址分布着:

  • 程序代码段及只读数据段

    • 程序代码,以及字符串常量等都存储在这里
  • 可读可写数据段
    • 全局变量,静态变量存储在这里  
  • 数据堆
    • 程序中动态分配的内存在这一块 
  • 共享库
    • 程序加载的共享库加载在这个地方
  • 数据栈
    • 局部变量,函数参数,函数返回地址等存储在这里
  • 内核地址
    • 操作系统内核内存空间

C语言代码如下(var_memory.c)

 #include <stdio.h>
#include <stdlib.h> #include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <argp.h> int global = ;
static int static_global = ; int main()
{
char *string_literal = "hello";
static int static_local = ;
char local_array[] = {'a', 'b', 'c'};
int *malloced = (int *)malloc(*sizeof(int)); pid_t pid = getpid();
printf("pid: %ld\n", (long)pid); // string_literaling literal and function in this module are loaded
// into readonly and excutable segment
printf("%.8p: addr of main\n", &main);
printf("%.8p: addr of local string_literal literal\n", string_literal);
puts(""); // static and global variables are allocated in data segment
printf("%.8p: addr of global int\n", &global);
printf("%.8p: addr of global static int\n", &static_global);
printf("%.8p: addr of local static int\n", &static_local);
puts(""); // allocated in heap
printf("%.8p: addr of local malloc\n", malloced);
puts(""); // errno is from a library
printf("%.8p: addr of errno\n", &errno);
puts(""); // printf is from a library
printf("%.8p: addr of printf\n", &printf);
puts(""); printf("%.8p: addr of argp_program_verstatic_localon_hook\n", & argp_program_version_hook);
puts(""); // local variable in stack
printf("%.8p: addr of local pid\n", &pid);
printf("%.8p: addr of local array\n", local_array);
puts(""); scanf("%s", local_array);
return ;
}

在linux命令行

make var_memory.c

生成var_memory可执行文件,运行

./var_memory

输出如下:

 pid:
0x004b26dd: addr of main
0x004b29d0: addr of local string_literal literal 0x004b4008: addr of global int
0x004b400c: addr of global static int
0x004b4010: addr of local static int 0x00d30160: addr of local malloc 0xb7db86b4: addr of errno 0xb7e0b1c0: addr of printf 0xb7f8f798: addr of argp_program_verstatic_localon_hook 0xbf85518c: addr of local pid
0xbf855198: addr of local array

打开另外一个终端窗口,根据上面输出的pid,运行命令

 pmap 4760 # 进程id以上面输出为准

输出如下

 :   ./var_memory
004b2000 4K r-x-- var_memory
004b3000 4K r---- var_memory
004b4000 4K rw--- var_memory
00d30000 136K rw--- [ anon ]
b7db8000 8K rw--- [ anon ]
b7dba000 1852K r-x-- libc-2.26.so
b7f89000 4K ----- libc-2.26.so
b7f8a000 8K r---- libc-2.26.so
b7f8c000 4K rw--- libc-2.26.so
b7f8d000 12K rw--- [ anon ]
b7fbb000 8K rw--- [ anon ]
b7fbd000 12K r---- [ anon ]
b7fc0000 8K r-x-- [ anon ]
b7fc2000 148K r-x-- ld-2.26.so
b7fe7000 4K r---- ld-2.26.so
b7fe8000 4K rw--- ld-2.26.so
bf836000 132K rw--- [ stack ]
total 2352K

从运行C语言程序与pmap的输出可以看出。

  • main函数(0x004b26dd)和字符串常量(0x004b29d0)被分配在了内存区域

    • 004b2000      4K r-x-- var_memory,其中rx代表只读和可执行
  • 全局变量global(0x004b4008),全局静态变量 static_global(0x004b400c),局部静态变量(0x004b4010)被分配在了数据段
    • 004b4000      4K rw--- var_memory,其中rw代表可以读和写
  • 使用malloc分配的空间(0x00d30160)处在堆(heap)里
    • 00d30000    136K rw---   [ anon ]
  • 共享库提供的全局变量errno(0xb7db86b4),被分配在共享库数据段里
    • b7db8000      8K rw---   [ anon ]
  • 共享库提供的函数printf(0xb7e0b1c0),被分配在共享库代码段里
    • b7dba000   1852K r-x-- libc-2.26.so
  • 局部变量(0xbf85518c,0xbf855198)被分配在了栈上
  • bf836000    132K rw---   [ stack ]

希望这篇文章,对同样困惑于C程序在内存中布局的你有所帮助。

Linux 上 C 程序的内存布局的更多相关文章

  1. Linux下C程序的内存布局

    参考下列书籍中的对应章节: <Linux高级程序设计(第3版)>第3章Linux进程存储管理.相关视频:一.二. <C专家编程>第6章 运动的诗章:运行时数据结构. <U ...

  2. Anatomy of a Program in Memory.剖析程序的内存布局

    原文标题:Anatomy of a Program in Memory 原文地址:http://duartes.org/gustavo/blog/ [注:本人水平有限,只好挑一些国外高手的精彩文章翻译 ...

  3. linux上应用程序的执行机制

    linux上应用程序的执行机制 执行文件是如何在shell中被"执行"的.本文中尽可能少用一些源码,免得太过于无 聊,主要讲清这个过程,感兴趣的同学可以去查看相应的源码了解更多的信 ...

  4. C语言程序的内存布局

    C语言程序的内存布局 一:C语言程序的存储区域 C语言编写的程序经过编绎-链接后,将形成一个统一的文件,它由几个部分组成,在程序运行时又会产生几个其他部分,各个部分代表了不同的存储区域: 1.代码段( ...

  5. Linux下C程序的内存映像

    2.Linux下C程序的内存映像 2.1. 代码段.只读数据段(1)对应着程序中的代码(函数),代码段在Linux中又叫文本段(.text)(2)只读数据段就是在程序运行期间只能读不能写的数据,con ...

  6. Linux上java程序的jar包启动通用脚本(稳定用过)

    Linux上java程序的jar包启动通用脚本如下: #! /bin/sh export LANG="zh_CN.GBK" SERVICE_NAME=` .sh` SCRIPT_N ...

  7. ESP32应用程序的内存布局

    应用程序内存布局 ESP32芯片具有灵活的内存映射功能.本节介绍ESP-IDF在默认情况下如何使用这些功能. ESP-IDF中的应用程序代码可以放置在以下内存区域之一中. IRAM(指令RAM) ES ...

  8. Linux 上如何清除 RAM 内存高速缓存,缓存和交换空间

    像任何其他的操作系统一样,GNU / Linux已经有效地实施了内存管理甚至更多.但是,如果有任何进程正在蚕食你的内存,你要清除它,Linux提供了一个方法来刷新或清除RAM缓存. 在Linux中如何 ...

  9. 分析Linux上的程序依赖

    ldd [path_to_exe] ldd通过调用动态链接器来获取可执行程序的依赖库,但是并不推荐在未知的可执行程序上执行业ldd来获取其依赖库,因为部分版本的ldd会直接通过调用该可执行程序来获取其 ...

随机推荐

  1. Java 敏感词过滤,Java 敏感词替换,Java 敏感词工具类

    Java 敏感词过滤,Java 敏感词替换,Java 敏感词工具类   =========================== ©Copyright 蕃薯耀 2017年9月25日 http://www ...

  2. 经典CSS坑:如何完美实现垂直水平居中?

    经典CSS坑:如何完美实现垂直水平居中? 踩了各种坑之后总结出的方法,开门见山,直接上代码和效果图.希望对读者有所帮助,后续如果有更好的方法,我也会持续更新 第一种方法:transform(个人认为最 ...

  3. mybatis if条件查询 及<号的问题

    摘录自:http://flt95.blog.163.com/blog/static/12736128920136185841551/ <if test="p=='1'"> ...

  4. Django 模版中如何对主菜单进行选中?

    问题描叙: 在实际模版中,返回的页面我们想要主菜单保持一种状态,这个主菜单进去的子页面,都需要保持主菜单选择状态 例如 主菜单是 课程, 下面展现的章节和视频页面时候, 课程这个主菜单在展现这个菜单下 ...

  5. elasticsearch聚合查询

    作者注:本文系作者自己的理解.希望大家多多交流指正 官网java API term是代表完全匹配,也就是精确查询,搜索前不会再对搜索词进行分词,所以我们的搜索词必须是文档分词集合中的一个 TermsB ...

  6. Servlet--HttpSessionBindingListener接口,HttpSessionBindingEvent类

    HttpSessionBindingListener 接口 定义 public interface HttpSessionBindingListener 这个对象被加入到 HTTP 的 session ...

  7. java基础-静态,非静态(构造)代码块,类加载

    static block and non-static block(constructor block) [toc] 想来想去,先来一题比较好 public class Foo { public st ...

  8. jquery +/-小样式

    <script>部分 var num = 0; $(document).on('click','#add',function(){ _this = $(this); div = _this ...

  9. 循环while 、do…while 、for

    循环while .do-while .for 循环结构的选择? - 当对一个条件进行一次判断时,用if语句: - 当对一个条件进行多次判断时,用while语句: 1.While 循环条件成立就会执行循 ...

  10. ado.net 参数传递之 in

    之前项目有一放行的功能,对某界面维护时(数据的增删改),先将数据保存到临时表中,放行后再真正的写入到库中.由于设计到主从表多中约束关系,所以当时我采用的是写一个存储过程来对某个界面的操作进行统一处理, ...