C
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions
-
Section 4: Arrays and Strings
-
Section 5: Pointers
-
Section 6: Memory Management
-
Section 7: Structures and Unions
-
Section 8: The Preprocessor and Build Process
-
Section 9: Standard Library: stdio.h
-
Section 10: Standard Library: stdlib.h
-
Section 11: Standard Library: string.h
-
Section 12: Standard Library: ctype.h and wctype.h
-
Section 13: Standard Library: math.h, complex.h, fenv.h, tgmath.h
-
Section 14: Standard Library: Type and Limit Headers
-
Section 15: Standard Library: Error Handling and Debugging
-
Section 16: Standard Library: Localization and Encoding
-
Section 17: Standard Library: time.h
-
Section 18: Standard Library: Concurrency (C11)
-
Section 19: POSIX and System Programming (unistd.h)
-
Section 20: More Data Structures
-
Section 21: Algorithms in C
-
Section 22: Bitwise Operations
-
Section 23: Command-Line Programs
-
Section 24: Debugging and Best Practices
-
Section 25: Compiler and Language Internals
-
Section 26: Embedded and Cross-Platform Considerations
-
Section 27: Networking Basics
-
Section 28: Practical Projects
-
Section 29: Interview Practice
-
Section 30: C23 Modern Features
-
Section 31: More Practice and Review
51: const with Pointers
I've seen a lot of developers get tripped up by const when pointers enter the mix. It feels like the syntax is just playing a prank on you. Let's stop talking about the theory and actually break some code to see how the compiler reacts.
Trying to protect a value
Imagine we're writing a small piece of a system that handles a server's port number. This value should be read-only for most of the program. My first instinct is to just throw const at the pointer. Let's see what happens:
int port = 8080;
const int *p_port = &port;
printf("Port: %d\n", *p_port);
// Let's try to change the port through the pointer
*p_port = 443;
I just tried to compile this, and the compiler yelled at me: error: assignment of read-only location '*' p_port. Great. That's exactly what I wanted. The const is protecting the integer that the pointer points to. If I try to use the pointer to reach into that memory address and flip the value, C stops me.
But wait. Is the pointer itself locked? Let's try to point it somewhere else.
int backup_port = 80;
p_port = &backup_port; // Does this work?
Surprisingly, it compiles perfectly. So, const int *p means "the data is constant, but the pointer is free to wander." In my head, I call this a "pointer to a constant."
The "wrong" side of the asterisk
Now, what if I don't care if the port value changes, but I want to make sure this pointer always points to the same memory address? Maybe it's pointing to a critical hardware register. I'll move the const to the other side of the asterisk.
int port = 8080;
int * const p_port = &port;
// Let's try to change the value again
*p_port = 443;
This time, it compiles without a hitch. The port is now 443. The const didn't protect the data at all. But let's try that pointer reassignment again:
int backup_port = 80;
p_port = &backup_port;
Boom. There's the error: error: assignment of read-only variable 'p_port'. This is the opposite of the first scenario. Now, the address stored in the pointer is locked. The data at that address is fair game, but the pointer is stuck pointing to port forever. This is a "constant pointer."
Locking everything down
I'm starting to see the pattern. The const modifier applies to whatever is to its left. If there's nothing to its left, it applies to whatever is to its right. It's a bit clunky, but consistent.
What if I want both? I want a pointer that can't move, pointing to data that can't change. I'll just use const twice.
int port = 8080;
const int * const p_port = &port;
*p_port = 443; // Error: data is const
p_port = &backup_port; // Error: pointer is const
Now we have a total lockdown. I can read the value, but I can't touch the data or move the pointer. This is the safest way to handle fixed configuration values that should never be mutated or redirected during the lifecycle of the program.
A quick rule of thumb
If you're staring at a piece of code and can't remember which is which, read the declaration from right to left. It sounds weird, but it works every time:
int * const p→ p is a const pointer to an int.const int * p→ p is a pointer to an int that is const.const int * const p→ p is a const pointer to an int that is const.
📋 Practical Task
Exercise: Hardening the System Clock Pointer
You are working on an embedded system where the system clock is stored at a specific memory address. You need to create a program that ensures the clock's value cannot be modified and that the pointer used to access it cannot be accidentally redirected to another memory location.
Your Task:
- Declare an integer variable named
system_clockand initialize it to1000. - Create a pointer named
clock_ptrthat is both a constant pointer and points to a constant integer. - Write two lines of code (and comment them out) that demonstrate the two things the compiler should prevent:
- Attempting to change the value of
system_clockviaclock_ptr. - Attempting to point
clock_ptrto a different integer variable.
- Attempting to change the value of
- Print the value of the clock using the pointer to prove it still works for reading.
There are no comments for now.