Pointer Basics
Pointers are like street addresses -- the address itself is not the house, but through it you can find that exact house. Master addresses, and you master C's most powerful feature.
What Is a Pointer?
Every byte in memory has a unique number called an address. A pointer variable is a variable specifically designed to store an address.
int x = 42;
int *p = &x;
xis an integer variable with the value 42&xis the address ofxin memorypis a pointer variable storing the address ofx*paccesses the value ofxthrough its address, which is 42
Pointers themselves also occupy memory. On a 32-bit system, a pointer is 4 bytes; on a 64-bit system, it is 8 bytes, regardless of the type it points to.
The Address-of Operator &
& is used to obtain a variable's address:
int a = 10;
printf("Value of a: %d\n", a);
printf("Address of a: %p\n", (void *)&a);
The %p format specifier prints an address; the argument should be cast to void *.
What you can take the address of:
- Variables:
&x - Array elements:
&arr[3] - Struct members:
&s.age
What you cannot take the address of:
- Literals:
&10(illegal) - Register variables:
&r(ifrisregister) - Expression results:
&(a+b)(illegal)
The Dereference Operator *
* is used to access the data a pointer points to:
int x = 42;
int *p = &x;
printf("%d\n", *p);
*p = 100;
printf("%d\n", x);
The first output is 42, the second is 100. *p = 100 modifies x through the pointer.
* in a declaration means "this is a pointer"; in an expression it means "dereference" (get the pointed-to value). The meaning depends on context.
Dereferencing an uninitialized pointer is a serious error:
int *p;
*p = 10;
The value of p is random; writing to a random address may corrupt other data or crash immediately.
The NULL Pointer
NULL is a special pointer value meaning "does not point anywhere". It is defined in several headers including <stddef.h>, and its value is typically 0.
int *p = NULL;
if (p == NULL) {
printf("Pointer does not point to a valid address\n");
}
Dereferencing a NULL pointer causes a segmentation fault and crashes the program. Therefore, always check whether a pointer is NULL before using it:
void safe_print(int *p) {
if (p != NULL) {
printf("%d\n", *p);
}
}
Pointer Types and Stride
The pointer type determines how many bytes are read during dereference, and how many bytes the pointer moves during arithmetic (the stride).
int a = 10;
double b = 3.14;
int *pi = &a;
double *pd = &b;
*pireads 4 bytes (size of int),*pdreads 8 bytes (size of double)pi+1moves 4 bytes to the next int,pd+1moves 8 bytes to the next double
Pointer stride equals the size of the pointed-to type:
int arr[] = {10, 20, 30, 40, 50};
int *p = arr;
printf("%d\n", *p);
printf("%d\n", *(p + 1));
printf("%d\n", *(p + 3));
Output: 10, 20, 40. p+1 does not add 1 to the address value; it adds sizeof(int) (4 bytes), pointing to the next int element.
Pointer subtraction results are also measured in elements:
int *q = &arr[4];
printf("%ld\n", (long)(q - p));
Output: 4, meaning 4 int elements separate the two pointers.
int *p = &d; (where d is a double) will produce a compiler warning because the type mismatch causes the wrong number of bytes to be read during dereference.
Example
Swap two variable values using pointers:
#include <stdio.h>
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main(void) {
int x = 10, y = 20;
printf("Before swap: x=%d, y=%d\n", x, y);
swap(&x, &y);
printf("After swap: x=%d, y=%d\n", x, y);
return 0;
}
Before swap: x=10, y=20
After swap: x=20, y=10
Here, addresses are passed to the function, which modifies the original variables through dereference. This is the classic technique for "simulating pass by reference" -- the function parameter is an int * pointer, and the call passes &x.
Pointers and const
Combining const with pointers has two meanings:
Pointer to a Constant
const int *p = &x;
*p = 20;
Illegal. You cannot modify the data through p, but p itself can point to a different variable:
p = &y;
Legal.
Constant Pointer
int * const p = &x;
p = &y;
Illegal. p cannot change what it points to, but you can modify the data through p:
*p = 20;
Legal.
Memory trick: const to the left of * modifies the data; const to the right of * modifies the pointer itself.
Example
Sum an array using pointer traversal:
#include <stdio.h>
int array_sum(const int *arr, int len) {
int sum = 0;
const int *end = arr + len;
while (arr < end) {
sum += *arr;
arr++;
}
return sum;
}
int main(void) {
int data[] = {3, 7, 1, 9, 5};
int total = array_sum(data, 5);
printf("Sum: %d\n", total);
return 0;
}
Sum: 25
The parameter const int *arr indicates the function will not modify the array contents. The pointer end marks the stopping position, avoiding the need for subscripts.
❓ FAQ
* in int *p belong to the type or the variable?int* p, q; declares p as an int pointer and q as a plain int. It is recommended to declare only one pointer per line.📖 Summary
- Pointers store addresses;
&takes the address,*dereferences to get the value - Uninitialized pointers are wild pointers; always initialize to NULL
- Pointer type determines the stride and the number of bytes read during dereference
- Passing a pointer to a function allows it to modify the caller's variables
constwith pointers has two positions: modifying the data or modifying the pointer itself
📝 Exercises
- Write a function
void divide(int a, int b, int *quotient, int *remainder)that returns the quotient and remainder through pointer parameters. - Write a program that defines an int variable and a double variable, points to them with
int *anddouble *respectively, prints their addresses and values, and observes the stride difference. - Write a function
int *find_max(int *arr, int len)that returns a pointer to the maximum element in the array.



