程序基石系列之内存里C语言代码布局

一个程序本质上都是由 BSS 段、data段、text段三个组成的。这样的概念在当前的计算机程序设计中是很重要的一个基本概念,而且在嵌入式系统的设计中也非常重要,牵涉到嵌入式系统运行时的内存大小分配,存储单元占用空间大小的问题。

  • BSS段:在采用段式内存管理的架构中,BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。BSS是英文Block Started by Symbol的简称。BSS段属于静态内存分配。
  • 数据段:在采用段式内存管理的架构中,数据段(data segment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态内存分配。
  • 代码段:在采用段式内存管理的架构中,代码段(text segment)通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域属于只读。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。

程序编译后生成的目标文件至少含有这三个段,这三个段的大致结构图如下所示:

其中.text即为代码段,为只读。.bss段包含程序中未初始化的全局变量和static变量。data段包含三个部分:heap(堆)、stack(栈)和静态数据区。

  • 堆(heap):堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)
  • 栈(stack):栈又称堆栈, 是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进先出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。

当程序在执行时动态分配空间(C中的malloc函数),所分配的空间就属于heap。其概念与数据结构中“堆”的概念不同。

stack段存放函数内部的变量、参数和返回地址,其在函数被调用时自动分配,访问方式就是标准栈中的LIFO方式。(因为函数的局部变量存放在此,因此其访问方式应该是栈指针加偏移的方式,否则若通过push、pop操作来访问相当麻烦)

data段中的静态数据区存放的是程序中已初始化的全局变量、静态变量和常量。

