Introduction
In systems programming, understanding how memory addresses are manipulated remains a foundational skill. Among the most common sources of confusion for developers learning C is the distinction between assigning pointers themselves versus assigning values through dereferenced pointers. This article clarifies these concepts through original examples and practical demonstrations, enabling readers to write safer, more predictable pointer-based code.
By the end of this guide, you will understand pointer assignment mechanics, recognize illegal memory access patterns, and apply correct syntax for both pointer copying and value assignment.
(toc) #title=(Table of Content)
What Is Pointer Assignment?
Pointer assignment refers to copying the memory address stored in one pointer variable into another pointer variable. When you write q = p, both pointers then hold the same address, meaning they reference the same memory location. This differs fundamentally from value assignment through dereferencing, written as *q = *p, which copies the actual data value from one memory location to another.
Consider two integer variables stored at different memory addresses:
| Variable | Memory Address | Stored Value |
|---|---|---|
| x | 0x7FFD1000 | 42 |
| y | 0x7FFD1004 | 87 |
Two pointer variables, ptr1 and ptr2, initially contain unknown addresses (garbage values). After ptr1 = &x, ptr1 holds 0x7FFD1000. Executing ptr2 = ptr1 makes ptr2 also hold 0x7FFD1000. Both pointers now reference x.
Pointer Declaration vs. Dereferencing
A common point of confusion arises from the asterisk * symbol, which serves two distinct roles in C:
- Declaration context: In statements like
int *p;, the asterisk indicates thatpis a pointer type. - Dereference context: In statements like
*p = 10;, the asterisk accesses the value at the address stored inp.
The compiler interprets the symbol based entirely on context. Within a declaration line, the asterisk never acts as an operator.
Types of Pointer Assignment
Address Copy Assignment
When assigning one pointer variable to another, you copy only the address:
int a = 25;
int b = 36;
int *p = &a; // p holds address of a
int *q; // q holds unknown address
q = p; // q now holds address of a (copied from p)
After this operation, both p and q point to a. Printing *p and *q would both output 25.
Dereferenced Value Assignment
When assigning through dereferenced pointers, you copy the actual data:
int a = 25;
int b = 36;
int *p = &a; // p points to a
int *q = &b; // q points to b
*q = *p; // value at address in p (25) copied to address in q
After this operation, b contains 25 instead of 36. Both *p and *q evaluate to 25, but a and b are distinct memory locations.
Illegal Pointer Operations
A pointer must point to a valid memory location before dereferencing. Consider this erroneous pattern:
int *p;
int *q;
*p = 10; // ERROR: p points nowhere
This attempts to write 10 to an unknown address, typically causing a segmentation fault or program crash. The same principle applies to reading through an uninitialized pointer:
int *q;
int x = *q; // ERROR: reading from unknown address
Type Compatibility in Pointer Assignment
C enforces strict type checking for pointer assignment. Assigning an address of one data type to a pointer of a different type generates a compiler warning or error:
The compiler issues this diagnostic because fp expects to point to a float (typically 4 bytes in IEEE 754 format), but &value points to an int (also 4 bytes but with different binary interpretation). Dereferencing fp would misinterpret the memory contents as a floating-point number, producing nonsensical results.
Practical Example: Tracking Sensor Readings
Imagine a system that monitors temperature and pressure sensors. Two independent sensors provide readings, and a logging function needs to record the latest value from either sensor:
#include <stdio.h>
int main() {
int temperature = 22; // Celsius
int pressure = 1013; // millibars
int *current_reading;
int *backup_reading;
current_reading = &temperature;
backup_reading = current_reading; // Both point to temperature
printf("Current: %d, Backup: %d\n", *current_reading, *backup_reading);
// Now log pressure instead
current_reading = &pressure;
// backup_reading still points to temperature (22)
// Copy pressure value into temperature location
*backup_reading = *current_reading; // temperature becomes 1013
printf("Temperature now: %d, Pressure: %d\n", temperature, pressure);
return 0;
}
This example demonstrates how backup_reading = current_reading copies addresses, while *backup_reading = *current_reading copies integer values across memory locations.
Common Pitfalls and Best Practices
- Always initialize pointers before dereferencing — Uninitialized pointers contain indeterminate addresses.
- Match pointer types exactly — Assigning an
int*to afloat*leads to misinterpreted data. - Distinguish between pointer assignment and value assignment —
q = pchanges whatqpoints to;*q = *pchanges the value atq's target. - Use NULL for unassigned pointers — Explicitly set pointers to
NULLwhen they don't point to valid memory, enabling safer checks before dereferencing.
Outlook
Modern systems programming continues to rely heavily on pointer semantics, from embedded firmware to operating system kernels. Understanding the distinction between address copying and value copying becomes even more critical when working with complex data structures like linked lists, trees, and graph adjacency representations. As memory safety features evolve in newer languages like Rust, the underlying concepts of ownership and borrowing directly parallel these pointer assignment rules, making mastery of C pointers valuable beyond the language itself.