关闭

C 程序在内存中的布局 [李园7舍_404]

1082人阅读 评论(2) 收藏 举报
分类:

笔记题目比较高级,相信还有很多地方没有笔记清楚,主要是理思路类型,为具体抽象出C程序的每个部分与内存之间的关系图,也没有找到相关书籍查看到内存每个区域(静态区(代码段,只读(代码,数据),读写段),常量区,堆,栈)的具体分布及大小。路漫漫,继续进步吧。要是有本相关的书籍看看实在是好极了。

 

1 硬件平台

软件的运行基于操作系统,操作系统基于特定的硬件结构。

 

一个硬件平台包含“处理器(CPU)”、“系统内存”、“输入、输出设备”,组成每个部分的硬件结构也都比较复杂。处理器与它们的联系依靠“控制总线”、“地址总线”及“数据总线”实现。如下图所示:

 

由图可见,处理器(processor)和内存(system memory只是硬件平台的一部分。

 

2 程序的存储

 

(1) 内存与其它概念的区别

主要与寄存器,硬盘等外部存储器区别。

 

[1] 寄存器

寄存器是为了解决访问内存数据的复杂性和耗时性而被集成在处理器内的存储单元。处理器内(以IA-32为例)包含“通用寄存器”、“段寄存器”、“指令指针寄存器”、“浮点数据寄存器”、“控制寄存器”、“调试寄存器”、“标志寄存器”。

 

[2]硬盘

硬盘是计算机的外部存储器,是计算机上常见的CDE盘。用来保存各种类型的文件(如操作系统及操作系统的安装包)。硬盘不是内存。

 

[3]内存

内存是计算机的内部存储器,也叫做主存储器。是硬件平台的一个重要部分,其作用是用于暂时存放CPU中的运算数据以及与硬盘等外部存储器交换的数据

 

内存一般采用半导体存储单元,包括随机存储器(RAM),只读存储器(ROM),以及高速缓存(CACHE)。只不过RAM是其中最重要的存储器。计算机裸机自带的程序存储在内存中的ROM部分,应用程序运行时被载入到RAM中(所有的应用程序只有被载入到内存中后才能运行,如word安装包中的可执行文件要被载入内存中后才能按照word源代码指令运行,从而才有呈现在用户面前的word交互界面)。所以跟用户打交道的多为RAM。

 

ROM

由于ROM不会因为掉电而丢失其中存储的内容,内存中的ROM部分主要用来存储计算机的固定程序和数据,如BIOS。在装操作系统之前就可以开机接入BIOS界面中。由厂家往内存中的ROM部分烧录BIOS固定程序。但随着ROM的发展,依据ROM的子系列的新功能,BIOS也不是完全的不可改写,可以通过特殊方法将BIOS升级即重新向ROM中载入新版本的BIOS

 

RAM

对于应用程序如对于一个C语言程序来说,通常所说的内存就是指RAM[ ROM不可更改,由厂家设定,一般不与ROM交道 ],此段内存具有随机读写的特性。当然随着时间的流逝,科学的进步,现在的高档内存条都是采用RAM的子系列。

一个存在硬盘上的可执行文件经双击后其内数据就会被载入内存中,从而使可执行文件开始运行。此时计算机一但关机后,载入到内存中的可执行文件数据就会丢失。在计算机开机后,还需要重新双击可执行文件载入各种数据到内存中从头开始运行。

 

CACHE

RAM由于容量大、寻址系统繁多、读写电路复杂等原因,造成了内存的工作速度大大低于CPU的工作速度,直接影响了计算机的性能。为了解决内存与CPU工作速度上的矛盾,计算机专家在CPU和内存之间增设一级容量不大、但速度很高的高速缓冲存储器(Cache)。Cache通常由静态存储器(SRAM)构成。Cache中存放常用的程序和数据,当CPU访问这些程序和数据时,首先从高速缓存中查找,如果所需程序和数据不在Cache中,则到内存中读取数据,同时将数据写到Cache中。采用Cache可以提高系统的运行速度。

 

(2)程序的存储

对于应用程序执行被载入内存中时,是被载入到内存中的RAM部分。RAM由静态区和动态区组成。静态区分为只读数据区,初始化数据区,未初始化数据区。动态区分为堆(heap)和栈(stack)。可执行程序运行被载入内存中时,会将对应的部分存储到相应的内存模块中去。

 

[1] 静态内存区

静态内存主要用来存储“程序代码”及“数据”。

 

最终的可执行文件的代码部分会被存储在RAM中的某区域,存储指令的内存区域可将其称为代码段。代码段的指令由CS值存储基址,由指令指针寄存器指向下一条要取的指令。

 

在静态区中存储的数据主要指自动全局变量和静态变量。在C语言中,经初始化的全局变量和静态变量存储在静态区的一块区域中(将其称为数据段),未经初始化的全局变量和静态变量存储在静态区的另一块内存中(可将其称之为BSSBSS是英文Block Started by Symbol的简称,表示此种变量不在可执行文件中占用内存,用变量名作占位符记录此类型变量需要多少内存空间,系统将其初始化为0)。

 

对于静态局部变量,对局部变量加了static关键字后就改变了局部变量的存储方式。静态局部变量就不会再存储在栈上而是被存储在RAM中的静态区。

 

[2] 动态内存区

动态内存主要是在程序运行时且运行完分配动态内存的指令后动态内存才被分配。

 

对于来说,栈专门用来存非静态的局部变量和调用子函数时传递给子函数的实参。当非静态局部变量的生存期完后,栈内存也随之被系统回收。

 

对于来说,程序代码中有动态分配堆内存的函数(malloc/new)出现才有可能分配堆空间。并使用完堆内存后要用释放动态内存函数(free/delete)释放。即堆空间的分配和回收都是通过特定的代码实现。不管是分配还是释放环节,稍有不慎就会有内存泄露的危险。

 

[3] 常量区

就算是没有这个区域也可以这么看。可以将程序中突如其来的常量的存储区域称作常量区

 

[4] 程序

常量区和栈

#include <stdio.h>

char *mm_address();
char *stack_address();

int main(void)
{
        char *p = NULL;

        p       = mm_address();
        if(p){
                printf("mm address content: %s\n", p);
                p       = NULL;
        }

        p       = stack_address();
        if(p){
                printf("stack address content: %s\n", p);
                p       = NULL;
        }
        return 0;
}

//Return const mm address
char *mm_address()
{
        char *pC = "Hello World!\n";
        return pC;
}

//Retrun stack address
char *stack_address()
{
        char ar[] = "Hello World!\n";
        return ar;
}

 

编译并执行程序得到如下结果:

  • mm_address()函数能将字符串的地址返回给main函数中的p。在main程序中p所指的地址内还有正确的内容,说明“char *pC = "Hello World!\n";”中的字符串常量绝对不是存在了栈上。这样的字符串常量就被存在RAM的常量区。从程序执行结果可知,常量区数据的生命期至少不必main函数小,因为在main函数中字符串常量的内存还没有被释放掉。
  • stack_address()能够将字符串常量的地址返回给main函数中的p(p不为空)。但输出的是乱码。说明p所指内存的内容已经不存在。其实“char ar[] = "Hello World!\n";”语句是在栈上分配了以ar为名(首)的连续空间。等子函数运行完毕后,曾经存储字符串的占空间已经被回收。故而输出乱码。

堆和栈

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

int main(void)
{
        char *p;
        p       = NULL;

        p       = (char *)malloc(sizeof(char) * 10);
        if(p){
                *(p + 1)        = 'h';
                printf("the seconde byte content: %c\n", *(p+1));
        }

        if(p){
                free(p);
                p       = NULL;
        }
        return 0;
}
  • 程序运行到“char *p;”语句时,由于p为局部变量,p指针变量的内存在栈上
  • p       = (char *)malloc(sizeof(char) * 10);”语句欲分配大小为10字节的堆空间,分配成功后堆空间的首地址由p存储。堆空间的首地址不容弄掉。
  • 接下来往堆空间的第二个字节内存入’h’字符并将其打印出来,这些都是操作堆空间的行为。
  • 若堆空间分配成功后,手动将其释放掉。free(p)只是表示将堆空间回收,但p原本指向堆空间的值还存在,故而需要将p重新赋值为NULL,以避免使用野指针。

 

其它

对于其它前辈所标识的程序中的何种数据位于RAM中的哪个区,在不需要高度把握整个程序的运行情况下,似乎作用不是很大。

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

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

int main(void)  
{  
	int b;// 栈  
	char s[] = "abc"; //栈  
	char *p2; //栈  
	char *p3 = "123456"; //"123456/0"在常量区,p3在栈上。  
	static int c =0; //全局(静态)初始化区  
	p1 = (char *)malloc(10);  
	p2 = (char *)malloc(20);  //分配得来得10和20字节的区域就在堆区。  
	strcpy(p1, "123456"); //123456/0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。  
}  


 

不禁猜之,内存硬件设计者肯定将内存划分了区域。如存储引导程序BIOS的ROM地址范围为0xyyyyyyyy ~ 0xzzzzzzzz。RAM代码段的地址范围为0xmmmmmmmm ~ 0xnnnnnnnn……清楚内存每个区域的范围,也就更加深入的了解了内存,可以直接对内存编写代码(不知道危险与否)。也知道了每个区域的内存大小,也就知道每种类型的数据最多能够定义多少,对编写程序实在是有帮助,尤其是对编写到嵌入式、单片机之类的程序。大师一样。继续探知。

 

此次笔记记录完毕。

0
0
查看评论

动态内存分配 [李园7舍_404]

上次聊C语言的时候聊到关于C语言的不同数据变量类型的存储方式。包括变量的存储位置,什么时候为其分配内存,未被初始化时的初始值,作用域,生存期等话题。并留了一个关于“堆”的动态内存分配问题。趁课余时间聊聊动态内存分配及使用动态内存分配时需要注意的问题。 为什么要动态内存分配 栈空间大小有限 这主要是...
  • misskissC
  • misskissC
  • 2012-10-21 13:06
  • 1342

内部排序算法实现(C) [李园7舍_404]

(C)涉及C中的常见内部排序。   1 内部排序 排序:将数据或者记录的任意序列重新排列成按照某个关键字的某种顺序的序列。 排序分类:按照被排序列的数量可分为内部排序和外部排序。 外部排序:如因被排序列的数量过大时,在排序过程中不得不访问外存的排序过程。 内部排序:全部被排元素在排序过程中...
  • misskissC
  • misskissC
  • 2013-11-04 21:35
  • 1327

C程序的内存布局(Memory Layout)

C语言程序的内存布局 C语言程序的内存布局结构,包括连接过程中目标程序各个段的组成和运行过程中各个段加载的情况。 C语言程序在内存中各个段的组成 C语言程序连接过程中的特性和常见错误 C语言程序的运行方式 C语言程序的存储区域       ...
  • gatieme
  • gatieme
  • 2015-02-06 20:26
  • 17168

ARM11Mini6410 Android 开发环境搭建 [李园7舍_404]

1 Android 系统移植 首先面临的一个任务就是给ARM11 Mini6410开发板安装操作系统。可在ARM11 Mini6410开发板之上安装WindowsCE系统、Android系统及Linux系统。此次是开发Android应用程序,需要将Android系统移植到ARM11 Mini6...
  • misskissC
  • misskissC
  • 2013-07-23 19:48
  • 5497

51 数码管动态显示 [李园7舍_404]

首先根据位选与段选理念(数码管静态显示一文)编写程序去控制不同数码管显示不同的数字。根据程序控制需要,在调试程序的过程中明白需要了解我们所要的控制对象(数码管)的一些特性。由于我自己拥有的这个单片机非常的简单,只有四个数码管。我的目标是通过编写C语言程序经编译连接后下载到单片机中,使各个灯依次显示数...
  • misskissC
  • misskissC
  • 2012-10-13 14:24
  • 2229

C语言 常见的文件操作函数 [李园7舍_404]

文件经打开后,就可以对它进行读写操作了,文件读写的方式可分为顺序读写和随机读写。顺序读写指从文件第一个数据开始,按照数据在文件中的排列顺序逐个地读写;随机读写是指不按照数据在文件中的排列顺序,而是随机的对文件中数据进行读写。 用函数读取文件,只有当读取到文件结束字符后才能用此字符来判断文件内容是否已...
  • misskissC
  • misskissC
  • 2013-07-17 18:31
  • 1424

C语言数据在内存中的存储类别 [李园7舍_404]

探究C语言不同数据类型在内存中的存储很有意思,也是学习c语言时的难点。1的内容推进2的理解,含各变量的小特性哦。   1.程序存储区域 程序(可执行文件)进行存储时分为三个区域:代码段,数据段,bss段。      代码段:存放CP...
  • misskissC
  • misskissC
  • 2012-10-16 17:54
  • 2691

C 指针的值来源 [李园7舍_404]

在linux平台下写的代码。 今天一个敖师妹上完C语言课后问我为什么要用malloc函数对一个指针分配内存(其实不是对一个指针内存分配的哈,就是分配了一段大小符合相应数据结构的内存块,首地址归指针管而已,然后这块内存就是用来发挥内存作用存数据的)。直接初始化不就好了么,还有她不知道为什么要用指针,...
  • misskissC
  • misskissC
  • 2013-05-07 20:54
  • 2009

Linux 汇编程序 系统调用 [李园7舍_404]

《professiona Assembly Language》 Richard Blum一书不错,练习一下读字母的能力。   大多数操作系统提供许多应用程序能够访问的内核函数,Linux操作系统亦不例外。Linux提供许多用户程序能够访问的内核函数。用户程序通过访问这些内核函数能够更易...
  • misskissC
  • misskissC
  • 2013-11-16 16:36
  • 2234

解析BMP文件 [李园7舍_404]

位图(bitmap)是使用像素矩阵来表示的图像。BMP文件中包含了位图中所有点的信息,每个像素的颜色信息由RGB值或者会读值表示。单个像素颜色信息所需的数据可以用1、4、8、24、及32位等容量在存储,位数越高表示颜色越丰富,相应的数据量越大。   1.位图各点的颜色数据是按点的位置从...
  • misskissC
  • misskissC
  • 2012-09-06 22:13
  • 981
    个人资料
    • 访问:1820570次
    • 积分:19710
    • 等级:
    • 排名:第520名
    • 原创:385篇
    • 转载:0篇
    • 译文:42篇
    • 评论:409条
    文章分类