如果我在嵌入式系统中定义宏,或使用静态const值,则 将使用哪种内存,芯片闪存或芯片内存? 哪种方式更好?
答案 0 :(得分:16)
我相信答案更复杂。
编辑:我为使用'should'和'might'道歉,但没有特定的编译器或调试器,我发现它必须准确和准确。也许如果问题可以说明目标是什么编译器和平台,我们可以更清楚了吗?
#define NAME ((type_cast)value)
在代码中显示之前不会占用任何空间。编译器可能能够使用其值来推断某些东西(与使用具有未知运行时值的变量相比),因此可能会更改生成的代码,使其有效地不占用空间,甚至可能减小代码的大小。如果编译器的分析是在运行时需要文字值,那么它会占用代码空间。字面值是已知的,因此编译器应该能够分配最佳空间量。根据处理器的不同,它应该存储在闪存中,但可能不是内联代码,而是在“文字池”中,一组局部变量,通常靠近代码,因此可以使用紧凑的地址。编译器可能会做出正确的决定。
static const type name = value;
在代码中使用之前不应占用空间。即使它在代码中使用,它可能会或可能不会消耗“空间”,具体取决于您的编译器(我认为,它正在编译的C标准)以及代码如何使用该值。
如果从未采用名称的地址,则编译器不必存储它。如果获取值的地址(并且该代码未被删除),则该值必须在内存中。智能编译器将检测源文件中的任何代码是否采用其地址。即使它可能存储,编译器也可能通过不使用存储的值生成更好(更快或更紧凑的代码)。
编译器可能会像#define NAME
一样做得好,尽管它可能比#define
更糟糕。
如果值具有其地址,则编译器将该变量视为初始化变量,这会消耗空间来存储常量值。编译器并没有真正将值放入RAM或闪存中。这取决于链接器。在gcc中,有“属性”可用于告诉链接器将变量放入哪个段。默认情况下,编译器将初始化变量放入默认数据段,并将const
初始化为只读段。通过使用属性,程序员可以将变量放入任何段。使用适当的链接器脚本(通常随工具链一起提供),可以将段放入闪存中。 Gcc将readonly数据段用于文字字符串等数据。
name
应该在调试器中可用,但#define NAME
不会。
还有第三种方法,即使用枚举:
enum CONSTANTS {name = 1234,height = 456 ...};
编译器可能会对这些问题进行处理,例如#define constants
,尽管它们不够灵活,因为它们的大小为int
(IIRC)。无法获取枚举值的地址,因此编译器可以使用尽可能多的选项来生成#define NAME
的良好代码。它们通常在调试器中可用。
const type name = value;
可能会消耗RAM。它必须在内存中,因为编译器无法知道不同文件中的代码是否使用它,或者获取其地址(但gcc LTO可能会改变它)const
告诉编译器'警告'(或'错误)任何代码尝试使用赋值运算符更改值e,g。通常,RAM中保存的变量存储在数据或bss内存段中。默认情况下,gcc将const
放入只读段,该段可以使用命令行选项-mrodata=
readonly-data-section 进行设置。该段是ARM上的.rodata。
在嵌入式系统上,所有初始化的全局和静态变量(const
或不是)都保存在flash中,并在程序启动时(调用main()
之前)复制到RAM中。在调用main()
之前,所有未初始化的全局或静态变量都设置为0。
编译器可能会将const
变量放入它们自己的内存段(gcc中),这可能允许链接器(例如ld)脚本将它们放入闪存中,而不是为它们分配任何RAM(这不会'在例如AVR ATmega上工作,它使用不同的imstructions来加载来自flash的数据)。
答案 1 :(得分:3)
好吧,如果你#define一个宏,没有为它分配额外的内存或代码空间(flash)。所有工作都在编译阶段完成。
如果使用static const
全局变量,将生成二进制代码以获取初始值和为其分配的内存。使用flash(bin文件大小更大)和内存(chip ram)。
答案 2 :(得分:2)
除了其他说法: