【问题标题】:C Keil compiler uses malloc for local variables,why?C Keil编译器使用malloc作为局部变量,为什么?
【发布时间】:2020-06-14 10:33:03
【问题描述】:

我的代码中某处有问题,我想声明一个数组,但它失败了。经过一些调试,我发现它在反汇编窗口中使用了 malloc,所以我增加了堆大小并且它工作正常!

所以我的问题是为什么 keil 使用 Heap 作为局部变量?

这里是变量声明代码:

uint8_t result[data->capacityBytes];
memset(result, 0, sizeof(result));

我添加了标志 C99

【问题讨论】:

    标签: c malloc heap-memory c99 keil


    【解决方案1】:

    您的数组具有动态大小,即编译器直到运行时才知道它有多大。这是 C99 中引入的称为可变长度数组 (VLA) 的功能。

    根据Keil 的documentation(见注),这样的数组是由这个编译器在堆上分配的。 (其他人可能在堆栈上分配。其他人可能根本没有实现此功能 - 它在 C11 中成为可选。)

    【讨论】:

    • 请注意,在堆上而不是在堆栈上分配可能较大的结构是有充分理由的:堆栈空间是有限的,并且/或者可能不会无限快速增长。因此,与在堆上分配数组相比,一次在堆栈上分配几个 MiB 会使应用程序崩溃的风险要高得多。奖牌的另一面是,堆栈分配要快得多。
    • 我使用的最后一个 Keil 编译器甚至没有用于局部变量的堆栈
    • @M.M 可以吗?我一直以为是C语言的要求
    • @Alireza -- C 标准根本不讨论堆栈或堆;这些概念属于实现细节。
    • @Alireza:一些 Keil 编译器的目标平台是支持递归的机器代码函数至少是不支持递归的机器代码函数的两倍大和慢; Keil 认识到,为它接受的程序生成高效代码的不太符合标准的编译器在许多用途上比生成效率极低的符合标准的编译器更有用。
    【解决方案2】:

    您的局部变量 result 声明如下:

    uint8_t result[data->capacityBytes];

    假设data->capacityBytes 不是一个常量,这意味着result 将是一个Variable Length Array (VLA),这将解释您正在使用的编译器的行为。

    那么你假设内存区域中的变量位置是标准化的,不幸的是这是不正确的,如answer中所述:

    C 语言实际上并没有定义任何变量的存储位置。但是,它确实定义了三个存储类:静态、自动和动态。

    变量的存储位置取决于编译器对源代码的解释。

    另见wikipedia entry about variable length array

    内存

    分配

    【讨论】:

    • data->capacityBytes 不能是常量,const 变量不是常量,它符合普通数组的条件。 const int a = 5; int b[a]; 使 b 成为 VLA。
    • 有没有我可以定义这些变量的位置?
    • @Alireza VLA 存在一个固有问题,堆栈分配是假设运行时如何从分配问题中恢复,在大多数情况下简单地崩溃不是一个选项。显式堆栈分配可以通过alloca 进行,但也有其自身的问题(特别是无法显式释放分配的内存),应不鼓励不受控制的堆栈分配(如果在 C 代码库中不被禁止)
    • @dvhh:对于不需要递归且不使用 VLA 的项目,可以静态验证堆栈使用情况。即使对于那些确实涉及递归的情况,也可以借助指令来静态验证堆栈的使用情况,以协助此类验证和/或内在函数来测试是否有足够的堆栈来执行“有用”分支上的代码,并回退到否则“无用但安全”的分支。用于此类事情的工具似乎并不常见,但与编译器的一点合作应该不会太困难。然而,VLA 会使事情变得非常复杂。
    • 栈分配比堆分配好还是差取决于目标平台。在 Cortex-M0 上,函数内任何 VLA 分配的存在都会降低访问函数内自动对象(不仅仅是 VLA!)的效率。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-25
    • 2019-11-08
    • 1970-01-01
    • 1970-01-01
    • 2012-03-03
    相关资源
    最近更新 更多