6

I am currently using static const in my code instead of using 'magic numbers'as mentioned in "static const" vs "#define" vs "enum".

void checkInvalidResponse (uint8_t response)
{
    static const uint8_t INVALID_RESP = 0xFF;

    if (response == INVALID_RESP)
    {
        /* Code for invalid response */
    }
}

I, however, think that using static const would consume memory in the compiled code for INVALID_RESP. The statement would also translate to machine code that does a LOAD from the memory followed by the compare, instead of comparing with a value provided as part of the instruction. Is this correct? If so, then this solution would not be optimal in terms of speed and memory right?

I am currently changing the code to use #defines

void checkInvalidResponse (uint8_t response)
{
    #define INVALID_RESP 0xFF

    if (response == INVALID_RESP)
    {
        /* Code for invalid response */
    }
}

However, since #define does not have a scope, would the behavior of the cut-and-paste aspect of #define be consistent across multiple compilers? For example, if INVALID_RESP is re-defined later, would any code in lines following the redefinition use the new value?

Another approach I have looked at is to use enumerations.

void checkInvalidResponse (uint8_t response)
{
    typedef enum
    {
        INVALID_RESP = 0xFF
    } resp_t;

    if ((resp_t)response == INVALID_RESP)
    {
        /* Code for invalid response */
    }
}

However, the typecasting to an enum would allocate more memory than needed (The processor would do a 32 bit(?) compare instead of an 8 bit compare).

What is the best method to use instead of using magic numbers in code?

Community
  • 1
  • 1
Krishnan
  • 63
  • 6
  • 1
    Are you really worried by enum base type being longer than 1 byte? – void_ptr Jul 06 '16 at 17:42
  • 1
    Smart people are suggesting consts over `#define`s everywhere it is possible. – Eugene Sh. Jul 06 '16 at 17:44
  • You can `undef` the `#define`, where you decide that you are done with that value ... – sps Jul 06 '16 at 17:45
  • 3
    Whether `static const` is less efficient than `#define`s is entirely implementation dependent. – Jabberwocky Jul 06 '16 at 17:45
  • @Eugene Sh.: It is significantly less possible in C (than, say, in C++), which is why in C it is either `#define` or `enum`. – AnT stands with Russia Jul 06 '16 at 17:46
  • The chief point is to define the value in one place where you have control over the whole code, unless the "magic number" is of the kind that is better defined with such as `'A'` instead of `65`. – Weather Vane Jul 06 '16 at 17:47
  • 1
    "(The processor would do a 32 bit(?) compare instead of an 8 bit compare)." Doesn't matter. If an implementation uses a 32-bit for enums while it could have been 8 bits, there's probably a very good reason for it. Fun fact: 8-bit comparison could be slower, and would still use the full 32-bits for storing the operands. So feel free to use `enum` and forget about your completely imaginary problem. When thinking about performance, always keep this in mind: **it's not a problem until you can measure it.** (Also, the `_t` suffix is reserved for POSIX, so don't use it for your own stuff.) –  Jul 06 '16 at 18:42

4 Answers4

3

I think in all cases, the compiler, at least with -O2 turned on, will generate the same code. It has gotten pretty hard to trick compilers into doing stupid things.

Typically, magic numbers are defined in a common header and used as needed throughout the code.

The bigger question for you is, does it matter? Is this something that is in the critical path of your code, and something you are going to be doing a very high percentage of the time? Given the name, I'd guess not.

Moving the definition to a header file will make your code less distracting. In checkInvalidResponse, the reader couldn't care less exactly what is represented by INVALID_RESPONSE, only that the test passes or fails.

tad
  • 506
  • 3
  • 7
2

In C language const entities are not constants at language level, which significantly limits the usability of const for defining manifest constants. Note that this is not about the efficiency of the generated code, it's about the basic validity ("compilability") of the code: in C language const entities are simply not allowed in contexts that require constants.

For this reason in C language the only truly universal and versatile approach to defining manifest constants is C preprocessor (i.e. #define), with enum being another vialble alternative, but only where it is applicable (a significant drawback of enum constants in C is the fact that they unconditionally have signed int type).

Just use #define and don't attempt to "scope" the constants. There's rearely any point in doing so. But if for some reason you need to restrict the scope of #define'd constant, you can always use #undef for that purpose.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • Can you back this up. As far as I remember, gcc places constant in the .text section – doron Jul 06 '16 at 17:57
  • 2
    @doron: It is not about where the compiler places them. It is about the fact that in C language `const` entities do not qualify as language "constants". You are not allowed to use `const` entities in `case` labels, bit-field widths, non-VLA array sizes etc. The language prohibits it and the code simply will not compile. The topic is already well-covered by answers linked in the comments. The OP used a non-representative example with an `if`, which doesn't require a constant. But if they used a `switch/case` statement instead, the problem with `const` would immediately become apparent. – AnT stands with Russia Jul 06 '16 at 17:58
  • Thanks for that. This is an unexpected difference in C and C++ for me. – doron Jul 06 '16 at 20:47
1

enum is guaranteed by the standard to support up to unsigned int width.

If you use #define there is no implicit specification of the width because the pre-compiler will just replace the symbol with the number (or whatever else was defined), so you can append L to the end of your number and guarantee long values.

Frankly, I never had use for enums larger than integers myself...

http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf

6.7.2.2 Enumeration specifiers
[...]
Constraints
The expression that defines the value of an enumeration constant shall be an integer constant expression that has a value representable as an int.
[...]
Each enumerated type shall be compatible with char, a signed integer type, or an unsigned integer type. The choice of type is implementation-defined, but shall be capable of representing the values of all the members of the enumeration.

Ishay Peled
  • 2,783
  • 1
  • 23
  • 37
0

This will all be compiler and platform specific. And unless you really need to sqeeze a few bytes out to fit in a ROM, does not really matter. Using the compiler is almost always less error prone than the preprocessor so stick with the consts.

Also bear in mind that the compiler is always allowed to inline if it makes more sense.

doron
  • 27,972
  • 12
  • 65
  • 103