What are Function Addresses in C- Embedded C Programming Training In Hyderabad

Need Help or More Information?

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

PREV_LEC | NEXT_LEC

Function Addresses in C

Lecture 53 of the free C programming course: every function has an address in memory. Learn how to get the function address in C, print it correctly, and why this idea powers embedded C programming.

Variables have addresses, and so do functions. A function address in C is simply the place in memory where the first instruction of that function is stored. Once you can read and store that address, you can build callbacks, interrupt vector tables, state machines and plug-in style drivers. This lecture of our free embedded C course introduces the idea with small programs you can run today.

It is also the first lecture of the function pointer topic in this free C programming course, so take your time: the next lecture builds directly on it.

What You Will Learn

  • Why a function must occupy memory, and what its address means
  • How to obtain the function address in C using only the function name
  • The correct, portable way to print a function address on modern systems
  • Why the printed numbers change between runs on Linux but stay fixed on a microcontroller
  • How to find function addresses with the nm tool

Prerequisites

You should know how functions are defined and called (the Functions chapter), what a pointer and an address are, and how to compile with gcc. If needed, go back through the earlier lectures of this free C tutorial.

Why Every Function Has an Address

When you compile a program, each function is translated into machine instructions. Those instructions must live somewhere in memory when the program runs: in the code section of an executable on Linux, or in flash memory on a microcontroller. The CPU executes a function by loading the address of its first instruction into the program counter. A function call is therefore nothing more than “jump to that address, and remember where to come back”.

Where functions live in a running program

code section → led_on starts hereled_off starts heremain starts here
data section → global variables
stack → local variables
heap → malloc blocks

Getting a Function Address in C

The rule is easy: write the function name without parentheses. With parentheses you call the function; without them you get its address. This is similar to an array name, which also turns into an address when used in an expression. Writing &led_on with the address-of operator gives the same value, and both forms are legal.

ExpressionMeaning
led_on()Call the function
led_onAddress of the function
&led_onAlso the address of the function (same value)
*led_onAlso the same function designator again

Complete Example: Printing Function Addresses

Save this as func_addr.c. It prints the addresses of three functions and then uses a variable to hold one of them.

#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>

static void led_on(void)
{
    puts("LED on");
}

static void led_off(void)
{
    puts("LED off");
}

int main(void)
{
    printf("address of main   : %#" PRIxPTR "\n", (uintptr_t)main);
    printf("address of led_on : %#" PRIxPTR "\n", (uintptr_t)led_on);
    printf("address of led_off: %#" PRIxPTR "\n", (uintptr_t)led_off);

    void (*handler)(void) = led_on;     /* name alone is the address */
    void (*handler2)(void) = &led_on;   /* explicit & gives the same value */
    printf("handler == handler2: %d\n", handler == handler2);

    led_on();
    led_off();
    handler();                          /* call through the pointer */
    return 0;
}

Build and Run

$ gcc -std=c11 -Wall -Wextra -Wpedantic -o func_addr func_addr.c
$ ./func_addr
address of main   : 0x55b4883df19d
address of led_on : 0x55b4883df169
address of led_off: 0x55b4883df183
handler == handler2: 1
LED on
LED off
LED on

Your hexadecimal numbers will not match these, and that is expected. The last line shows the interesting part: handler() printed “LED on” because the variable holds the address of led_on, and calling through it runs that function.

Why the Output Differs Between Runs

Modern Linux distributions build programs as position-independent executables and randomise where they are loaded (ASLR). The start of the program moves each time, but the distance between two functions inside the same program never changes. Run it twice and you will see different upper digits but the same last three digits, such as 169, 183 and 19d. If you want fixed addresses for experiments, build without PIE:

$ gcc -std=c11 -no-pie -o func_addr func_addr.c
$ ./func_addr
address of main   : 0x40118a
address of led_on : 0x401156
address of led_off: 0x401170
...
$ nm func_addr | grep -E ' (main|led_on|led_off)$'
0000000000401170 t led_off
0000000000401156 t led_on
000000000040118a T main

The symbol table agrees with the program. A lowercase t means a function with internal linkage (static); an uppercase T is a global one. On an ARM microcontroller you would run arm-none-eabi-nm on the ELF file, or read the linker map, to get the same information.

Printing the Address Correctly

Older books print function addresses with %u. That was acceptable on 16-bit and some 32-bit systems, but on a 64-bit machine an address does not fit in an unsigned int, so the output is truncated or undefined. Use one of the following instead.

