Question

While getting my code reviewed here the issue of using the const keyword came up. I understand that it is used for implementing read-only behaviour on variables.

I am confused about what are the various situations when it can be useful.

  • Should it be used for the sake of clarity in function prototypes?
  • Should it be used as a security measure during code development?
  • Should it be used in the scope of various functions for declaring run-time constants?
  • Should it be used at all?

These question are just examples of the confusion that I am facing. The general confusion is

  • When should be the const keyword used in C programming?
  • What are the various types of benefits that can be gained by using this keyword in C?
  • Are there any cons of using const keyword?


It has been pointed that this question may be too broad due to all these questions in the detail of my question. I just wanted to clarify that these questions are just to clarify the confusion regarding the main question.

When and for what purposes should the const keyword be used in C for variables?

It can also be rephrased as

The proper use of const keyword in C` with the pros and cons of the same.

Was it helpful?

Solution

When reviewing code, I apply the following rules:

  • Always use const for function parameters passed by reference where the function does not modify (or free) the data pointed to.

    int find(const int *data, size_t size, int value);
    
  • Always use const for constants that might otherwise be defined using a #define or an enum. The compiler can locate the data in read-only memory (ROM) as a result (although the linker is often a better tool for this purpose in embedded systems).

    const double PI = 3.14;
    
  • Never use const in a function prototype for a parameter passed by value. It has no meaning and is hence just 'noise'.

    // don't add const to 'value' or 'size'
    int find(const int *data, size_t size, int value); 
    
  • Where appropriate, use const volatile on locations that cannot be changed by the program but might still change. Hardware registers are the typical use case here, for example a status register that reflects a device state:

    const volatile int32_t *DEVICE_STATUS =  (int32_t*) 0x100;
    

Other uses are optional. For example, the parameters to a function within the function implementation can be marked as const.

// 'value' and 'size can be marked as const here
int find(const int *data, const size_t size, const int value)  
{
     ... etc

or function return values or calculations that are obtained and then never change:

char *repeat_str(const char *str, size_t n) 
{
    const size_t len = strlen(str);
    const size_t buf_size = 1 + (len * n);
    char *buf = malloc(buf_size);
    ...

These uses of const just indicate that you will not change the variable; they don't change how or where the variable is stored. The compiler can of course work out that a variable is not changed, but by adding const you allow it to enforce that. This can help the reader and add some safety (although if your functions are big or complicated enough that this makes a great difference, you arguably have other problems). Edit - eg. a 200-line densely coded function with nested loops and many long or similar variable names, knowing that certain variables never change might ease understaning significantly. Such functions have been badly designed or maintened.


Problems with const. You will probably hear the term "const poisoning". This occurs when adding const to a function parameter causes 'constness' to propagate.

Edit - const poisoning: for example in the function:

int function_a(char * str, int n)
{
    ...
    function_b(str);
    ...
}

if we change str to const, we must then ensure that fuction_b also takes a const. And so on if function_b passes the str on to function_c, etc. As you can imagine this could be painful if it propagates into many separate files/modules. If it propagates into a function that cannot be changed (eg a system library), then a cast becomes necessary. So sprinkling const around in existing code is perhaps asking for trouble. In new code though, it is best to const qualify consistently where appropriate.

The more insidious problem of const is that it was not in the original language. As an add-on it doesn't quite fit. For a start it has two meanings (as in the rules above, meaning "I'm not going to change this" and "this cannot be modified"). But more than that, it can be dangerous. For example, compile and run this code and (depending upon the compiler/options) it may well crash when run:

const char str[] = "hello world\n";
char *s = strchr(str, '\n');
*s = '\0';

strchr returns a char* not a const char*. As its call parameter is const it must cast the call parameter to char*. And in this case that casts away the real read-only storage property. Edit: - this applies generally to vars in read-only memory. By 'ROM', I mean not just physical ROM but any memory that is write-protected, as happens to the code section of programs run on a typical OS.

Many standard library functions behave in the same way, so beware: when you have real constants (ie. stored in ROM) you must be very careful not to lose their constness.

OTHER TIPS

Generally in any programming language its recommended to use const or the equivalent modifier since

  • It can clarify to the caller that what they passed in is not going to change
  • Potential speed improvements since the compiler knows for certain it can omit certain things that are only relevant if the parameter can change
  • Protection from yourself accidentally changing the value

In agreement with TheLQ's statements:

When working with a team of programmers declaring const is a good way of indicating that said variable shouldn't be modified, or just for reminding yourself in large projects. It's useful in that sense, and can save many headaches.

Yes, it's basically the TheLQ's answer.

Is a security measure for the programmer so you don't modify a variable, and to not call functions that may modify them. In an array or structure the const specifier indicates that the values of their contents won't be modified, and even the compiler will not allow you to do so. You still can easily change the value of the variable with just a cast however.

In what I have usually see, it's mostly used to add constant values in code, and to indicate that the array or structure won't be modified if you call a particular function. This last part is important, because when you call a function that WILL modify your array or structure, you may want to keep the original version, so you create a copy of the variable and then pass it to function. If that is not the case, you don't require the copy, obviously, so for example you can change,

int foo(Structure s);

to

int foo(const Structure * s);

and not getting the copy overhead.

Just to add, note that C has particular rules with the const specifier. For example,

int b = 1;
const int * a = &b;

is not the same as

int b = 1;
int * const a = &b;

The first code won't allow you to modify a. In the second case, the pointer is constant but its contents is not, so the compiler will allow you to say * a = 3; without a compiler error, but you can't make a to be a reference to another thing.

Licensed under: CC-BY-SA with attribution
scroll top