|$ curl https://forge-ai.dev/api/markdown?path=docs/c/pointers
$cat docs/c-—-pointers.md
updated Recently·50 min read·published

C — Pointers

CPointersMemoryIntermediateIntermediate🎯Free Tools
What Is a Pointer?

A pointer is a variable that stores the memory address of another variable. This indirection is the foundation of C's power — it enables dynamic data structures, efficient function calls, and direct hardware interaction.

pointer_basics.c
C
1#include <stdio.h>
2
3int main(void) {
4 int x = 42;
5 int *p = &x; /* p holds the address of x */
6 printf("x: %d\n", x);
7 printf("&x: %p\n", (void *)&x);
8 printf("p: %p\n", (void *)p);
9 printf("*p: %d\n", *p);
10 return 0;
11}
ConceptSymbolDescription
Address-of&xReturns the memory address of x
Dereference*pAccesses the value at the address in p
Declarationint *pDeclares p as a pointer to int
Declaration and Initialization

The asterisk (*) is part of the variable name, not the type. Each pointer in a multi-declaration must be prefixed with *.

declaration.c
C
1int *p; /* pointer to int */
2int *a, *b, *c; /* all pointers to int */
3int *d, e; /* d is a pointer, e is plain int! */
4
5int x = 10;
6int *p = &x; /* p points to x */
7
8int *q; /* wild pointer: uninitialized (DANGEROUS) */
9
10int *r = NULL; /* safe: r points to nothing */

warning

An uninitialized pointer is a "wild pointer". Dereferencing it is undefined behavior. Always initialize pointers to a valid address or NULL.
The NULL Pointer

NULL represents a pointer pointing to nothing. Dereferencing NULL typically causes a segmentation fault. Always check for NULL before dereferencing.

null_pointer.c
C
1#include <stdio.h>
2
3int main(void) {
4 int *p = NULL;
5 if (p != NULL) printf("%d\n", *p);
6 else printf("p is NULL\n");
7
8 /* Function returning NULL on failure */
9 int nums[] = {10, 20, 30};
10 int *found = NULL;
11 for (int i = 0; i < 3; i++)
12 if (nums[i] == 20) { found = &nums[i]; break; }
13 if (found) printf("Found: %d\n", *found);
14 return 0;
15}
Pointer StateValueSafe to Dereference?
Valid variableValid addressYes
NULL pointer0x0No — segfault
Wild pointerRandom/garbageNo — undefined behavior
Dangling pointerFreed/invalid addressNo — use-after-free
Pointer Types

Pointers can point to any data type. void* is a generic pointer that can hold any address but cannot be dereferenced directly — you must cast it first.

pointer_types.c
C
1#include <stdio.h>
2
3int main(void) {
4 int x = 42; char c = 'A'; double d = 3.14;
5 int *pi = &x; char *pc = &c; double *pd = &d;
6 printf("int: %d, char: %c, double: %.2f\n", *pi, *pc, *pd);
7
8 /* void* — generic pointer */
9 void *generic = &x;
10 printf("generic as int: %d\n", *(int *)generic);
11 generic = &c;
12 printf("generic as char: %c\n", *(char *)generic);
13 return 0;
14}
📝

note

void* is used by malloc(), qsort(), pthread_create(), and many other standard library functions. Cast to the correct type before dereferencing.
Pointer Arithmetic

Pointer arithmetic scales by sizeof the pointed-to type. Adding 1 to an int* at 0x1000 advances to 0x1004 (4-byte ints), not 0x1001.

pointer_arithmetic.c
C
1#include <stdio.h>
2
3int main(void) {
4 int arr[] = {10, 20, 30, 40, 50};
5 int *p = arr;
6 printf("p = %p\n", (void *)p);
7 printf("p + 1 = %p\n", (void *)(p + 1)); /* +4 bytes */
8 printf("Distance: %td\n", &arr[4] - &arr[0]); /* element count */
9
10 for (int *ptr = arr; ptr < arr + 5; ptr++)
11 printf("%d ", *ptr);
12 printf("\n");
13 return 0;
14}
OperationResultScales By
p + nAddress + n * sizeof(*p)Size of type
p - qElement count between pointersDivides by sizeof(*p)
p++Advances to next elementsizeof(*p)
Arrays and Pointers

Array names decay into pointers to their first element in most contexts. However, arrays are not pointers — they have different sizeof semantics.

arrays_pointers.c
C
1#include <stdio.h>
2
3int main(void) {
4 int arr[5] = {10, 20, 30, 40, 50};
5 int *p = arr;
6
7 printf("arr[2] = %d\n", arr[2]);
8 printf("*(p+2) = %d\n", *(p + 2)); /* same thing */
9
10 printf("sizeof(arr) = %zu\n", sizeof(arr)); /* 20 */
11 printf("sizeof(p) = %zu\n", sizeof(p)); /* 8 */
12
13 int (*ptr_to_arr)[5] = &arr; /* pointer to array of 5 ints */
14 int *arr_of_ptrs[5]; /* array of 5 pointers to int */
15 arr_of_ptrs[0] = &arr[0];
16 return 0;
17}
Passing Arrays to Functions

When you pass an array to a function, it decays to a pointer. The function can modify the original. Always pass the length separately.

passing_arrays.c
C
1#include <stdio.h>
2
3void print_array(int *arr, int len) {
4 for (int i = 0; i < len; i++) printf("%d ", arr[i]);
5 printf("\n");
6}
7
8void double_elements(int *arr, int len) {
9 for (int i = 0; i < len; i++) arr[i] *= 2;
10}
11
12void print_matrix(int matrix[][4], int rows) {
13 for (int r = 0; r < rows; r++) {
14 for (int c = 0; c < 4; c++) printf("%3d ", matrix[r][c]);
15 printf("\n");
16 }
17}
18
19int main(void) {
20 int nums[] = {1, 2, 3, 4, 5};
21 print_array(nums, 5);
22 double_elements(nums, 5);
23 print_array(nums, 5);
24 return 0;
25}
Pointers and Strings

A string is a char array terminated by '\0'. A char* points to the first character. String manipulation is pointer manipulation.

strings_pointers.c
C
1#include <stdio.h>
2#include <string.h>
3
4int main(void) {
5 const char *msg = "Hello, World!";
6 printf("First char: %c\n", *msg);
7
8 for (const char *p = msg; *p != '\0'; p++)
9 printf("%c ", *p);
10 printf("\n");
11
12 char buf[] = "Hello";
13 buf[0] = 'J'; /* Legal: buf is an array */
14 printf("buf: %s\n", buf);
15 return 0;
16}
Pointers and Functions

Pointers enable pass-by-reference semantics. A function receiving an address can modify the caller's data.

pointers_functions.c
C
1#include <stdio.h>
2
3void swap(int *a, int *b) {
4 int temp = *a; *a = *b; *b = temp;
5}
6
7void get_min_max(int *arr, int len, int *min, int *max) {
8 *min = arr[0]; *max = arr[0];
9 for (int i = 1; i < len; i++) {
10 if (arr[i] < *min) *min = arr[i];
11 if (arr[i] > *max) *max = arr[i];
12 }
13}
14
15int main(void) {
16 int x = 10, y = 20;
17 swap(&x, &y);
18 printf("swap: x=%d, y=%d\n", x, y);
19
20 int nums[] = {5, 3, 8, 1, 9, 2};
21 int min, max;
22 get_min_max(nums, 6, &min, &max);
23 printf("min=%d, max=%d\n", min, max);
24 return 0;
25}

warning

Never return a pointer to a local variable. Local variables are destroyed when the function returns. Use static variables, heap allocation, or pass the result buffer as a parameter.
Dangling and Wild Pointers

A dangling pointer points to freed or out-of-scope memory. A wild pointer is uninitialized. Both are dangerous.