在采用段式内存管理的架构中(比如intel的80x86系统),BSS 段(Block Started by Symbol segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域,一般在初始化时 BSS 段部分将会清零。BSS 段属于静态内存分配,即程序一开始就将其清零了。

比如,在C语言之类的程序编译完成之后,已初始化的全局变量保存在.data 段中,未初始化的全局变量保存在.bss 段中。在《Programming ground up》里对.bss的解释为:There is another section called the .bss. This section is like the data section, except that it doesn’t take up space in the executable.其中text和data段都在可执行文件中(在嵌入式系统里一般是固化在镜像文件中),由系统从可执行文件中加载;而BSS段不在可执行文件中,由系统初始化。

图引自《C专家编程》

BSS段只保存没有值的变量,所以事实上它并不需要保存这些变量的映像。运行时所需要的BSS段大小记录在目标文件中,但BSS段并不占据目标文件的任何空间。

如下C语言的代码所示:

//main.c
int a = 0; //全局初始化区
char *p1; //全局未初始化区

main()
{
    static int c =0; //全局(静态)初始化区
    int b; //栈

    char s[] = "abc"; //栈
    char *p2; //栈
    char *p3 = "123456"; //"123456\0"在常量区,p3在栈上。

    p1 = (char *)malloc(10);
    p2 = (char *)malloc(20); //分配得来得10和20字节的区域就在堆区。
}

关于程序设计基石与实践更多讨论与交流,敬请关注本博客和新浪微博songzi_tea.

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-09-29 17:50:05

程序基石系列之内存里C语言代码布局的相关文章

android黑科技系列——Apk混淆成中文语言代码

一.前言 最近想爆破一个app,没有加壳,简单的使用Jadx打开查看源码,结果把我逗乐了,代码中既然都是中文,而且是一些比较奇葩的中文字句,如图所示: 瞬间感觉懵逼了,这app真会玩,我们知道因为Java语言是支持双字符的,所以可以将包名,类名,变量名,方法名定义成中文,或者其他国家的语言都可以的.所以本身这种做法是不会运行报错的,比如下面我们新建一个Java工程看一下效果: 运行是没有任何问题的.看到这里的时候觉得很好奇,所以就先没去看他的源码了,而是想着怎么实现这种混淆的功能.下面就来介绍一

程序设计基石与实践系列之失落的C语言结构体封装艺术

英文来源于 Eric S. Raymond-- The Lost Art of C Structure Packing 谁该阅读这篇文章 本文是关于削减C语言程序内存占用空间的一项技术--为了减小内存大小而手工重新封装C结构体声明.你需要C语言的基本知识来读懂本文. 如果你要为内存有限制的嵌入式系统.或者操作系统内核写代码,那么你需要懂这项技术.如果你在处理极大的应用程序数据集,以至于你的程序常常达到内存的界限时,这项技术是有帮助的.在任何你真的真的需要关注将高速缓存行未命中降到最低的应用程序里

程序最多能new多少内存(2G内存里要放程序的5大区,HeapAlloc比new要快多了,而且超过2G的时候会告诉你)

根据<Windows核心编程>得知:X86操作系统提供每个程序最多只有4G的虚拟内存,其中2G虚拟内存提供给系统用(具体用来干什么还待考察),还有2G的内存留给用户使用.那这2G内存能拿来干嘛呢?首先给你介绍程序内存的5大区:栈区,堆区,全局区(静态区),文字常量区,程序代码区.这5大区就只能可怜的蜗居在这2G的内存里,所以我们要好好的珍惜这2G内存了!大部分程序员在申请堆时都习惯使用new,因为它简单好用,但很多一部分程序员不清楚我能new多少内存!这时候就得看你的除了堆区其余4大分区占用了

2.C语言------程序运行为什么需要内存2

C语言------程序运行为什么需要内存(二) 1.代码就是函数.C语言中全局变量和局部变量就是数据. 2.在运行应用程序时,所有应用程序的代码和数据都在DRAM中就是冯诺依曼结构. 3.在单片机中将程序烧写到Flash(NorFlash)中,然后程序在Flash中运行.如果程序运行过程中不需要处理数据,那么这个这个程序也就不需要内存.程序中涉及到的数据(全局变量或局部变量)不能再Flash中必须放到RAM(SRAM:内存)中.CPU从Flash中读取程序并运行该程序,Flash中的程序只会被读

漫谈程序员系列:程序员的生活就这样吗

我当了快十年程序员了,终于老得可以来谈谈程序员的生活是什么样子了. 或许陈奕迅的<十年>中的一段歌词,可以表示很多程序员和软件开发之间的感情纠葛: " 十年之前 我不认识你 你不属于我 我们还是一样 陪在一个陌生人左右 走过渐渐熟悉的街头 十年之后 我们是朋友 还可以问候 只是那种温柔 再也找不到拥抱的理由 情人最后难免沦为朋友 怀抱既然不能逗留 何不在离开的时候 一边享受 一边泪流 " 这首歌的词作者是林夕,香港才子.林夕的歌词写得真不错,我还因为这个在 13 年时买了他

C程序中常见的内存操作错误

对C/C++程序员来说,管理和使用虚拟存储器可能是个困难的, 容易出错的任务.与存储器有关的错误属于那些令人惊恐的错误, 因为它们在时间和空间上, 经常是在距错误源一段距离之后才表现出来. 将错误的数据写到错误的位置, 你的程序可能在最终失败之前运行了好几个小时,且使程序中止的位置距离错误的位置已经很远啦.而避免这种噩梦的最好方法就是防范于未然. 幸好<深入理解计算机系统>中有一段讲: C程序中常见的内存操作有关的10种典型编程错误,十分经典, 因此抄写在此, 以便以后随时查看,复习. 把优秀

【转】《深入理解计算机系统》C程序中常见的内存操作有关的典型编程错误

原文地址:http://blog.csdn.net/slvher/article/details/9150597 对C/C++程序员来说,内存管理是个不小的挑战,绝对值得慎之又慎,否则让由上万行代码构成的模块跑起来后才出现内存崩溃,是很让人痛苦的.因为崩溃的位置在时间和空间上,通常是在距真正的错误源一段距离之后才表现出来.前几天线上模块因堆内存写越界1个字节引起各种诡异崩溃,定位问题过程中的折腾仍历历在目,今天读到<深入理解计算机系统>第9章-虚拟存储器,发现书中总结了C程序中常见的内存操作有

漫谈程序员系列:程序员零门槛?

六个小故事,一一来看. 博主从技术支持转做开发 2005年3月份我决定辞去技术支持工作,转行做软件开发.大学时我曾经学习过PASCAL这种编程语言,但和程控交换机打了几年交道之后,PASCAL早已灰飞烟灭,渣都找不到了,而且我孤陋寡闻,也没听说哪个软件公司用PASCAL做开发.于是呢,我决定学习C语言,花了一个星期,走马观花式学习了由Brian W. Kernighan和Dennis M. Ritchie合著的<The C Programming Language>(译作<C程序设计语言

漫谈程序员系列:神奇的四步编程法

我曾经学习过很多门开发语言,C.C++.Java.Lua.JavaScript.Python.Scala.Pascal等,不断地从零开始学习新语言,强化了我对学习过程的记忆,使得我对如何学习编程语言积累了一点点心得,我一直想把它记录下来,可考虑到这种经验的个人特征过于明显,我一直犹豫着要不要真的去做这件事儿.直到最近我又一次开始学习据说是太阳系最难的语言Scala时,我猜意识到,是时候把这种经验写下来了. 就在我要开始噼里啪啦敲键盘的时候,发现了下面的图片,碉堡了,汗出如浆,毅然放弃了我的想法.