Type Qualifiers in C Programming



What’s there to C?

When declaring variables in C, we can add some attributes to the variable being declared. The usual attributes have to do with allocation, scope, and accessibility of the variable and also whether the compilers can do optimizations on expressions involving the variable.

First things first

The usual type qualifiers are:
• const
• extern
• register
• static
• volatile

It is recommended that the register type qualifier not be used with the modern compilers. And the const type qualifier affects the accessibility of the variable while the static keyword affects the scope of the variable. The extern qualifier is a purely declarative statement (as opposed declare and define statement). Volatile type qualifier affects optimizations that can be done on an expression involving such variables.

In this blog, we will look at const and volatile keywords. The extern and static keywords will be visited at a later stage.

The const type qualifier

A better keyword for this type of qualifier might have been “read-only”. Having a constant variable seems a bit incongruent. The real meaning of the type qualifier is that the variable so declared may be initialized only once when being declared and can only be read afterwards.

The most simple and obvious declaration would be const int max_val = 10;

Within the scope of existence of the max_val variable the only thing to do is get its value or read its value. In software lingo, it is said that max_val has only the r value and not the l value. It means that max_val can’t be used to the left of any assignment operator.

A valid question then is why have it as a variable at all? Why not use a macro (or an enum)? Even though it may not be obvious at first look, there are at least two important uses for this type of qualifier.

The first and most common use for this is to qualify arguments, that are, received by a function. If we look at the prototype of some library functions, we will see that quite a few arguments (especially pointer arguments) are qualified with the const keyword. For eg:

int strcmp (const char *s1, const char *s2);

 

Usually, this is the point where a small question can arise at the back of one’s mind – with a declaration like const char *s1, what exactly is const? Is it the pointer – s1, or is it the char pointed to by s1 – *s1.

 

What is being qualified by the type of qualifier

This question arises only with two type qualifiers – const and volatile, and only when a pointer variable is declared.

With a pointer variable declaration, a point that goes under the radar is that, we get two variables, not one. One is the pointer itself and the other is the data, that is being pointed to by that pointer. If we have the following declaration:

char *ptr;

One thing is clear – we have a variable called ptr and its type is char *. But we don’t consciously think about another variable that can used – *ptr whose type is char. We can assign different values to ptr as well as *ptr. Of course, we can assign only valid memory addresses to ptr and char values to *ptr.

Now let us revisit the first argument to the strcmp () function – const char *s1. We have the pointer s1 and char variable *s1. As mentioned earlier, the question now is what is const here?

As usual, there is a rule that we can use to declare/understand such declarations involving pointers unambiguously. The main problem with const (and volatile) is that it can appear just about anywhere in a declaration (they can also appear together). So we need a rule that explicates the type that is being affected by the qualifier.

In the example we are considering – const char *s1, the const keyword is at the leftmost position and this is a one-off case and the rule says whatever is immediately to the right of the keyword is the const. In this case, it is char, and since the variable of type char is *s1, *s1 is const but not s1. This further means the pointer value can be changed (s1 can be changed) in the function but whatever s1 is pointing to (*s1) can’t be.

Now for other possibilities.

char const *s1;

 

With the declaration above and any other position of the keyword (except the first case), the type/operator that is immediate to the left of the keyword will be const. In the above example, immediately to the left of the keyword, we have the char keyword and so, again char variable will be const – i.e *s1 is const.

The third case would be:

char * const s1;

If we look at the declaration given above, to the immediate left of the keyword is *, and that means the pointer variable is const and not the int variable s1 is const, but not *s1.

And now for the last case.

char const * const i_ptr;

Now we have both char and * to the left of the two occurrences of the keyword. That means, to the immediate left of the first occurrence of the keyword we have char, and to the left of the second one we have *. That means both char and pointer variables are const – *s1 and s1 are both const.

This rule is applicable even with the volatile keyword.

NOTE: I am at a loss to figure a real practical use for the example shown last.

 

The Volatile Keyword

Whenever a variable is declared as volatile, we are telling the compiler not to optimize and statements having expressions involving that variable. This type of qualifier is essential when writing software that interacts directly with the hardware (like kernels and drivers) and doesn’t find much use in the world of software that doesn’t do so.

In general, when the compiler does optimisation, it will try to avoid any instruction that is likely to take a longer time – for example, an instruction that involves only processor registers will be very much faster than loading a value from a memory location into a register simply because accessing a memory location usually is much, much slower than accessing a register.

We will have a longer discussion about this particular keyword in a later blog. We will also look at a program that will demonstrate the behavior of this type of qualifier in using only software.

In the next blog, we will look at an IPC (Inter Process Communication) mechanism that will be used to understand the exact the effect of the volatile keyword. This IPC mechanism involves a function called signal () and a command called kill.

 

And on that rather ominous-sounding note, we will wind up this short blog.

Ready to take your skills to the next level? Explore job opportunities at Vayavya, where innovation meets talent. Apply now and join our dynamic team! 

100% LikesVS
0% Dislikes

Author

  • Venu Kolathur

    Venu Kolathur is Chief Architect and Co-Founder at Vayavya Labs and has over 38 years of industry & academic experience. He is responsible for product technology road-map, and design strategies.