I have read a few questions on the topic:
and I understand that enum are usually preferred on the #define
macros for a better encapsulation and/or readibility. Plus it allows the compilers to check for types preventing some errors.
const
declaration are somewhat in between, allowing type checking, and encapsulation, but more messy.
Now I work in Embedded applications with very limited memory space (we often have to fight for byte saving). My first ideas would be that constants take more memory than enums. But I realised that I am not sure how constants will appear in the final firmware.
Example:
enum { standby, starting, active, stoping } state;
In a resource limited environment, how does the enum
vs #define
vs static const
compare in terms of execution speed and memory imprint?
To try to get some substantial elements to the answer, I made a simple test.
I wrote a simple C program 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;
}
while constants.h contains
#define LOOP_SIZE 10000000
#define MACHINE_LOOP 100
And I considered three variants to define the state constants. The macro as above, the enum:
enum {
STATE_STANDBY=0,
STATE_START,
STATE_RUN,
STATE_STOP
} possible_states;
and the 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;
while the rest of the code was kept identical.
Tests were made on a 64 bits linux machine and compiled with gcc
gcc main.c -o main
gives
macro: 7310 bytes
enum: 7349 bytes
const: 7501 bytes
gcc -O2 main.c -o main
gives
macro: 7262 bytes
enum: 7301 bytes
const: 7262 bytes
gcc -Os main.c -o main
gives
macro: 7198 bytes
enum: 7237 bytes
const: 7198 bytes
When optimization is turned on, both the const and the macro variants come to the same size. The enum is always slightly larger. Using gcc -S
I can see that the difference is a possible_states,4,4
in .comm. So the enum is always larger than the macro. and the const can be larger but can also be optimized away.
I checked a few sections of the programs using objdump -h main
: .text, .data, .rodata, .bss, .dynamic. In all cases, .bss has 8 bytes, .data, 16 bytes and .dynamic: 480 bytes.
.rodata has 31 bytes, except for the non-optimized const version (47 bytes).
.text goes from 620 bytes up to 780 bytes, depending on the optimisation. The const unoptimised being the only one differing with the same flag.
I ran the program a few times, but I did not notice a substantial difference between the different versions. Without optimisation, it ran for about 50 seconds. Down to 20 seconds with -O2
and up to more than 3 minutes with -Os
. I measured the time with /usr/bin/time
.
Using time -f %M
, I get about 450k in each case, and when using valgrind --tool=massif --pages-as-heap=yes
I get 6242304 in all cases.
Whenever some optimisation has been activated, the only notable difference is about 40 Bytes more for the enum case. But no RAM or speed difference.
Remains other arguments about scope, readability... personal preferences.
and I understand that enum are usually preferred on the #define macros for a better encapsulation and/or readibility
Enums are preferred mainly for better readability, but also because they can be declared at local scope and they add a tiny bit more of type safety (particularly when static analysis tools are used).
Constant declaration are somewhat in between, allowing type checking, and encapsulation, but more messy.
Not really, it depends on scope. "Global" const can be messy, but they aren't as bad practice as global read/write variables and can be justified in some cases. One major advantage of const
over the other forms is that such variables tend to be allocated in .rodata
and you can view them with a debugger, something that isn't always possible with macros and enums (depends on how good the debugger is).
Note that #define
are always global and enum
may or may not be, too.
My first ideas would be that constants take more memory than enums
This is incorrect. enum
variables are usually of type int
, though they can be of smaller types (since their size can vary they are bad for portability). Enumeration constants however (that is the things inside the enum declaration) are always int
, which is a type of at least 16 bits.
A const
on the other hand, is exactly as large as the type you declared. Therefore const
is preferred over enum
if you need to save memory.
In a resource limited environment, how does the enum vs #define vs static const compare in terms of execution speed and memory imprint?
Execution speed will probably not differ - it is impossible to say since it is so system-specific. However, since enums tend to give 16 bit or larger values, they are a bad idea when you need to save memory. And they are also a bad idea if you need an exact memory layout, as is often the case in embedded systems. The compiler may however of course optimize them to a smaller size.
Misc advice:
stdint.h
types in embedded systems, particularly when you need exact memory layout. const
is ideal when you need something to be stored in flash. Such variables get their own address and are easier to debug.#define
will always end up in .text flash memory, while enum and const may end up either in RAM, .rodata
flash or .text
flash.const
have ended up in RAM by mistake (a bug).If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With