Skip to content

C Pointers

Pointers are one of C’s most powerful and distinguishing features. They allow for direct memory manipulation, efficient data handling (like passing large structures to functions), dynamic memory allocation, and are essential for building complex data structures.

At its core, a pointer is a variable that stores the memory address of another variable.

Every variable you declare resides at a specific location in the computer’s memory. This location has a unique address. You can obtain the address of a variable using the address-of operator (&).

To print memory addresses portably, use the %p format specifier with printf and cast the address to (void *).

#include <stdio.h>
int main(void) {
int count = 10;
double price = 99.99;
char grade = 'A';
// Get and print the memory address of each variable
printf("Address of count: %p\n", (void *)&count);
printf("Address of price: %p\n", (void *)&price);
printf("Address of grade: %p\n", (void *)&grade);
return 0;
}

When you compile and run this, you’ll see hexadecimal numbers representing the memory locations (the exact values will vary each time you run it and on different systems):

Address of count: 0x7ffc1a4b9c8c
Address of price: 0x7ffc1a4b9c90
Address of grade: 0x7ffc1a4b9c8b

A pointer variable is declared to hold such an address. The declaration specifies the type of data the pointer will point to. This is crucial because it tells the compiler how many bytes to read/write when the pointer is dereferenced and how to perform pointer arithmetic.

Pointer Declaration Syntax:

type *pointer_variable_name;

Here, type is the data type of the variable that the pointer will point to (e.g., int, double, char, struct Point). The asterisk (*) indicates that pointer_variable_name is a pointer.

Examples of pointer declarations:

int *p_int; // Pointer to an integer
double *p_double; // Pointer to a double
char *p_char; // Pointer to a character
struct Point *p_pt; // Pointer to a struct Point variable

Note: The actual value stored in any pointer variable (regardless of the type it points to) is a memory address (often represented as a large unsigned integer). The type part is essential for the compiler’s interpretation when using the pointer.

The fundamental operations involving pointers are:

  1. Declaration: Declare a pointer variable of the appropriate type.
  2. Assignment: Assign the address of a variable (obtained using &) to the pointer.
  3. Dereferencing: Access the value at the address stored in the pointer using the dereference (or indirection) operator (*).

Example:

#include <stdio.h>
int main(void) {
int score = 95;
int *p_score; // 1. Declare a pointer to an integer
p_score = &score; // 2. Assign the address of 'score' to 'p_score'
printf("Address of score variable : %p\n", (void *)&score);
printf("Address stored in p_score : %p\n", (void *)p_score);
printf("Value of score variable : %d\n", score);
// 3. Dereference p_score to get the value stored at that address
printf("Value via p_score (*p_score): %d\n", *p_score);
// Modifying the value *through* the pointer
*p_score = 100; // Change the value at the address p_score holds
printf("New value of score : %d\n", score); // Original 'score' is changed!
return 0;
}

Output (addresses will vary):

Address of score variable : 0x7ffd6e0f8a5c
Address stored in p_score : 0x7ffd6e0f8a5c
Value of score variable : 95
Value via p_score (*p_score): 95
New value of score : 100

It’s crucial to initialize pointers before using them. If you don’t have a valid address to assign immediately, initialize the pointer to NULL. NULL is a special macro (defined in <stddef.h>, <stdio.h>, and others) representing a null pointer constant (typically (void *)0).

A NULL pointer signifies that the pointer is intentionally not pointing to any valid memory location.

#include <stdio.h>
#include <stddef.h> // For NULL definition
int main(void) {
int *p_data = NULL; // Initialize pointer to NULL
printf("Value of p_data: %p\n", (void *)p_data);
// --- Attempting to dereference a NULL pointer causes Undefined Behavior (often a crash!) ---
// *p_data = 10; // DANGER! DO NOT DO THIS!
// Always check if a pointer is NULL before dereferencing
if (p_data != NULL) {
printf("Value pointed to: %d\n", *p_data);
} else {
printf("p_data is a NULL pointer.\n");
}
// Alternative check
if (!p_data) { // Evaluates to true if p_data is NULL
printf("p_data is NULL (checked using !p_data).\n");
}
return 0;
}

Output:

Value of p_data: (nil) // Or 0x0, depending on system printf
p_data is a NULL pointer.
p_data is NULL (checked using !p_data).

Dereferencing a NULL pointer is a common programming error that typically leads to a program crash (Segmentation Fault). Always check pointers, especially those returned from functions like malloc or used in data structures, before dereferencing.

Pointers enable many essential C programming techniques:

ConceptDescription
Pointer ArithmeticPerforming arithmetic operations (+, -, ++, --) on pointers. Incrementing a pointer moves it forward by sizeof(pointed-to-type) bytes. Essential for array traversal.
Pointers and ArraysAn array name often ‘decays’ into a pointer to its first element in expressions. Pointers are commonly used to iterate through arrays efficiently.
Array of PointersCreating arrays where each element is a pointer (e.g., an array of strings, where each element points to the first character of a string).
Pointer to Pointer (Double Pointer)A pointer that stores the address of another pointer (e.g., int **p). Used for modifying pointers passed to functions or for arrays of pointers.
Pointers and FunctionsPassing pointers to functions allows the function to modify the original data (pass-by-reference simulation). Functions can also return pointers (e.g., malloc).
Dynamic Memory AllocationUsing functions like malloc, calloc, realloc (from <stdlib.h>) to allocate memory at runtime. These functions return pointers to the allocated block, which must be managed (freed using free) by the programmer.
Function PointersPointers that store the address of a function. Allow functions to be passed as arguments to other functions (callbacks) or stored in data structures.

These concepts build upon the fundamental understanding of pointer declaration, assignment, and dereferencing, and will be explored in more detail in relevant sections.