C Basics: Separate Read-Only Access from Fixed Pointer Bindings
A read-only parameter and a pointer that cannot be reassigned express different promises. Make that difference visible in a small counter API.
Prepare a scratch directory
Tested on Linux amd64 with GCC 14.2.0. You need GCC with C17 support, Bash, a text editor, and the standard mktemp, rm, and rmdir utilities. No external library is required.
Keep the same terminal open. Stop if setup fails, before saving the source:
start_directory="$PWD"
work_directory="$(mktemp -d)" || exit 1
cd -- "$work_directory" || exit 1Run the example
Save this as const-pointer-contracts-demo.c:
#include <stdio.h>
struct counter { int value; };
static void print_counter(const struct counter *counter) {
printf("Count: %d\n", counter->value);
}
static void increment_counter(struct counter *counter) {
++counter->value;
}
int main(void) {
struct counter current = { .value = 4 };
struct counter *const fixed_pointer = ¤t;
const struct counter *read_only_view = ¤t;
print_counter(read_only_view);
increment_counter(fixed_pointer);
print_counter(read_only_view);
return 0;
}Compile and run it. The program runs only if compilation succeeds:
gcc -std=c17 -Wall -Wextra -Werror -pedantic const-pointer-contracts-demo.c -o const-pointer-contracts-demo &&
./const-pointer-contracts-demoObserved output:
Count: 4
Count: 5Read the result
The read-only view prevents modification through that pointer. The fixed pointer cannot be reassigned, but still permits changes to the counter. Both point to the same object.
Apply it carefully
Const is not an ownership or synchronization mechanism. Another permitted alias can change a non-const object. Do not cast away const to modify an object that was defined const; that has undefined behavior. These helpers require a non-null pointer to a live counter, and incrementing INT_MAX would overflow.
See the WG14 C draft, sections 6.7.3 and 6.7.6.1 for the underlying language rules.
Clean up
Remove only the source and executable created here, return to the starting directory, and remove the empty practice directory:
rm -- const-pointer-contracts-demo.c const-pointer-contracts-demo
cd -- "$start_directory" || exit 1
rmdir -- "$work_directory"
unset start_directory work_directoryIf the directory is not empty, inspect its contents before removing anything else.
Acknowledgements
This post was created with assistance from AI.