What are Assigning Addresses to Pointers – Free Online C Programming Course

Need Help or More Information?

Questions about this free C programming course, embedded C training or career guidance? Get in touch.

Free C Programming Course · Chapter 8 · Pointers · Lecture 4

Assigning Addresses to Pointers

Give your pointers real targets, initialise them at declaration, copy one pointer into another and make unused pointers safe with NULL. Free C tutorial for embedded systems learners.

&x
Source of a valid address
NULL
Points to nothing
0
Wild pointers allowed

A freshly declared pointer is an empty promise. In this lecture of our free C programming course we cover assigning addresses to pointers: how to point at a variable, how to initialise safely, how to copy a pointer, and what NULL really guarantees.

These habits are the dividing line between code that works and firmware that crashes at 3 a.m. For anyone studying embedded C programming through this free embedded C course, safe pointer initialisation is not optional.

What You Will Learn

By the end of this lecture you can:

  • Explain why a pointer must never be used before it is assigned.
  • Assign the address of a variable to a pointer using &.
  • Declare and initialise a pointer in a single statement.
  • Explain why the base type of pointer and target must match.
  • Copy one pointer to another so both point to the same variable.
  • Use NULL to mark a pointer that points to nothing, and test for it.

Prerequisites

Complete the earlier lectures in this chapter: memory and addresses, the address operator, and declaring pointer variables. You need to be able to write int *p; and to understand that &x gives the address of x. That is all we need to get going.

Why Uninitialised Pointers Are Dangerous

When you declare an ordinary local variable and do not set it, it holds garbage. A pointer behaves the same way, but the consequence is far worse. A local int with garbage gives you a wrong number. A local pointer with garbage points to a random place in memory. If you then read through it you get unpredictable data; if you write through it you may overwrite something unrelated, such as another variable, a return address or a hardware register.

What an unset pointer holds
Declared but never assigned: the pointer holds leftover bits from earlier use of that memory.
ptr = unknown address points to somewhere random
Using it can crash the program, corrupt data, or appear to work until it fails later.
Rule: always assign a valid address, or NULL, before using a pointer. A pointer with an unknown value is called a wild pointer. The program may crash at once, or it may silently damage other data and fail somewhere far away, which is the hardest kind of bug to find.

Assigning Addresses to Pointers

You give a pointer a target with an ordinary assignment, putting the address operator on the right-hand side:

int   level = 30;
float ratio = 2.5f;

int   *ip;
float *fp;

ip = &level;     /* ip now holds the address of level */
fp = ∶     /* fp now holds the address of ratio */

After these two assignments, ip points to level and fp points to ratio. Note that we assign without a star on the left: ip = &level; changes what ip holds. The star in a later statement means something different, and we study that in the next lecture.

After ip = &level and fp = &ratio
ip (holds address of level) points to level = 30
fp (holds address of ratio) points to ratio = 2.5

Initialising at Declaration

You can combine declaration and assignment, which is the safest style because the pointer never exists in an unset state:

int   level = 30, *ip = &level;
float ratio = 2.5f, *fp = ∶

Here the variable is declared before the pointer, in the same statement. That order matters: the compiler must already know about level to take its address. Writing int *ip = &level, level = 30; would use level before its declaration finishes and is an error. In the real world, many teams prefer one declaration per line anyway:

int  level = 30;
int *ip    = &level;

Matching the Base Type

An int * is meant to hold the address of an int. If you assign the address of a float to it, you break the contract: later, reading through the pointer would interpret float bits as an integer. Older textbooks say the compiler stays silent and the output is simply wrong. Modern GCC is stricter. Try this:

int main(void)
{
    float ratio = 2.5f;
    int *ip = ∶
    return 0;
}

GCC 13 reports:

bad2.c: In function 'main':
bad2.c:4:15: warning: initialization of 'int *' from incompatible pointer type 'float *' [-Wincompatible-pointer-types]
    4 |     int *ip = ∶
      |               ^

Since GCC 14, this incompatible-pointer-types problem is an error by default, not just a warning, so such code no longer compiles at all. Never silence this diagnostic with a cast just to get rid of it. The warning is telling you the truth.

AssignmentResult
int *ip = &int_var;Correct: types match
float *fp = &float_var;Correct: types match
int *ip = &float_var;Incompatible pointer type: warning or error
char *cp = &int_var;Incompatible pointer type: warning or error

Copying One Pointer into Another

You may assign one pointer to another when their base types are the same. The assignment copies the address, so afterwards both pointers point to the same target. No data is duplicated. There is still only one variable.

int *first  = &gain;
int *second = first;     /* second now holds the same address */
Two pointers, one target
first points to gain = 40
second points to gain = 40 (the same variable)

This is called aliasing, and it is powerful and dangerous in equal measure: a change made through one pointer will be visible through the other. In embedded code, a pointer to a shared buffer given to both an interrupt handler and the main loop is a classic example.

The NULL Pointer

Sometimes you have a pointer but no valid target yet, or you are done with its target. For that case C gives you a special value, the null pointer. You write it with the macro NULL, defined in <stddef.h> and also made available by <stdio.h>, <stdlib.h> and <string.h>:

#include <stddef.h>

int *ptr = NULL;

The language guarantees that a null pointer compares unequal to the address of any real object or function. That gives you a reliable way to say “this pointer points to nothing”, and to test it before use:

if (ptr != NULL) {
    /* safe to follow ptr here */
}

You may assign the constant zero to a pointer of any type, and the compiler turns it into a null pointer of that type. Writing NULL is clearer than writing 0, so always prefer it. Strictly speaking, the internal bit pattern of a null pointer is not required to be all zeros on every machine, but you never need to care: just use NULL and compare against NULL.

