Hello! Welcome to the first lesson in our new module, "Type Qualifiers and Const Correctness."
In our last lesson, we designed a plugin architecture, defining a clear API contract between a host application and its plugins using a struct of function pointers. That contract is a form of communication, but C doesn't, by default, prevent the plugin from accidentally modifying data it was only supposed to read. This is where type qualifiers, specifically const, become essential.
Today's learning outcome is to distinguish between a 'pointer to const data' and a 'const pointer'. Mastering this distinction is fundamental to writing safer, more readable, and self-documenting code—a practice that is especially critical in the high-integrity environments you work in, such as automotive systems governed by standards like ISO 26262 and MISRA-C.
We will explore how a single keyword, const, completely changes the behavior and guarantees of a pointer based on its placement in a declaration.
The Two Flavors of const with Pointers
At its heart, a pointer involves two distinct entities:
- The pointer variable itself, which stores a memory address.
- The data at that memory address, which the pointer "points to".
The const qualifier can be applied to either of these, leading to two different meanings with very similar-looking syntax. To get a clear overview of these concepts, let's start with a short video that explains and demonstrates them with simple examples.
Constant Pointer VS. Pointer To A Constant | C Programming Tutorial
The video 'Constant Pointer VS. Pointer To A Constant' from Portfolio Courses provides a concise and clear explanation of the core concepts we're covering today.
Please watch the video from the beginning to 04:58, and then from 05:40 to the end. The video will walk you through: What a constant pointer is. What a pointer to a constant is. How to combine them into a constant pointer to a constant. A final summary of all possibilities.
Now, let's break down these concepts in detail, formalize the rules, and connect them to practical use cases.
1. Pointer to const Data
This is a non-constant pointer that points to constant data.
-
What it means: You cannot change the value of the data being pointed to through this pointer. However, you can change the pointer itself to point to a different memory address.
-
Syntax: The
constkeyword comes before the data type or between the data type and the asterisk (*). Both forms are identical to the compiler.const int *p; // is the same as int const *p;
Let's see it in action:
int main() {
int val1 = 10;
int val2 = 20;
const int *p_to_const = &val1; // p_to_const points to val1
// ALLOWED: We can change where the pointer points.
p_to_const = &val2;
printf("Value via pointer: %d\n", *p_to_const); // Prints 20
// NOT ALLOWED: We cannot modify the data through the pointer.
// *p_to_const = 30; // This line would cause a compile-time error.
// However, the original data is not constant, so it can be changed directly.
val2 = 30;
printf("Value via pointer after direct change: %d\n", *p_to_const); // Prints 30
return 0;
}
Key Use Case: This is extremely common for function parameters. When you pass a pointer to a function and you want to guarantee that the function will not modify your data, you declare the parameter as a pointer to const data. It’s a contract that enforces read-only access.
// This function promises not to change the content of 'buffer'.
void print_data(const char* buffer) {
// ... logic to print buffer ...
}
2. const Pointer
This is a constant pointer that points to non-constant data.
-
What it means: The pointer itself is locked to a single memory address for its entire lifetime. It cannot be made to point somewhere else. However, you are free to change the value of the data it points to.
-
Syntax: The
constkeyword comes after the asterisk (*).int * const const_p; -
Rule: A
constpointer must be initialized when it is declared, because you can't assign it a value later.
Let's see the code:
int main() {
int val1 = 10;
int val2 = 20;
// Must be initialized at declaration
int * const const_p = &val1;
// ALLOWED: We can modify the data the pointer points to.
*const_p = 50;
printf("val1 is now: %d\n", val1); // Prints 50
// NOT ALLOWED: We cannot change where the pointer points.
// const_p = &val2; // This line would cause a compile-time error.
return 0;
}
Key Use Case: This is useful when you have a pointer that must always refer to the same object or hardware resource. For instance, in embedded systems, a pointer to a specific memory-mapped hardware register (like a UART data register) could be declared as volatile uint8_t * const UART_DATA_REG. The address of the register never changes, but its contents do.
A Simple Rule for Reading const Declarations
The placement of const can be confusing. Here's a simple and effective rule: read the declaration from right to left. Another way to think about it is that const applies to the item immediately to its left. If there's nothing to its left, it applies to the item on its right.
Let's apply this to our examples.
-
int const * p;- Start with
p:pis... - Look left, see
*:pis a pointer to... - Look left, see
const:pis a pointer to aconst... - Look left, see
int:pis a pointer to aconst int. - Meaning: The
intdata isconst.
- Start with
-
int * const p;- Start with
p:pis... - Look left, see
const:pis aconst... - Look left, see
*:pis aconstpointer to... - Look left, see
int:pis aconstpointer to anint. - Meaning: The pointer
*isconst.
- Start with
This "right-to-left" reading method is very powerful for parsing more complex C declarations, which we'll encounter later.
The article 'How to read C declarations' from educative.io provides an excellent summary and a handy 'Const-correctness quick reference' that reinforces this rule.
Please read the sections 'Explanation of Type Qualifier Rule' and 'Const-correctness quick reference'. This text formalizes the rule we just discussed: the qualifier binds to what it's next to. This is a great way to solidify your understanding.
3. Combining Them: const Pointer to const Data
You can also combine both qualifiers to create a pointer that is itself constant and points to data that is also constant.
- Syntax:
const int * const p; - Meaning: You can neither change where the pointer points, nor can you change the data it points to through the pointer.

Here is a code example:
int main() {
int val = 10;
const int * const p = &val;
// NOT ALLOWED: Cannot modify the data.
// *p = 20; // Compile-time error.
// NOT ALLOWED: Cannot change the pointer.
// int val2 = 30;
// p = &val2; // Compile-time error.
// The only thing you can do is read the value.
printf("Value: %d\n", *p);
return 0;
}
Test your understanding!
For each of the following declarations, state whether p can be reassigned and whether *p can be modified.
char c = 'A'; char * const p = &c;int i = 100; const int * p = &i;float f = 3.14; float const * p = &f;long l = 123; const long * const p = &l;
Show answer
-
char * const p:constpointer tochar.- Can
pbe reassigned? No. (The pointer isconst.) - Can
*pbe modified? Yes. (Thecharis notconst.)
- Can
-
const int * p: Pointer toconst int.- Can
pbe reassigned? Yes. (The pointer is notconst.) - Can
*pbe modified? No. (Theintisconst.)
- Can
-
float const * p: Pointer toconst float. (This is the same asconst float * p.)- Can
pbe reassigned? Yes. - Can
*pbe modified? No.
- Can
-
const long * const p:constpointer toconst long.- Can
pbe reassigned? No. - Can
*pbe modified? No.
- Can
Conclusion
You've now learned the crucial difference between a pointer to const data and a const pointer. This isn't just a syntactic trick; it's a powerful tool for expressing intent and enforcing correctness, which is paramount in safety-critical software development.
Key Takeaways:
- Pointer to
constdata (const int *porint const *p): The data is read-only through this pointer. The pointer itself can be changed. constPointer (int * const p): The pointer itself is read-only and cannot be reassigned once initialized. The data it points to can be modified.- The Reading Rule: Read declarations from right to left to decipher them.
constapplies to what is on its left, unless it's at the start of the declaration. - Combined (
const int * const p): Neither the pointer nor the data it points to can be changed.
Preview of the Next Lesson:
Now that we have the theoretical foundation, our next lesson will focus on the practical application of these concepts. We will learn how to apply const correctness to function parameters to create safer, more readable APIs. This will directly build on today's knowledge and show how const helps create robust interfaces, a skill directly applicable to your work with AUTOSAR components and communication stacks.