我已经阅读了一些关于这个主题的问题:
我知道在#define
宏上通常首选枚举,以获得更好的封装和/或可读性。此外,它允许编译器检查类型以防止出现一些错误。
const
声明介于两者之间,允许进行类型检查和封装,但更加混乱。
现在我在内存空间非常有限的嵌入式应用程序中工作(我们经常需要争取字节保存)。我的第一个想法是常量比枚举占用更多的内存。但我意识到我不确定最终固件中会出现常数。
示例:
enum { standby, starting, active, stoping } state;
在资源有限的环境中,enum
vs #define
vs static const
在执行速度和内存印记方面有何比较?
答案 0 :(得分:4)
为了尝试获得一些重要的元素,我做了一个简单的测试。
我写了一个简单的C程序 main.c :
#include <stdio.h>
#include "constants.h"
// Define states
#define STATE_STANDBY 0
#define STATE_START 1
#define STATE_RUN 2
#define STATE_STOP 3
// Common code
void wait(unsigned int n)
{
unsigned long int vLoop;
for ( vLoop=0 ; vLoop<n*LOOP_SIZE ; ++vLoop )
{
if ( (vLoop % LOOP_SIZE) == 0 ) printf(".");
}
printf("\n");
}
int main ( int argc, char *argv[] )
{
int state = 0;
int loop_state;
for ( loop_state=0 ; loop_state<MACHINE_LOOP ; ++loop_state)
{
if ( state == STATE_STANDBY )
{
printf("STANDBY ");
wait(10);
state = STATE_START;
}
else if ( state == STATE_START )
{
printf("START ");
wait(20);
state = STATE_RUN;
}
else if ( state == STATE_RUN )
{
printf("RUN ");
wait(30);
state = STATE_STOP;
}
else // ( state == STATE_STOP )
{
printf("STOP ");
wait(20);
state = STATE_STANDBY;
}
}
return 0;
}
而 constants.h 包含
#define LOOP_SIZE 10000000
#define MACHINE_LOOP 100
我考虑了三种变体来定义状态常量。如上所述的宏,枚举:
enum {
STATE_STANDBY=0,
STATE_START,
STATE_RUN,
STATE_STOP
} possible_states;
和const
:
static const int STATE_STANDBY = 0;
static const int STATE_START = 1;
static const int STATE_RUN = 2;
static const int STATE_STOP = 3;
,其余代码保持相同。
在64位Linux机器上进行测试,并使用gcc
gcc main.c -o main
给出了
宏:7310字节
枚举:7349字节
const:7501字节
gcc -O2 main.c -o main
给出了
宏:7262字节
枚举:7301字节
const:7262字节
gcc -Os main.c -o main
给出了
宏:7198字节
枚举:7237字节
const:7198字节
启用优化后,const和宏变体的大小相同。枚举总是略大。使用gcc -S
我可以看到.comm中的差异是possible_states,4,4
。所以枚举总是大于宏。 const可以更大,但也可以优化。
我使用objdump -h main
检查了程序的几个部分:.text,.data,.rodata,.bss,.dynamic。在所有情况下,.bss有8个字节,.data,16个字节和.dynamic:480个字节。
.rodata有31个字节,但非优化的const版本(47字节)除外。
.text从620字节到780字节,具体取决于优化。未被优化的const是唯一一个与同一旗帜不同的人。
我运行了几次程序,但我没有发现不同版本之间存在实质性差异。没有优化,它运行大约50秒。 -O2
最多20秒,-Os
最多3分钟。我用/usr/bin/time
测量了时间。
使用time -f %M
,我在每种情况下得到大约450k,而在使用valgrind --tool=massif --pages-as-heap=yes
时,我得到6242304。
每当激活一些优化时,唯一值得注意的差异是枚举情况大约多40字节。但没有RAM或速度差异。
还有关于范围,可读性......个人偏好的其他论点。
答案 1 :(得分:1)
我知道在#define宏上通常首选枚举,以获得更好的封装和/或可读性
首选枚举主要是为了提高可读性,但也因为它们可以在本地范围内声明,并且它们增加了更多的类型安全性(特别是在使用静态分析工具时)。
常量声明介于两者之间,允许进行类型检查和封装,但更麻烦。
不是,它取决于范围。 &#34;全球&#34; const可能很混乱,但它们并不像全局读/写变量那样糟糕,在某些情况下可以证明是合理的。 const
优于其他形式的一个主要优点是,这些变量倾向于在.rodata
中分配,您可以使用调试器查看它们,这些内容对于宏和枚举来说是不可能的(取决于调试器的好坏。)
请注意,#define
始终是全局的,而enum
可能也可能不是。{/ p>
我的第一个想法是常量占用的内存比枚举更多
这是不正确的。 enum
变量通常是int
类型,但它们可以是较小的类型(因为它们的大小可能会有所不同,但它们对于可移植性不利)。然而,枚举常量(即枚举声明中的内容)总是 int
,这是一种至少16位的类型。
另一方面,const
与您声明的类型完全一样大。因此,如果您需要节省内存,const
优先于enum
。
在资源有限的环境中,enum vs #define vs static const在执行速度和内存印记方面有何比较?
执行速度可能没有差别 - 因为它是如此系统特定所以不可能说。但是,由于枚举值往往会提供16位或更大的值,因此当您需要节省内存时,它们是个坏主意。如果您需要精确的内存布局,那么它们也是一个坏主意,就像在嵌入式系统中一样。然而,编译器当然可以将它们优化为更小的尺寸。
其他建议:
stdint.h
类型,尤其是在需要精确内存布局时。 const
是理想的选择。这些变量有自己的地址,更容易调试。#define
将始终以.text闪存结尾,而枚举和常量可能会在RAM中.rodata
闪存或.text
闪存结束。const
的变量是否已经错误地结束在RAM中(一个错误)。答案 2 :(得分:1)
枚举,#define和static const通常会给出完全相同的代码(因此具有相同的速度和大小),具有某些假设。
声明枚举类型和枚举常量时,这些只是整数常量的名称。他们没有任何空间或时间。这同样适用于#define&#39; d值(尽管这些不限于int&#39; s。)
A&#34;静态const&#34;声明可能占用空间,通常在flash中的只读部分。通常情况下,只有在未启用优化时才会出现这种情况,但如果您忘记使用&#34;静态&#34;并写一个简单的&#34; const&#34;,或者如果你取静态const对象的地址。
对于类似给定示例的代码,结果将与所有版本相同,只要至少启用基本优化(以便优化静态const对象)。但是,样本中存在错误。这段代码:
enum {
STATE_STANDBY = 0,
STATE_START,
STATE_RUN,
STATE_STOP
} possible_states;
不仅创建枚举常量(不占空间)和匿名枚举类型,还创建一个名为&#34; possible_states&#34;的该类型的对象。这具有全局链接,并且必须由编译器创建,因为其他模块可以引用它 - 它被放在&#34; .comm&#34;共同部分。应该写的是其中之一:
// Define just the enumeration constants
enum { STATE_STANDBY, ... };
// Define the enumeration constants and the
// type "enum possible_states"
enum possible_states { STATE_STANDBY, ... };
// Define the enumeration constants and the
// type "possible_states"
typedef enum { STATE_STANDBY, ... } possible_states;
所有这些都将提供最佳的代码生成。
在比较编译器生成的大小时,请注意不要包含调试信息!给出的示例显示了枚举版本的更大的目标文件,部分原因是上面的错误,但主要是因为它是一种新类型并导致更多的调试信息。
这三种方法都适用于常数,但它们都有其特殊性。 A&#34;静态const&#34;可以是任何类型,但您不能将其用于案例标签或数组大小等内容,也不能用于初始化其他对象。枚举常量仅限于类型&#34; int&#34;。 #define宏不包含类型信息。
但是,对于这种特殊情况,枚举类型有一些很大的优点。它在一个定义中收集状态,这个定义更清晰,它允许你使它们成为一种类型(一旦你得到正确的语法:-))。当您使用调试器时,您应该看到枚举类型变量的实际枚举常量,而不仅仅是数字。 (&#34; state&#34;应声明此类型。)并且您可以从工具中获得更好的静态错误检查。而不是使用一系列&#34; if&#34; /&#34;否则如果&#34;语句,使用开关并使用&#34; -Wswitch&#34; gcc中的警告(或其他编译器的等效警告),如果您忘记了案件,则会发出警告。
答案 3 :(得分:0)
我使用了一个OpenGL库,它定义了枚举中的大多数常量,默认的OpenGL-header将其定义为#defines
。因此,作为标题/库的用户,没有太大的区别。这只是设计的一个方面。使用普通C时,没有static const
,直到它发生变化或者像(extern) static const char[] version;
这样的大字符串。对于我自己,我避免使用宏,因此在特殊情况下,标识符可以重复使用。当将C代码移植到C ++时,枚举甚至是作用域并且是Typechecked。
看点:characterusage&amp;可读性:
#define MY_CONST1 10
#define MY_CONST2 20
#define MY_CONST3 30
//...
#define MY_CONSTN N0
VS
enum MyConsts {
MY_CONST1 = 10,
MY_CONST2 = 20,
MY_CONST3 = 30,
//...
MY_CONSTN = N0 // this gives already an error, which does not happen with (unused) #defines, because N0 is an identifier, which is probably not defined
};