Using ‘const’ keyword in C

Variables are symbols used to identify memory locations of data used by program instructions. High level programming languages provide various data types for reserving memory addresses at compile time to hold different types of data. For example the look at the following code snippet.

#include <stdio.h> 
#include <stdlib.h> 
int main() 
{ 
        int a = 10; 	/* a variable of type integer */ 
        a++;   		
        return EXIT_SUCCESS; 
}

When compiled, that first statement would result in memory address reservation for symbol ‘a’ with value 10 initialized to it.  Variable has been declared in the function and would be in stack frame of function main ().  Instructions in main function can access the value in variable ‘a’ and alter it.

Let’s assume that we need variable ‘a’ with value 10 remain constant throughout the program lifetime; we can achieve that if we consciously write the code in such a way that there are no statements in our source which attempt to alter value of ‘a’. our code review  process must verify each statement that access the variable and ensure that it not altering variables state, we may also have to check all  functions to which the variable was passed as argument  and ensure that variables state is not altered within the function’s statements .  This would be tedious job and would need developers to spend loads to time manually reviewing code to ensure that variable is unaltered throughout.

It would be easy and better if such validations be automated. This is exactly what “const” keyword does. ‘const’ keyword instructs compiler to validate statements in the source file and check if any statement is attempting to alter the value of a specified variable.  If compiler finds any such statement in compilation unit it throws up compile error.

const keyword can be used to declare Variables whose values cannot be modified like this

const int a = 10;

Const variables should be initialized at the time of declaration.

#include <stdio.h> 
#include <stdlib.h> 
int main() 
{ 
        const int a = 10; 	/* a is read-only variable of type integer */ 
        a++;   		        /* ERROR: as "a" is declared as read-only variable */ 
        return EXIT_SUCCESS; 
}

Constants variables can also be declared as

int const a = 10;

Compiler treats ‘a’ as constant integer

so from compiler perspective integer constant or constant integer both are same .

const does not have any effect at runtime. For example look into below code.

#include <stdio.h> 
#include <stdlib.h>

int inc(int *p) 
{ 
        *p = *p+1;      /* indirect reference to a */ 
} 
int main() 
{ 
        const int a = 10;       /* a is read-only variable of type integer */ 

        inc(&a); 
        printf(" %dn",a); 
        return EXIT_SUCCESS; 
}

In the above example if you see in “main” function “a” is declared as read-only variable and passing its address as argument to “inc”. In “inc” we are trying to update “a” using with its address which is valid.

If “a” value is stored in read-only memory that *p = *p+1 should be an error. But that is not the case here. So from this it’s very clear that “const keyword” is not an instruction which will store the variable value in read-only memory but simply an instruction given to compiler to check for those statements which are changing the const variable state.

Pointer to const

 

#include <stdio.h> 
#include <stdlib.h>

int main() 
{ 
        int a = 10;     
        const int *p = &a; 

        a=20;   /* Valid: as "a" is read/write variable */      
        *p=30; /* Not valid: as p is a pointer to integer constant */   

        printf(" %dn",a); 
        return EXIT_SUCCESS; 
}

In the above sample code “a” is declared as integer, but “p” is declared as a pointer to a location where integer constant is stored. So now compiler will treat “p” as a pointer to constant. So we cannot deference “p” to perform write operation over data. We can assign any address to “p”, compiler will treat all those addresses as locations where integer constant is stored even if those locations are not storing constant integer.

Let look into the another sample code

#include <stdio.h> 
#include <stdlib.h>

int main() 
{ 
        int a = 10,b;     
        const int *p = &a; 

        a=20;   /* Valid: as "a" is read/write variable */      
       *p=30; /* Not valid: as p is a pointer to integer constant */   
        p = &b; /* is valid as p is not read-only variable */

        printf(" %dn",a); 
        return EXIT_SUCCESS; 
}

In the above example “p = &b;” is a valid statement as “p” is not declared as read-only variable but instead address stored in “p” is read-only.

Const pointer

Now what if we want to declare pointers as read-only variable

int * const p;

Compiler will interpret the above declarations as “ p is a constant pointer to integer”. It means “p” is read-only variable which will stored “a” address whose value can be read/write.

#include <stdio.h> 
#include <stdlib.h>

int main() 
{ 
        int a = 10,b;     
        int * const p = &a; 

        a=20;   /* Valid: as "a" is read/write variable */      
        *p=30; /* Valid: as p is a pointer to integer */   

        p = &b; /* Not valid as p is read-only pointer variable */

        printf(" %dn",a); 
        return EXIT_SUCCESS; 
}

