我正在尝试在低资源应用程序中完全禁用动态内存分配。
这很不寻常。一般来说,人们限制他们的(托管)应用程序使用的动态内存(以及如何做到这一点是一个不同的问题,通常是operating system 特定的)。为什么要完全禁用它?如下所述,动态内存很可能在您的C standard library 实现中在内部使用。
仔细阅读 C11 标准 n1570(或 C99 标准)。
C 基本上有两种“模式”或两种“方言”:宿主 C 语言和独立 C 语言。标准的§4 Conformance 中的确切措辞是
一致性实现的两种形式是托管的和独立的。符合要求的托管实现应接受任何严格符合要求的程序。符合标准的独立实现应接受任何严格符合标准的程序,其中库条款(第 7 条)中指定的功能的使用仅限于标准头文件<float.h>、<iso646.h>、<limits.h>、<stdalign.h> 的内容、<stdarg.h>、<stdbool.h>、<stddef.h>、<stdint.h> 和 <stdnoreturn.h>。
并且malloc 已定义(在<stdlib.h> 中声明)并且应该可用于托管 实现,并且通常在独立实现中不可用(但这是特定于实现的)。
显然,您使用的是独立实现(因为您没有标准要求托管实现的malloc)。 GCC has -ffreestanding 模式。你应该使用它。那么<stdlib.h> 不可用,并且您的代码不能在该模式下使用标准malloc(除非它明确声明 malloc)。
在托管实现中,您通常可以重新定义您的malloc(前提是它仍然具有标准要求的所有属性)。然后你可能会使用this 之类的东西(总是失败,但仍然符合标准,malloc 实现)。
最后,如果您使用 GNU binutils 链接器,如果您的目标文件包含对 malloc 的任何外部引用,则链接总是会失败。通过在Makefile(可能使用nm)中添加一些特定的配方或规则,或者使用任何像样的build automation 工具(如果您的构建自动化之前不允许这样的检查),这很容易实现链接,切换到一个:make、ninja、omake 和许多其他......)。
如果您想在托管环境中编译时检测malloc的任何使用,您可以编写自己的GCC plugin这样做(我觉得是矫枉过正,但选择是你的)。或者(在实践中更简单)使用一些脚本(例如使用 grep)检测 C 源代码中出现的 malloc 或 calloc 单词。
请注意,在大多数托管实现中,在实践中,fprintf、fopen、printf、fputc(以及许多其他)等标准函数在内部 - 至少有时 - 使用 malloc。具体来说,如果您的程序(在托管实现之上)使用fopen,它很可能间接使用malloc,因为在标准FILE 内通常有一些堆分配缓冲区fopen 是malloc- ing(通常在fclose 时间得到free-d)。
有没有办法强制不能进行动态内存分配,如果是这样,构建会失败?
在实践中,是的。只需在您的Makefile 中添加一些脚本即可进行此类检查。在源文件上使用grep,或者在目标文件上使用nm。但是如果你在代码中使用标准的fopen(来自<stdio.h>),它通常会在内部做一些malloc。
或者,定义您自己的总是失败的malloc 和calloc 和琐碎的free(如here)
在许多operating systems(您的应用程序使用的那个,如果有的话)上,有一种方法可以在运行时限制堆内存。 Linux 有setrlimit(2) 和RLIMIT_DATA。
如果您正在使用一些 free software 或开源 C standard library 实现(在托管环境中),例如 GNU glibc 或 musl-libc,您可以研究其源代码并检查 fopen 是否使用堆记忆。