学习交流加

  • 个人qq:
    1126137994
  • 个人微信:
    liu1126137994
  • 学习交流资源分享qq群:
    962535112

在我之前学习底层的知识的时候,也写过相关的内容。可以对比的学习:【软件开发底层知识修炼】二十 深入理解可执行程序的结构,【软件开发底层知识修炼】二十三 ABI-应用程序二进制接口三之深入理解函数栈帧的形成与摧毁

学习本文的前提是了解进程的内存布局空间。可以看上面两篇博客进行巩固

文章目录

  • 1 程序中的栈内存结构
    • 1.1 函数的调用过程对应的栈的变化
      • 1.11 函数调用栈上的数据
  • 2 程序中(应该称为进程中才对)的堆结构
  • 3 程序中的静态存储区(其实就是数据区)
  • 4 总结

1 程序中的栈内存结构

  • 栈在程序中用于维护函数调用的上下文
  • 函数中的参数和局部非静态变量存储在栈上
  • esp指针始终指向栈顶
  • ebp是函数栈帧,用于定位的,使用ebp可以查找到函数的参数,返回地址等信息。后面的函数调用分析会认识到这一点
  • 栈在整个进程内存空间中是从上往下扩展,也就是从高地址往低地址扩展。如下图是一个栈

push 操作相当于往栈中填数据,esp指针会向下走。pop操作相当于将栈顶数据弹出,esp指针会网上走。

那么栈对于程序而言,到底有什么用呢?

栈用于保存一个函数调用的时候所需要维护的信息,包括函数的参数、返回地址、局部变量、上下文信息等。它们在栈中的位置大致如下图所示:

1.1 函数的调用过程对应的栈的变化

每次函数调用都对应一个栈上的活动记录,被调用函数的活动记录位于调用函数的下面。比如有如下的几个函数调用:

那么在某一个时刻,栈中的内容大致是这样的:

注意:上面的栈中的内容并没有具体,只是说明各个函数的活动记录信息在哪个文职,具体里面的内容没有列出。看下面的分析:

从程序开始运行时分析:

  1. 从main函数开始运行

当main开始运行的时候,main函数的栈的信息是下面这样的:

  • 可以看到,首先入栈的是函数参数,然后是函数的返回地址
  • old_ebp代表调用main函数的ebp的位置。这个暂且不管
  • 可以看到,ebp向上偏移4字节(在X86 32位系统中,栈是以4字节为单位进行存储数据,所以一次偏移就是4字节)就能找到返回地址(这个返回地址是调用main函数的那个函数之前执行指令的地址)。ebp向下偏移4字节就是old_ebp。所以说ebp是函数栈帧,用于定位查找其他参数
  • esp始终指向栈顶
  1. 当main函数调用f()函数

当main函数执行到调用f() 函数的时候,main函数的活动记录(寄存器,返回地址等)需要保存入栈,f() 函数的参数信息需要入栈。如下图所示:


可以看到:

  • f函数的参数先入栈
  • 然后返回地址入栈(main函数调用f()函数那里的地址)
  • 然后main函数的ebp的地址入栈。用于定位上一个函数的ebp
  • 然后才是函数中的局部非静态变量信息入栈。这个参数的入栈顺序可以参考本文开头给出的两篇文章中的内容

f函数调用g函数就是一样的过程,这里就不再赘述。下面直接上当f执行完返回的过程是怎样的?

  1. 当从f()调用中返回到main函数的栈的变化

上述f返回后当前栈就只有白色空白部分,下面的深颜色(橘黄色???分不清)并不是当前栈的内容了

可以看出;

  • 通过f函数栈帧中的old_ebp找到main函数的ebp,然后将当前ebp寄存器指向它。
  • 通过f栈帧中的返回地址找到main函数之前被中断的地址处,main函数继续执行。关于这里,非常详细的文章请参考:【软件开发底层知识修炼】二十三 ABI-应用程序二进制接口三之深入理解函数栈帧的形成与摧毁

1.11 函数调用栈上的数据

函数调用栈上的数据,在函数返回时,将被释放,不再有效。所以对于以下代码,是错误的代码;

