具有最小RAM的C程序

我想了解应用程序开发的C和C ++编程中的内存管理。 应用程序将在PC上运行。

如果我想在运行时制作一个尽可能少使用RAM的程序,那么在编程时我需要考虑哪些要点?

根据我的理解,这里有两点,但我不确定:

(1)在main()和其他函数中使用最小局部变量。 由于局部变量保存在堆栈中,这是RAM吗?

(2)代替局部变量,在顶部使用全局变量。 全局变量保存在未初始化和初始化的ROM区域中?

谢谢。

1)通常,在堆栈上分配的替代方法是在堆上进行分配(例如,使用malloc ),由于簿记/等等实际上具有更大的开销,并且堆栈已经为其保留了内存,因此尽可能在堆栈上进行分配通常是优选的。 另一方面,堆栈上的空间较少,而在具有虚拟内存和64位地址空间的现代系统上,堆可以接近“无限制”。

2)在PC和其他非嵌入式系统中,程序中的所有内容都在RAM中,即它不会闪存到类似ROM的内存中,因此全局与本地在这方面没有帮助。 此外,只要应用程序正在运行,全局变量†就会“生存”,而本地人可以根据需要分配和释放(在堆栈或堆上),因此是优选的。

†更准确地说,还可能存在具有static持续时间的局部变量,以及具有全局范围的变量,这些变量是指向动态分配的内存的指针,因此这里使用的术语local和global非常松散。

一般来说,现代桌面/笔记本电脑甚至移动操作系统都非常擅长管理内存,因此您可能不应该尝试微观优化所有内容,因为实际上您可能弊大于利。

如果你确实需要降低程序的内存占用,你必须意识到程序中的所有内容都存储在RAM中,因此你需要努力减少你拥有的东西的数量和大小,而不是试图耍弄他们的位置。 您可以在PC上本地存储物品的另一个地方是硬盘驱动器,因此在那里存储大量资源并且只根据需要加载它们(最好只是所需的部件)。 但请记住,磁盘访问速度比内存访问慢几个数量级,并且如果内存已满,操作系统也可以将内容交换到磁盘。

程序代码本身也存储在RAM中,因此您的编译器可以针对大小进行优化(许多常见编译器中的-Os/Os选项)。 还要记住,如果通过编写更复杂的代码在变量中节省一些空间,则可能会因增加的代码大小而无法完成工作; 保存您的优化以获得大赢(例如,压缩大型资源将需要添加的解压缩代码,但仍可能产生大的净赢)。 如果同时运行的多个程序使用相同的库,则使用动态链接库(和其他资源)也有助于系统的整体内存占用。

(注意,上面的一些不适用于嵌入式开发,例如,代码和静态常量可能确实存储在闪存而不是RAM等)

这很难,因为在你的电脑上,程序将耗尽RAM,除非你能以某种方式执行ROM或Flash。

以下是要考虑的要点:

减少代码大小。
代码占用RAM。

减少可变数量和大小。
变量需要存在于某个地方,并且某处在RAM中。

减少字符文字。
他们也占据了空间。

减少函数调用嵌套。
函数可能需要参数,这些参数放在RAM中。 调用其他函数的函数需要返回路径; 路径存储在RAM中。

使用其他设备的RAM。
其他设备(如图形处理器和硬盘适配卡)可能具有可以使用的RAM。 如果使用此RAM,则不使用主RAM。

页面内存到外部设备。
操作系统具有虚拟内存function,可以将内存分页到外部设备,例如硬盘驱动器。

编辑1 – 动态库如果要减少程序的RAM占用空间,可以分配一个替换库函数的区域。 这类似于DLL概念。 需要某个function时,将其从硬盘驱动器加载到保留区域。

通常,将为堆栈分配一定量的空间; 无论是否使用,此类空间将无法用于其他目的。 如果空间不足,程序将会死于可怕的死亡。

