Mastering C Pointers: From Value Semantics to Advanced Pointer Types
This article explains C's value semantics versus reference semantics, introduces pointers as the bridge between them, details pointer definitions, types, operators, and common pitfalls such as null, dangling, and wild pointers, and demonstrates pointer arithmetic with clear code examples.
Value Semantics and Reference Semantics
In C, variables use "value semantics": the variable name is compiled into a memory address, and the stored data is accessed directly via that address.
Variable name : after compilation it becomes the memory address, transparent to the developer.
Variable value : the actual data stored at that address; this is the essence of value semantics.
Advantages of value semantics are simplicity, directness, and high‑performance data access, but it lacks flexibility. For example, a variable name is bound one‑to‑one with its value, and function arguments are copied, leading to extra memory usage for large structures.
Variable name and value are tightly coupled; multiple names cannot refer to the same value.
Function calls copy arguments, incurring overhead.
High‑level languages like Python adopt "reference semantics", where a name refers to an address that points to the actual data. C achieves reference semantics through pointers, providing both high performance and flexibility.
Pointers are the mechanism that enables C to combine value and reference semantics.
Definition of Pointers
A pointer is a special C data type whose size depends on the CPU and OS (typically 4 bytes on 32‑bit systems, 8 bytes on 64‑bit systems).
Pointer : a memory address.
Pointer variable : a variable that stores a memory address.
Pointer variable type : informs the programmer and compiler about the type and size of the data the pointer points to.
Variable Pointer and Pointer Variable
Variable pointer : essentially a pointer that points to a variable’s address, roughly equivalent to the variable name.
Pointer variable : a variable with a data type that stores an address pointing to another variable or data. Declaration syntax:
type* var-name; // type*: pointer variable type.
// var-name: pointer variable name.Common examples of pointer variables:
/* basic data types */
int a; // (int)
int* a; // (int pointer)
/* array */
int a[n]; // (int array) with n int elements
int* a[n]; // (pointer array): n pointers to int
int (*a)[n]; // (array pointer): pointer to an int array, equivalent to the array name
/* function */
int func(); // (int) function returning int
int* func(); // (int pointer) function returning int pointer
int (*func)(); // (function pointer): pointer to function, equivalent to function name
/* double pointer */
int **a; // pointer to int pointerint* i or int *i ?
Both syntaxes are equivalent; the choice is a matter of style.
int* i viewpoint : emphasizes the type; int* i, j does NOT declare j as a pointer, so the habit is to write int* i = &a; int* j = &b;.
int *i viewpoint : emphasizes the variable; the asterisk belongs to the variable name.
Dereference and Address Operators
* (dereference operator) : used to define a pointer variable and to obtain the value stored at the address the pointer points to.
& (address‑of operator) : obtains the memory address of a variable.
Example:
#include <stdio.h>
int main ()
{
int var = 20;
int* ip; /* pointer variable */
ip = &var; /* assign address of var to ip */
printf("Address of var variable: %p
", &var);
printf("Address stored in ip variable: %p
", ip);
printf("Value of *ip variable: %d
", *ip); // get the value
return 0;
}Output:
Address of var variable: bffd8b3c
Address stored in ip variable: bffd8b3c
Value of *ip variable: 20Kinds of Pointers
Double Pointer
A double pointer (pointer to pointer) provides multi‑level indirect addressing.
#include <stdio.h>
int main ()
{
int var = 3000;
int* ptr = NULL;
int** pptr = NULL; // double pointer
ptr = &var;
pptr = &ptr;
printf("Value of var = %d
", var);
printf("Value available at *ptr = %d
", *ptr);
printf("Value available at **pptr = %d
", **pptr);
return 0;
}Null Pointer
Assigning NULL to a pointer that has no valid address is good practice. NULL is defined in the C standard library as the constant 0x0; accessing address 0x0 is prohibited by the OS.
#include <stdio.h>
int main ()
{
int* ptr = NULL;
printf("ptr address is %p
", ptr); // prints 0x0
return 0;
}Checking a null pointer:
if (ptr) /* non‑null */
if (!ptr) /* null */Dangling Pointer
A dangling pointer refers to memory that has already been freed. Using it can cause undefined behavior.
Bad example (creates a dangling pointer):
void* p = malloc(size);
assert(p);
free(p); // p is now danglingGood practice (nullify after free):
void* p = malloc(size);
assert(p);
free(p);
p = NULL; // avoid dangling pointerWild Pointer
A wild pointer is an uninitialized pointer whose value is indeterminate.
void* p; // p is a wild pointerWild pointers can corrupt data or cause crashes; initializing them to NULL mitigates the risk.
Pointer Arithmetic
Pointers are essentially hexadecimal numbers, so they support arithmetic operators ++, --, +, and -. Incrementing moves to the next element; decrementing moves to the previous element. The step size depends on the pointed‑to type (e.g., int steps by 4 bytes).
Pointers can also be compared with ==, <, and > when they point into the same array.
#include <stdio.h>
const int MAX = 3;
int main ()
{
int var[] = {10, 100, 200};
int i;
int* ptr;
/* address of first element */
ptr = var;
i = 0;
while (ptr <= &var[MAX-1])
{
printf("Address of var[%d] = %x
", i, ptr);
printf("Value of var[%d] = %d
", i, *ptr);
ptr++; // move to next element
i++;
}
return 0;
}Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