ApproachNotes
(uintptr_t)fn with PRIxPTRUsed in this lecture. Standard C, no warnings with strict flags.
%p with (void *)fnWorks on common platforms and is required to work on POSIX systems, but ISO C does not guarantee it, so -Wpedantic may warn.
%u or %dWrong on 64-bit targets. Avoid.

Function Addresses in Embedded C Programming

This topic is not an academic curiosity. On a microcontroller, function addresses are everywhere:

  • Interrupt vector table: an array in flash whose entries are the addresses of the reset handler and all interrupt service routines. When an interrupt fires, the hardware reads the matching entry and jumps there.
  • Callbacks: a driver stores the address of a function supplied by the application and calls it when data arrives.
  • State machines and command tables: each state or command maps to the address of its handler.
  • Bootloaders: after verifying an application image, the loader reads a stored address and jumps to it.

Embedded detail: on ARM Cortex-M devices, the address of a Thumb function normally has bit 0 set to 1, so function addresses in the vector table look odd even though the code itself is aligned. That bit tells the processor to stay in Thumb state. It is one more reason to treat a function address as an opaque value and never do arithmetic on it.

Reading a Function Address in C from the Linker Map

When you build firmware for a microcontroller, the linker can produce a map file that lists every function and its final address. Open it and search for a function name, for example your timer interrupt handler. The number you find there is the same function address in C that your program would print, because both come from the same link step. Comparing the two is a quick way to confirm that the handler really ended up in the memory region you planned, such as flash for normal code or a fast RAM region for time-critical routines.

The same habit helps on Linux. If a crash report shows a faulting address, subtract the load base and look the offset up with nm or addr2line. Understanding that a function address in C is just a number that maps to a symbol is the first step towards reading stack traces and fault registers with confidence.

Common Mistakes with a Function Address in C

MistakeResultFix
Writing handler = led_on();Calls the function and stores its (void) result; compile error hereDrop the parentheses to store the address
Printing with %uTruncated or wrong output on 64-bitUse uintptr_t and PRIxPTR
Comparing addresses between two runsConfusion due to ASLRCompare offsets, or use -no-pie
Doing arithmetic on a function addressUndefined and non-portableTreat it as an opaque value
Converting between function and data pointers casuallyNot guaranteed by ISO CKeep function pointers as function pointers

Warning: never assume that a function pointer and a data pointer have the same size or representation on every architecture. Many 8-bit and Harvard-architecture microcontrollers keep code and data in separate address spaces, and their function pointers can be a different width.

Best Practices and Security Notes

  • Use function names without parentheses to get addresses, and keep them in properly typed pointer variables (next lecture).
  • Print addresses only for debugging. Logging them in production helps attackers defeat ASLR.
  • Use static for functions that are only used inside one file, as we did with led_on.
  • Use nm, objdump -d or the linker map to cross-check what you see at run time.
  • On small devices, place frequently-called handlers in the correct memory region using the linker script, but only when measurements show a need.

Summary and Key Takeaways

  • Every function occupies memory and therefore has an address.
  • Writing the function name without parentheses gives that address.
  • Print it with uintptr_t and PRIxPTR, not %u.
  • On Linux the base address moves with every run, but offsets between functions stay constant.
  • Vector tables, callbacks and state machines in embedded C programming are all built on function addresses.

Now that you can get a function address in C, the next lecture shows how to store it properly: you will declare a variable of pointer-to-function type, assign an address to it, and call a function through it. Keep going with this free C tutorial to learn C programming for embedded systems.

Frequently Asked Questions

How do I get the address of a function in C?

Use the function name without parentheses, or the address-of operator with the name. Both give the same address.

Why do function addresses change every time I run my program?

Linux loads position-independent executables at a random base address for security. Distances between functions remain constant.

Can I print a function address with %p?

On most systems yes, after casting to void pointer. ISO C does not guarantee the conversion, so converting to uintptr_t and using PRIxPTR is the strictly portable choice.

What is stored in an interrupt vector table?

The addresses of the exception and interrupt handler functions. The CPU reads the entry for the active interrupt and jumps to that address.

Are function addresses the same on a microcontroller?

They are fixed by the linker script and the build, so they do not move between runs. On Cortex-M a Thumb function address also has its lowest bit set.

What is the difference between led_on and led_on()?

The first is the address of the function; the second calls the function and produces its return value.

Your Free Embedded C Course Continues

Build real skills in embedded C programming with free, original lectures, tested code and interview questions from EmbeddedPathashala.

PREV_LEC | NEXT_LEC

Need Help or More Information?

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

2 Comments

Leave a Reply

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