Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What to use instead of magic numbers in C [duplicate]

Tags:

c

constants

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?

like image 610
Krishnan Avatar asked Jul 06 '16 17:07

Krishnan


3 Answers

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.

like image 89
tad Avatar answered Oct 07 '22 15:10

tad


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.

like image 27
AnT Avatar answered Oct 07 '22 15:10

AnT


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.

like image 36
Ishay Peled Avatar answered Oct 07 '22 15:10

Ishay Peled