C Pointers
Introduction to Pointers
Pointers are variables that store memory addresses instead of values. They are one of the most powerful features of C, allowing direct memory manipulation and efficient data handling.
Key concepts:
- Pointers store memory addresses
- The & operator gets the address of a variable
- The * operator dereferences a pointer (accesses the value at the address)
- Pointers have types that determine valid dereferencing and pointer arithmetic
Note: In standard C, pointer arithmetic is only defined within the bounds of the same array object (including one-past-the-end).
Pointer Declaration and Initialization
data_type *pointer_name;
Basic Pointer Operations
#include <stdio.h>
int main(void) {
int num = 10;
int *ptr = #
printf("Value of num: %d\n", num);
printf("Address of num: %p\n", (void*)&num);
printf("Value of ptr: %p\n", (void*)ptr);
printf("Value pointed to by ptr: %d\n", *ptr);
*ptr = 20;
printf("New value of num: %d\n", num);
return 0;
}
Value of num: 10 Address of num: 0x7ffd42a8b54c Value of ptr: 0x7ffd42a8b54c Value pointed to by ptr: 10 New value of num: 20
Pointer Arithmetic
Pointer arithmetic moves by the size of the pointed-to type. For an int* on a system where sizeof(int) == 4:
- ptr + 1 advances by 4 bytes
- ptr - 1 moves 4 bytes back
Only perform pointer arithmetic within the same array object (or one past the last element).
#include <stdio.h>
int main(void) {
int arr[] = {10, 20, 30, 40, 50};
int *ptr = arr; // arr decays to &arr[0]
printf("Element 0: %d at %p\n", *ptr, (void*)ptr);
ptr++;
printf("Element 1: %d at %p\n", *ptr, (void*)ptr);
return 0;
}
Element 0: 10 at 0x7ffc5f3a8b60 Element 1: 20 at 0x7ffc5f3a8b64
Void Pointers (Generic Pointers)
A void* can hold the address of any object type, but you must cast it back to the correct type before dereferencing. Standard C does not allow arithmetic on void*.
#include <stdio.h>
int main(void) {
int x = 42;
void *vp = &x; // store address of int in a generic pointer
printf("x via void*: %d\n", *(int*)vp); // cast before dereferencing
return 0;
}
x via void*: 42
Null Pointers
A null pointer points to no valid object. Initialize pointers to NULL when they have no valid target yet and always check before dereferencing.
NULL is a macro defined in several headers (e.g.,
#include <stdio.h>
#include <stddef.h> // for NULL
int main(void) {
int *ptr = NULL;
if (ptr == NULL) {
printf("Pointer is NULL - safe to check\n");
}
return 0;
}
Common Pitfalls & Best Practices
1. Never dereference uninitialized or NULL pointers.
2. After freeing dynamically allocated memory, set the pointer to NULL to avoid dangling pointers.
3. Keep pointer arithmetic within array bounds; out-of-bounds access is undefined behavior.
4. Use const-correctness (e.g., const int *p) when not modifying the pointed-to data.
5. Remember sizeof(*ptr) gives the element size; sizeof(ptr) gives the pointer size.
6. Do not assume pointer size equals int size; both are implementation-defined.