dangling_wild.c
C
1#include <stdio.h>
2#include <stdlib.h>
3
4int main(void) {
5 int *wild; /* wild: uninitialized */
6 int *p = (int *)malloc(sizeof(int));
7 *p = 42;
8 free(p);
9 /* *p = 10; /* UNDEFINED BEHAVIOR: use-after-free */
10 p = NULL; /* Fix: prevent dangling */
11
12 int *dangling;
13 { int local = 100; dangling = &local; }
14 /* dangling points to invalid stack memory */
15 return 0;
16}
ProblemCausePrevention
Wild pointerUninitializedInitialize to NULL or valid address
Dangling pointerFreed/out-of-scopeSet to NULL after free
Null dereferenceDereferencing NULLCheck for NULL first
Pointer to Pointer

A double pointer stores the address of another pointer. Essential for modifying pointers inside functions and building arrays of strings.

pointer_to_pointer.c
C
1#include <stdio.h>
2#include <stdlib.h>
3
4void allocate_int(int **pp, int value) {
5 *pp = (int *)malloc(sizeof(int));
6 if (*pp) **pp = value;
7}
8
9int main(void) {
10 int *p = NULL;
11 allocate_int(&p, 42);
12 if (p) { printf("*p = %d\n", *p); free(p); }
13
14 int x = 10, *ip = &x, **ipp = &ip;
15 printf("**ipp = %d\n", **ipp); /* ip -> x -> 10 */
16 return 0;
17}
Common Pointer Patterns
patterns.c
C
1#include <stdio.h>
2#include <string.h>
3#include <ctype.h>
4
5int string_length(const char *s) {
6 const char *p = s;
7 while (*p) p++;
8 return (int)(p - s);
9}
10
11void to_upper(char *s) {
12 while (*s) { *s = toupper((unsigned char)*s); s++; }
13}
14
15const char *find_char(const char *s, char c) {
16 while (*s) { if (*s == c) return s; s++; }
17 return NULL;
18}
19
20void copy_string(char *dst, const char *src) {
21 while ((*dst++ = *src++));
22}
23
24int filter_positive(int *src, int *dst, int len) {
25 int count = 0;
26 for (int *end = src + len; src < end; src++)
27 if (*src > 0) { *dst++ = *src; count++; }
28 return count;
29}
30
31int main(void) {
32 printf("Length: %d\n", string_length("Hello"));
33 char buf[] = "hello world";
34 to_upper(buf);
35 printf("Upper: %s\n", buf);
36
37 const char *f = find_char("Hello", 'l');
38 if (f) printf("Found at offset: %td\n", f - "Hello");
39
40 int nums[] = {-3, 5, -1, 8, 0, -2, 7};
41 int filtered[7];
42 int count = filter_positive(nums, filtered, 7);
43 for (int i = 0; i < count; i++) printf("%d ", filtered[i]);
44 printf("\n");
45 return 0;
46}
Pointer Pitfalls
pitfalls.c
C
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4
5void overflow_example(void) {
6 char buf[5];
7 strncpy(buf, "Hello World!", sizeof(buf));
8 buf[sizeof(buf) - 1] = '\0';
9}
10
11void use_after_free(void) {
12 int *p = malloc(sizeof(int)); *p = 42;
13 free(p); p = NULL;
14}
15
16void double_free(void) {
17 int *p = malloc(sizeof(int));
18 free(p); p = NULL;
19}
20
21int main(void) { return 0; }

best practice

Rules: (1) Initialize to NULL or valid memory, (2) Check malloc return values, (3) Set to NULL after free, (4) Never use freed memory, (5) Pass array lengths separately.
Memory Layout Visualization
memory_layout.c
C
1/*
2 int x = 42; int *p = &x; int **pp = &p;
3
4 Stack:
5 +------------------+-------------------+
6 | Address | Value |
7 +------------------+-------------------+
8 | 0x7fff5c8 (pp) | 0x7fff5c0 (of p) |
9 | 0x7fff5c0 (p) | 0x7fff5b8 (of x) |
10 | 0x7fff5b8 (x) | 42 |
11 +------------------+-------------------+
12 Dereferencing: **pp -> pp -> p -> x -> 42
13
14 Array: int arr[4] = {10, 20, 30, 40};
15 +------+------+------+------+
16 | 10 | 20 | 30 | 40 |
17 +------+------+------+------+
18 ^ ^
19 arr arr+2
20*/
$Blueprint — Engineering Documentation·Section ID: C-POINTERS·Revision: 1.0