Pointer states
StateHow it happensSafe to follow?
Wild (uninitialised)Declared, never assignedNo, unknown target
ValidAssigned &variable of the right typeYes, while the variable exists
NullAssigned NULLNo, points to nothing; test first
DanglingTarget no longer existsNo, target has gone

Demo Program

This program ties the whole lecture together: it points one pointer at a variable, copies it, compares the two, makes a null pointer and tests it.

#include <stdio.h>
#include <stddef.h>

int main(void)
{
    int gain = 40;
    int *first = &gain;
    int *second = first;
    int *idle = NULL;

    printf("first  holds %p\n", (void *)first);
    printf("second holds %p\n", (void *)second);
    printf("same target? %s\n", first == second ? "yes" : "no");
    printf("idle holds %p\n", (void *)idle);

    if (idle == NULL)
        printf("idle points nowhere, safe to skip\n");

    float ratio = 2.5f, *rp = &ratio;
    printf("ratio is %.1f and rp %s\n", ratio, rp == &ratio ? "points to it" : "is elsewhere");
    return 0;
}

Build and run

gcc -Wall -Wextra -o ptr_assign ptr_assign.c
./ptr_assign

Sample output

first  holds 0x7fff17aa5960
second holds 0x7fff17aa5960
same target? yes
idle holds (nil)
idle points nowhere, safe to skip
ratio is 2.5 and rp points to it

The two addresses are identical because second is a copy of first. Your address will differ. The text (nil) is how the GNU C library prints a null pointer with %p; other libraries may print 0x0 or (null), which is another reason never to depend on the printed form.

Pointer Assignment in Embedded C Programming

In c programming for embedded systems, these rules have very practical consequences:

  • Initialise every pointer. Local variables on a microcontroller stack hold leftovers from earlier calls. A wild pointer can write into a peripheral register and toggle a motor.
  • NULL as “not configured”. Callback pointers and optional buffers are set to NULL until the application provides them, and drivers check before using them.
  • Address zero is not always protected. On a PC with Linux, following a null pointer usually stops the program with a segmentation fault, which is a helpful alarm. On many bare-metal microcontrollers address 0 is a perfectly valid location (often an alias of flash or the vector table), so following a null pointer may read or write something real without any fault. Your own NULL checks are the only protection.
  • Do not point at stack data that dies. Never store the address of a local variable in a global pointer and use it after the function returns.

Habit worth building today

Write int *p = NULL; instead of int *p; whenever you cannot assign the real target yet. A pointer that is NULL can be tested. A wild pointer cannot.

Common Mistakes

MistakeConsequenceFix
Using a pointer before assigning itWild pointer, crash or silent corruptionAssign &var or NULL immediately
Assigning the address of a different typeWarning or error, wrong data readMatch the base type
Writing int *p = &x, x; (variable after pointer)Compile errorDeclare the variable first
Assuming a copy of a pointer copies the dataSurprise changes through the aliasRemember only the address is copied
Skipping the NULL checkCrash on a PC, silent corruption on bare metalTest if (p != NULL) before use
Comparing pointers to integers like p == 5Meaningless, compiler warningCompare pointers with pointers or NULL

Best Practices

  • Initialise every pointer at declaration, to a real address or NULL.
  • Compile with -Wall -Wextra and treat incompatible pointer warnings as bugs.
  • Use NULL, not a bare 0, so the intent is obvious.
  • Check pointers received as function parameters before using them.
  • Keep pointer lifetimes shorter than the lifetime of their targets.

Security note

Uninitialised and dangling pointers are behind a large share of memory-safety vulnerabilities. Setting pointers to NULL after you finish with their target removes a whole class of “use after free” accidents, because a NULL check can then catch the stale pointer.

Key Takeaways

  • Never use a pointer before it holds a valid address or NULL.
  • ptr = &variable; makes a pointer point to a variable.
  • A pointer can be initialised at declaration if the variable is declared first.
  • Pointer and target must have matching base types.
  • Assigning one pointer to another copies the address, not the data.
  • NULL marks a pointer that points to nothing, and you should test for it.

Conclusion

Your pointers now point at real things. We have a variable, a pointer holding its address, and a safe way to say “nothing here”. The final lecture in this part of the free C tutorial follows the pointer to reach the target, which is called dereferencing. It is the step that finally lets you read and change a variable without using its name. As practice, write a program with three int variables and one pointer, and make the pointer visit each variable in turn, printing its address each time.

Frequently Asked Questions

How do I assign an address to a pointer in C?

Use the address operator on a variable of the matching type: ptr = &variable;.

What is a wild pointer?

A wild pointer is one that was declared but never assigned, so it holds an unknown address. Following it is unsafe.

What does NULL mean in C?

NULL is the null pointer constant. A pointer set to NULL is guaranteed not to point to any valid object or function.

Can I assign a float address to an int pointer?

You should not. The types are incompatible. GCC warns about it, and since GCC 14 it is an error by default.

What happens when I copy one pointer to another?

Only the address is copied. Both pointers then point to the same variable, so a change made through one is visible through the other.

Does dereferencing NULL always crash?

No. On Linux it normally causes a segmentation fault, but on many bare-metal microcontrollers address 0 is valid memory, so it may not fault at all. Always check for NULL yourself.

Is NULL the same as 0?

Assigning the constant 0 to a pointer produces a null pointer, but writing NULL is clearer and is the recommended style.

Next in the Free C Programming Course

Time to follow the pointer: dereferencing, the indirection operator, and reading and writing variables through their addresses, then a quiz and interview questions.

Need Help or More Information?

Questions about this free C programming course, embedded C training or career guidance? Get in touch.

1 Comment

Leave a Reply

Your email address will not be published. Required fields are marked *