If we want to declare a read-only pointer to read-only data than

int const * const p = &a;

Now compiler will interpret the above declaration as “p” is a constant pointer to integer constant.

Let look into the another sample code

#include <stdio.h> 
#include <stdlib.h>
int main() 
{ 
        int a = 10,b;     
        const int * const p = &a; 

        a=20;   /* Valid: as "a" is read/write variable */      
        *p=30; /* not Valid: as p is a pointer to integer constant */   

        p = &b; /* Not valid as p is read-only pointer variable */

        printf(" %dn",a); 
        return EXIT_SUCCESS; 
}

Global variables as const

 Till now we understood behaviour of const keyword when used with local variables. Now let’s try to understand what happen when const is used with global variables. If const is used with global variables that those variable are stored in read-only location.

#include <stdio.h> 
#include <stdlib.h> 

const int a = 10;       /* a is read-only variable of type integer */ 

int main() 
{ 
        const int b = a; /* b is read-only variable of type integer */ 
        int *p = &b; 

        *p = 100;       
         printf(" %dn",b); 

        return EXIT_SUCCESS; 
}

From the above explanations it’s very clear that “*p = 100” is valid which will update b value to 100.

If we change the above code as the following

#include <stdio.h> 
#include <stdlib.h> 

const int a = 10;       /* a is read-only variable of type integer */ 
int main() 
{ 
        const int b = a; /* a is read-only variable of type integer */ 
        int *p = &a; 

        *p = 100;    
        printf(" %dn",a); 

        return EXIT_SUCCESS; 
}

In the above code “*p = 100” is causes segmentation fault.

How is that possible if assign the address of b to pointer p everything was fine and we are able to update b  value to 100 using pointer p but is assign the address of a its causing segmentation fault. It’s because when globe variable are declared with const that they are stored in read-only location and when we use pointer p to update value of a we are trying to write into read-only memory.

Let’s  print symbols information from executable image and see where a is stored.

root@veda-Inspiron-1564:~/const# objdump -t const

const:     file format elf64-x86-64 

SYMBOL TABLE: 
0000000000400238 l    d  .interp	0000000000000000              .interp 
0000000000400254 l    d  .note.ABI-tag	0000000000000000              .note.ABI-tag 
0000000000400274 l    d  .note.gnu.build-id	0000000000000000              .note.gnu.build-id 
0000000000400298 l    d  .gnu.hash	0000000000000000              .gnu.hash 
00000000004002b8 l    d  .dynsym	0000000000000000              .dynsym 
0000000000400318 l    d  .dynstr	0000000000000000              .dynstr 
........................................
........................................
........................................
0000000000601030 g       *ABS*	0000000000000000              _end 
0000000000400410 g     F .text	0000000000000000              _start 
000000000040063c g     O .rodata	0000000000000004              a 
0000000000601020 g       *ABS*	0000000000000000              __bss_start 
0000000000400511 g     F .text	0000000000000034              main 
0000000000000000  w      *UND*	0000000000000000              _Jv_RegisterClasses 
00000000004003c8 g     F .init	0000000000000000              _init 
00000000004004f4 g     F .text	000000000000001d              inc

From the symbol table it’s very clear that “a” is stored in “.rodata” which is read-only section

Tags: , , , ,

7 Responses to “Using ‘const’ keyword in C”

  1. Achyuth sai says:

    very useful article on ‘const’ keyword. Thank you sir.

    Current score: 0
  2. vikas reddy says:

    very helpfull sir…..

    Current score: 0
  3. jagadesh says:

    very useful information with clear explanation

    Current score: 0
  4. jignesh patel says:

    Advantage of using constant over macro :-
    (1) compiler doesn’t check the macro type it replaces the macro’s with values where it finds the macro,
    where as for constant compiler does checks for type.
    (2) constant can be defined over a scope and where as for a macro the scope is a file.
    (3) macro doesn’t take any space in the stack, macro just replaced by value in the program so it increasing the size of the executable. where as the constant variable takes up space in the stack but it doesn’t affect the size of executable.

    Thus its better to have constant’s in program over macro’s.

    Thanks to Raghu & Hari sir to give such fundamental information which we can’t find in any books.

    Current score: 2
  5. gandhimathi says:

    good information !!!

    Current score: 3
  6. gangadhar says:

    very clear information sir

    Current score: 1
  7. karthik dmg says:

    very helpful

    Current score: 2

Leave a Reply