Nearby lessons

89 of 124

C - Pointer Initialization

Initialising a pointer means giving it a valid address before you use it. Learn the ways to do that — &variable, an array name, malloc, or NULL — and why an uninitialised pointer is one of C's most dangerous constructs.

Initialise from a Variable's Address

The most common form. Declare and point in a single statement:

Example01
CCode Cell
1#include <stdio.h>
2 
3int main()
4{
5 int x = 42;
6 float f = 3.14f;
7 
8 int *pi = &x; /* initialised at declaration */
9 float *pf = &f;
10 
11 int *pLater; /* declared now... */
12 pLater = &x; /* ...pointed later */
13 
14 printf("*pi = %d\n", *pi);
15 printf("*pf = %.2f\n", *pf);
16 printf("*pLater = %d\n", *pLater);
17 return 0;
18}
Output
*pi     = 42
*pf     = 3.14
*pLater = 42

The Uninitialised Pointer

This is the bug that defines C's reputation. int *p; creates a pointer holding whatever bytes were already on the stack. Dereferencing it writes to a random address — which may crash instantly, corrupt an unrelated variable, or appear to work until a customer runs it.
Example02
CCode Cell
1#include <stdio.h>
2 
3int main()
4{
5 /* int *wild;
6 printf("%d", *wild); UNDEFINED - reads a random address
7 *wild = 42; WORSE - writes to a random address */
8 
9 int x = 0;
10 int *safe = &x; /* always give it a target */
11 
12 *safe = 42;
13 printf("x = %d\n", x);
14 return 0;
15}
Output
x = 42

NULL — Pointing Nowhere Deliberately

When you have no address yet, NULL says so explicitly. Unlike garbage, NULL can be tested:

Example03
CCode Cell
1#include <stdio.h>
2 
3int main()
4{
5 int *p = NULL; /* explicitly points nowhere */
6 int x = 100;
7 
8 if (p == NULL)
9 printf("p is NULL - nothing to read yet\n");
10 
11 p = &x; /* now it has a target */
12 
13 if (p != NULL)
14 printf("p now points to %d\n", *p);
15 return 0;
16}
Output
p is NULL - nothing to read yet
p now points to 100

NULL Is Not Zero-the-Value

NULL is a null pointer constant. Testing a pointer for truth tests whether it is non-null, which reads cleanly:

Example04
CCode Cell
1#include <stdio.h>
2 
3int main()
4{
5 int *p = NULL;
6 int x = 0;
7 int *q = &x; /* points to a variable holding 0 */
8 
9 printf("p is %s\n", p ? "valid" : "NULL");
10 printf("q is %s, and *q = %d\n", q ? "valid" : "NULL", *q);
11 
12 /* Note: q is valid even though the VALUE it points to is 0 */
13 return 0;
14}
Output
p is NULL
q is valid, and *q = 0

Initialise from an Array

An array name is already the address of its first element, so no & is needed:

Example05
CCode Cell
1#include <stdio.h>
2 
3int main()
4{
5 int a[5] = {10, 20, 30, 40, 50};
6 
7 int *p = a; /* same as &a[0] */
8 int *q = &a[2]; /* point into the middle */
9 
10 printf("*p = %d (first element)\n", *p);
11 printf("*q = %d (third element)\n", *q);
12 printf("p == &a[0] : %s\n", (p == &a[0]) ? "yes" : "no");
13 return 0;
14}
Output
*p = 10  (first element)
*q = 30  (third element)
p == &a[0] : yes

Initialise from malloc — and Check It

malloc returns an address, or NULL if it fails. Checking is not optional:

Example06
CCode Cell
1#include <stdio.h>
2#include <stdlib.h>
3 
4int main()
5{
6 int *p = malloc(sizeof(int));
7 
8 if (p == NULL) /* always check */
9 {
10 printf("Allocation failed\n");
11 return 1;
12 }
13 
14 *p = 42;
15 printf("*p = %d\n", *p);
16 
17 free(p);
18 p = NULL; /* avoid a dangling pointer */
19 return 0;
20}
Output
*p = 42

Allocating an Array