2 程序中(应该称为进程中才对)的堆结构

  • 堆空间是进程的内存空间中一块预留的内存空间,供程序在运行的时候动态分配。
  • 使用malloc与free进行堆中的内存的动态申请(具体的使用可以参考这篇文章:【C语言进阶深度学习记录】三十三 C语言中动态内存分配

对于堆空间,本文只简单的讲述系统对堆空间的管理方式(也就是使用malloc时,系统是如何申请内存的,以及系统对内存的管理方式,当然本文也是简单描述,具体可以参考CSAPP书籍中相关章节)

操作系统对堆的管理方式主要有:

  • 空闲链表法、位图法、对象池法

下面主要简要说明空闲链表发的原理:

空闲链表就是操作系统将整个可用的堆内存空间分为一块一块的,对应的相同数量的指针指向各个内存块,然后内存块的末尾又是一个指针指向下一个内存块的头。

其实简单来说就是将多个内存块串联成一个链表形式。

如下图所示:

当使用malloc函数进行内存分配的时候,系统会遍历链表,找到能够满足申请大小的且没有被别人申请的空闲的链表的一个节点对应的内存块。比如上图,申请一个4字节的内存,最终遍历链表找到了一个大小为5字节的内存块,然后系统就为我们在该内存块上申请4字节的内存空间供指针p使用。

简单来说,空闲链表法就是上述的大致过程。

想要深入了解操作系统对堆空间的管理,可以阅读书籍《深入理解计算机系统》(csapp)。本文不再重复赘述。

3 程序中的静态存储区(其实就是数据区)

我们一般说进程,但这里说程序。有些术语描述真的是很模棱两可,但是只要自己明白就行

在进程的地址空间,栈,堆是程序运行时的效果。我们也知道这两个内存对应的数据到底是什么。

那么对于下面程序中的变量g_init_v,g_uninit_v,s_v1,s_v2以及字符串字面量,它们是存储在哪里呢?

我们已经看到了上图中,它们在可执行文件中都是存储在.data区,.bss区。这些区域。

当程序运行起来之后,它们还是会在进程的内存空间的.data与.bss段。与可执行文件的data与bss名字一样如下图


我们称之为静态存储区。为什么叫静态?并不是因为static,而是因为它们的值虽然有可能被改变,但是却一直在那个区域。不像栈区域的值最后会被销毁,堆上的值也需要free。

现在终于明白静态存储区实际就是.data与.bss区域了。

下面总结一下静态存储区的几点重要知识:

  1. 静态存储区是随着程序的运行而分配空间
  2. 静态存储区的生命周期直到程序运行而结束
  3. 在程序的编译期间,静态存储区的大小就已经确定
  4. 静态存储区主要用于保存全局变量静态局部变量以及类似于字符串字面量样式的字面量

4 总结

栈,堆与静态存储区是进程地址空间的基本数据区域。

  • 一定要注意区分可执行文件中的内容与程序运行起来后的进程地址空间中的内容的差别。
  • 本文所描述的就是进程地址空间中内容。可执行文件的内容可参考书籍《程序员的自我修养》

【C语言进阶深度学习记录】三十五 程序中的堆、栈以及静态存储区(数据区)相关推荐

  1. 【C语言进阶深度学习记录】十五 编译过程简介

    文章目录 1 初识编译器 2 总结 1 初识编译器 我们平时口中所说的编译器,是广泛的编译器.实际上,编译器包括了以下四个部分: 一个C代码被编译为可执行代码,包括以下几个过程: 下面就对上述的各个过 ...

  2. 【C语言进阶深度学习记录】十六 静态库与动态库的创建与使用

    上一篇文章学习了编译的过程,点击链接查看:[C语言进阶深度学习记录]十五 编译过程简介,每一个C源文件编译后将会生成目标文件,那么这些目标文件,还需要链接起来,生成可执行文件. 文章目录 1 链接的意 ...

  3. 【C语言进阶深度学习记录】十九 #pragma使用与分析

    文章目录 1 #pragma 概念简介 1.1 #pragma message 的用法 1.2 #pragma once 的用法 1.3 #pragma pack 的用法 1.31 struct占用的 ...

  4. 【C语言进阶深度学习记录】十 C语言中:struct的柔性数组和union分析

    本文并不讲C语言的基础 文章目录 1 空struct的大小 2 结构体与柔性数组 2.1 柔性数组的使用方法 2.2 柔性数组使用代码案例分析 3 C语言中的union分析 3.1 使用union判断 ...

  5. 【C语言进阶深度学习记录】十四 C语言中 三目运算符和逗号表达式

    文章目录 1 三目运算符 1.1 三目运算符的返回类型的代码案例分析 2 逗号表达式 2.1 逗号表达式代码案例分析 2.2 如何用一行代码实现 strlen函数 3 总结 1 三目运算符 三目运算符 ...

  6. 【C语言进阶深度学习记录】十八 条件编译的使用与分析

    文章目录 1 基本概念 1.1 代码分析 1.2 通过命令行定义宏 2 #include 的本质 2.1 解决重复包含头文件的问题 3 条件编译的应用 4 总结 1 基本概念 条件编译的行为类似于C语 ...

  7. 【C语言进阶深度学习记录】十二 C语言中的:字符和字符串

    文章目录 1 C语言中的单引号和双引号 1.1 双引号带来的BUG 2 总结 1 C语言中的单引号和双引号 C语言中的单引号用来表示字符字面量 C语言中的双引号用来表示字符串字面量,存储于全局的只读存 ...

  8. 【C语言进阶深度学习记录】二十六 C语言中的字符串与字符数组的详细分析

    之前有一篇文章是学习了字符和字符串的,可以与之结合学习:[C语言进阶深度学习记录]十二 C语言中的:字符和字符串 文章目录 1 字符串的概念 1.1 字符串与字符数组 1.2 字符数组与字符串代码分析 ...

  9. 【C语言进阶深度学习记录】三十八 C/C++语言中的函数声明与函数定义

    文章目录 1 函数的声明和定义 1.1 代码分析 2 总结 1 函数的声明和定义 声明的意义在于告诉编译器程序单元的存在.只是告诉编译器它存在但是不在声明这里定义,有可能在当前文件中的其他地方或者其他 ...

最新文章

  1. CREATE TABLESPACE
  2. Java随笔--分布式
  3. 自律到极致-人生才精致:第12期
  4. autodesk powerinspect ultimate 2021中文版
  5. win10任务栏怎么还原到下面_Windows10 系统桌面底部的任务栏在侧边了怎么还原
  6. 【转载】从头编写 asp.net core 2.0 web api 基础框架 (1)
  7. 面试和人生目标(转)
  8. cesium学习之环境搭建
  9. 新电脑怎么分盘_电脑新系统装了杀毒软件之后,原本非常流畅,怎么突然变卡了?...
  10. 怎么在ReactNative里面使用Typescript
  11. redis和php有什么,redis与jedis的区别是什么
  12. 清除SQL SERVER日志
  13. 恒压板框过滤实验数据处理_中学少见、高考常考的化学实验仪器
  14. 基于matlab的数字图像处理---图像的锐化与边缘提取
  15. 软件介绍:搜索工具 Listary
  16. C语言的32个关键字怎么背,谁知道c语言的32个关键字怎么读,还有语法。
  17. c语言中 cos函数图像,余弦函数图像(cos余弦函数图像)
  18. 社团管理系统数据流图_实例6:社团管理系统数据库设计
  19. Axure RP使用攻略--入门级(一)
  20. 每日一淘赋能产品经济全面发展

热门文章

  1. shiro学习(7):shiro连接数据库 方式二
  2. Java面试题18 牛客 假定Base b = new Derived();
  3. 第七十二期:Visual Studio Online 终于公开上线了
  4. java面试题3 牛客:下面有关jdbc statement的说法错误的是
  5. SourceInsight 常用快捷键
  6. 怎么使用7zip进行分批压缩_怎么使用钢结构抛丸机对钢结构进行除锈?
  7. 是什么东西_隐形牙套附件是什么东西?
  8. javascript的stack overflow
  9. typescript在ES3(IE7)环境下使用async、await
  10. 面向对象之反射、包装、(定制)