将使用寄存器和堆栈空间的某种组合来存储局部变量。 有些编译器会对程序执行中不同时间“活动”的变量使用相同的寄存器或堆栈空间; 别人不会。 此外,函数参数通常在调用函数之前被压入堆栈,并且在调用者方便时被移除。 在评估代码序列时:

 function1(1,2,3,4,5); function2(6,7,8,9,10); 

第一个函数的参数将被压入堆栈并调用该函数。 此时编译器可以从堆栈中删除这五个值,但由于单个指令可以删除任意数量的推送值,因此许多编译器将推送第二个函数的参数(将第一个参数留在堆栈中),调用第二个函数,然后使用一条指令消除所有十个。 通常这将是一个非问题,但在一些深度嵌套的递归方案中,它可能是一个问题。

除非按照今天的标准开发的“PC”很小,否则我不会过分担心尝试微量优化RAM的使用。 我已经为只有25字节RAM的微控制器开发了代码,甚至编写了成熟的游戏,用于基于微处理器的控制台,内存高达128字节(不是KB!)的RAM,并且在这样的系统上是有意义的担心每个字节。 但是,对于PC应用程序来说,担心单个字节的唯一时间是它们是数据结构的一部分,它将在RAM中复制数千次。

您可能想要一本关于“嵌入式”编程的书。 这本书可能会讨论如何减少内存占用,因为嵌入式系统比现代桌面或服务器系统更受限制。

使用“本地”变量时,它们将保存在堆栈中。 只要你不使用太多堆栈,这基本上就是空闲内存,就像返回内存时退出的function一样。 多少“太多”变化…最近我不得不在一个系统上工作,每个进程的堆栈数据限制为8 KB。

当您使用“全局”变量或其他静态变量时,您使用的内存将与程序的持续时间相关联。 因此,您应该最小化对全局变量的使用,和/或找到在程序中的多个函数之间共享相同内存的方法。

我为几年前写的一个项目写了一篇相当精细的“对象管理器”。 函数可以使用“get”操作借用一个对象,然后在借用该对象时使用“release”操作。 这意味着系统中的所有function都可以通过轮流使用共享对象来共享相对少量的数据空间。 由你来决定是否值得花时间构建一个“对象管理器”的东西,或者你是否有足够的内存来使用简单的变量。

只需简单地调用malloc()free()就可以获得“对象管理器”的大部分好处。 然后堆分配器为您管理共享资源,即堆内存。 我写自己的“对象管理器”的原因是需要速度。 我的系统一直使用相同的数据对象,只是继续使用相同的数据对象比继续释放它们并再次动画它们更快。 此外,我的系统可以在DSP芯片上运行,而malloc()在某些DSP架构上的function可能会非常慢。

如果一个函数试图保持全局缓冲区而另一个函数覆盖数据,则使用相同的全局变量具有多个函数可能会导致棘手的错误。 因此,如果使用malloc()free() ,只要每个函数只写入为自己分配的数据,那么您的程序可能会更强大。 (但是malloc()free()可以引入自己的bug:内存泄漏,双重自由错误,在它指向的数据被释放后继续使用指针…如果你使用malloc()并且free()务必使用Valgrind等工具检查代码。)

根据定义,任何变量都必须存储在读/写存储器(或RAM)中。 如果您正在讨论最初在ROM中使用代码的嵌入式系统,那么运行时会将您识别的ROM映像复制到RAM中以保存全局变量的值。

在运行期间,只有标记为不可更改的项(const)可以保留在ROM中。

此外,您需要减少程序调用结构的深度,因为每个函数调用都需要堆栈空间(也在RAM中)来记录返回地址和其他值。

为了最大限度地减少内存的使用,您可以尝试使用register属性标记局部变量,但编译器可能不会这样做。

另一种常见技术是在需要时动态生成大型可变数据,以避免必须创建缓冲区。 这些通常占用更多空间的简单变量。

如果这是一台PC,那么默认情况下,您将获得一定大小的堆栈(您可以将其设置为更大或更小)。 使用此堆栈比使用全局变量更有效。 因为你的ram用法将是固定的堆栈大小+全局变量+其他东西(程序,堆等)。 堆栈充当可恢复的内存块。