Multiply by the count. calloc does the same and zero-fills the memory:

Example07
CCode Cell
1#include <stdio.h>
2#include <stdlib.h>
3 
4int main()
5{
6 int n = 5, i;
7 int *a = malloc(n * sizeof(int));
8 int *b = calloc(n, sizeof(int)); /* zero-filled */
9 
10 if (a == NULL || b == NULL) return 1;
11 
12 for (i = 0; i < n; i++) a[i] = (i + 1) * 10;
13 
14 printf("malloc (filled by us): ");
15 for (i = 0; i < n; i++) printf("%d ", a[i]);
16 
17 printf("\ncalloc (auto-zeroed) : ");
18 for (i = 0; i < n; i++) printf("%d ", b[i]);
19 printf("\n");
20 
21 free(a);
22 free(b);
23 return 0;
24}
Output
malloc (filled by us): 10 20 30 40 50
calloc (auto-zeroed) : 0 0 0 0 0 

The Dangling Pointer

A pointer to memory that no longer exists. Freeing does not change the pointer — it only invalidates what it refers to:

Example08
CCode Cell
1#include <stdio.h>
2#include <stdlib.h>
3 
4int main()
5{
6 int *p = malloc(sizeof(int));
7 if (p == NULL) return 1;
8 
9 *p = 42;
10 printf("Before free: *p = %d\n", *p);
11 
12 free(p);
13 /* printf("%d", *p); DANGLING - undefined behaviour */
14 
15 p = NULL; /* the fix */
16 if (p == NULL) printf("After free : p is NULL, safely testable\n");
17 return 0;
18}
Output
Before free: *p = 42
After free : p is NULL, safely testable

Never Return a Local's Address

A local variable dies when its function returns, so its address is immediately worthless:

Example09
CCode Cell
1#include <stdio.h>
2#include <stdlib.h>
3 
4/* BROKEN: local dies on return
5int *broken(void)
6{
7 int local = 42;
8 return &local; <- dangling the instant we return
9}
10*/
11 
12int *works(void) /* heap memory outlives the function */
13{
14 int *p = malloc(sizeof(int));
15 if (p != NULL) *p = 42;
16 return p;
17}
18 
19int main()
20{
21 int *result = works();
22 
23 if (result != NULL)
24 {
25 printf("*result = %d\n", *result);
26 free(result);
27 }
28 return 0;
29}
Output
*result = 42

The Safe Pattern

Initialise to NULL, assign a real address, check before use, free, then NULL again:

Example10
CCode Cell
1#include <stdio.h>
2#include <stdlib.h>
3 
4int main()
5{
6 int *data = NULL; /* 1. start at NULL */
7 int i;
8 
9 data = malloc(3 * sizeof(int)); /* 2. allocate */
10 if (data == NULL) return 1; /* 3. check */
11 
12 for (i = 0; i < 3; i++) data[i] = i + 1; /* 4. use */
13 for (i = 0; i < 3; i++) printf("%d ", data[i]);
14 printf("\n");
15 
16 free(data); /* 5. free */
17 data = NULL; /* 6. NULL it again */
18 
19 printf("data is %s\n", data ? "valid" : "NULL");
20 return 0;
21}
Output
1 2 3
data is NULL

Common Mistakes

MistakeConsequence
int *p; *p = 5;Writes to a random address
int *p = 42;42 becomes the address, not the value
Not checking mallocDereferences NULL on failure
Using a pointer after freeDangling pointer, undefined behaviour
Returning a local's addressPoints at destroyed stack memory
Freeing twiceHeap corruption or a crash
Trainer's Note: free(NULL) is explicitly safe and does nothing. That is why setting a pointer to NULL after freeing it also protects you against a double free — the second free becomes a harmless no-op.
📝 Key Takeaways
  • Always initialise a pointer at declaration — to an address or to NULL.
  • An uninitialised pointer holds garbage and crashes when dereferenced.
  • NULL means "points to nothing" and is safe to test.
  • Always check malloc's return value before using it.
  • Set a pointer to NULL after freeing it.

🧠 Test Your Knowledge

4 Questions
Progress: 0 / 4