是否可以指示C不为零初始化全局数组?

是否可以指示C不为零初始化全局数组?,c,gcc,embedded,c99,C,Gcc,Embedded,C99,我正在编写一个嵌入式应用程序,几乎所有的RAM都被全局字节数组使用。当我的固件启动时,它首先用零覆盖RAM中的整个BSS部分,这在我的情况下是完全不必要的 是否有某种方法可以指示编译器不需要对某些数组进行零初始化?我知道这也可以通过将它们声明为指针并使用malloc()来解决,但我想避免这样做的原因有几个。您确定二进制格式实际上包含二进制中的BSS部分吗?在我使用过的二进制格式中,BSS只是一个整数,它告诉内核/加载程序要分配多少内存并将其归零 在C中,绝对没有获得未初始化全局变量的通用方法。这

我正在编写一个嵌入式应用程序,几乎所有的RAM都被全局字节数组使用。当我的固件启动时,它首先用零覆盖RAM中的整个BSS部分,这在我的情况下是完全不必要的


是否有某种方法可以指示编译器不需要对某些数组进行零初始化?我知道这也可以通过将它们声明为指针并使用malloc()来解决,但我想避免这样做的原因有几个。

您确定二进制格式实际上包含二进制中的BSS部分吗?在我使用过的二进制格式中,BSS只是一个整数,它告诉内核/加载程序要分配多少内存并将其归零


在C中,绝对没有获得未初始化全局变量的通用方法。这将是编译器/链接器/运行时系统的一个功能,并且非常具体。

问题是标准C强制静态对象零初始化。如果编译器跳过它,它将不符合C标准

在嵌入式系统编译器上,通常有一个非标准选项“紧凑启动”或类似选项。启用后,在程序中的任何位置都不会初始化静态/全局对象。如何做到这一点取决于编译器,或者在本例中取决于gcc端口

如果您提到您正在使用的系统,可能有人能够为特定的编译器端口提供解决方案


这意味着您显式初始化的任何静态/全局(静态存储持续时间)变量将不再被初始化。您必须在运行时初始化它,也就是说,而不是
static intx=1您必须编写
静态int x;x=1。以这种方式编写嵌入式C程序是很常见的,以使其与禁用静态初始化的编译器兼容。

C标准要求全局数据初始化为零

一些嵌入式系统制造商可能会提供一种绕过此选项的方法,但如果不执行“初始化为零”,肯定会有许多典型应用程序失败

某些编译器还允许您拥有更多的节,这些节可能具有“bss”节以外的其他特性

另一种选择当然是“自行分配”。由于它是一个嵌入式系统,我想您可以控制如何将应用程序和数据加载到RAM中,特别是使用什么地址


因此,您可以使用指针,只需使用自己的机制将指针分配给内存区域,该内存区域是为您需要的大型数组保留的。这避免了使用相当复杂的
malloc
——并且它提供了一个或多或少的永久地址,因此您不必担心以后试图找到数据的位置。这当然对性能影响很小,因为它增加了另一个间接级别,但在大多数情况下,当数组用作函数的参数时,这种间接级别就会消失,因为它会在该点衰减为指针。

有一些解决方法,如:

  • 从二进制文件中删除BSS节或将其大小设置为0或1。如果加载程序必须显式地为所有节分配内存,那么这将不起作用。如果加载程序只是将数据复制到RAM中,这将起作用
  • 在C代码中将数组声明为
    extern
    ,并在单独程序集文件的程序集代码或链接器脚本中定义符号(及其地址)。同样,如果必须显式分配内存,这将不起作用
  • 在加载程序中或在
    main()
    之前的程序中执行的启动代码中修补或删除相关BSS归零代码

事实证明,我的工具链中包含的链接器脚本有一个特殊的“noinit”部分

/**强制编译器不自动将给定的全局变量归零 变量,以便保留当前RAM内容。 在大多数情况下,由于 易失性存储器在断电后的行为,但可用于某些特定场合
在某些情况下,例如在系统看门狗重置后将值传回


因此,在引导过程中,所有标记有该属性的全局变量都不会初始化为零。

所有嵌入式编译器都应该允许使用noinit段。使用IAR AVR编译器,您不想初始化的变量只需声明如下:

__没有初始uint16\u t foo

这样做最有用的原因是允许变量通过看门狗或褐化重置来保持其值,这当然不会发生在基于计算机的C程序中,因此标准C中省略了它


只需在编译器手册中搜索“noinit”或类似内容。

使用gcc,在bss中初始化-fno-zero实际上不应该。通常,链接器只保存bss节的大小,而不保存它的(空)内容。固件只是清除所需的内存。如果不为阵列保留RAM,您打算如何使用它们?@fuzzxl您是对的,我以为它是在将零从闪存复制到RAM,但我现在看到引导加载程序只是将等于BSS部分大小的零写入RAM。这似乎仍然没有必要,因为我不依赖于大多数数组的零初始化,但它不会像我所想的那样影响性能。我编辑了我的问题以反映您的信息。我认为这取决于工具链。在我使用的工具链中,我可以指示链接器不要初始化或加载节。对于某些部分(多个核心共享数据),这实际上是强制性的。这是我的正常操作(不归零bss,不复制.data)查找并更改启动代码(在gnu工具链世界中通常称为crt0.S)或创建自己的启动代码。你说得对,我以为是copyi
__attribute__ ((section (".noinit")))