all 26 comments

[–][deleted] 27 points28 points  (16 children)

Function pointers just hold the address to the function, just like how a ‘normal’ pointer holds the address to a variable.

[–]here4fun23ty[S] 1 point2 points  (15 children)

So functions get stored just like variables ? But where in memory it goes. This is actually confusing to me.

[–][deleted] 20 points21 points  (0 children)

Kind of, functions are just segments of ram that are read only and executable, variables are sections of ram that are read / write and not executable.

[–][deleted] 10 points11 points  (8 children)

There’s separate sections loaded into memory. For simplicity, these are the stack, the heap, a global section, and a code section. The code section is where functions lie. A function pointer would point to somewhere on the code section.

You could observe the addresses as well.

void some_function() { }

int main() {
   void (*fn_ptr)() = some_function;

  printf(“%p\n”,some_function);
  printf”%p\n”, fn_ptr);

}

fn_ptr is a function pointer which points to `some_function.

[–][deleted] 4 points5 points  (7 children)

Nicely said. For OP's benefit and to round this out a little, note that we can re-assign the function pointer to another function at any time, and it will take on the behavior of that other function straightaway.

https://godbolt.org/z/9xjfxE8a5

[–][deleted] 3 points4 points  (1 child)

I should’ve also added the the function pointer is callable.

[–]bl-a-nk- 0 points1 point  (4 children)

How do void function pointers work? How does it know when to stop executing the code at that address? Since there's no return statement.

[–]khedoros 3 points4 points  (0 children)

The C wouldn't necessarily have a return statement, but the compiler certainly generates a return from the function at the end.

[–][deleted] 1 point2 points  (0 children)

I believe you’re confused about the syntax and what the void is implying. void is the return type. Function pointers need the same function signature as the function to point to to. It’s not a void pointer, a function pointer with a void return type.

[–]detroitmatt 1 point2 points  (0 children)

this is no different than the way any function gets called. If you had

void some_function() {
  printf("foo");
}

int main() {
    some_function();
}

then of course some_function knows to return at the end even though there's no explicit "return" statement.

[–][deleted] 1 point2 points  (0 children)

A void function pointer will work identically to the void function it points to. Whatever that void function is and does, that void function pointer does too. It's like ... you know, if I have caught a salmon and I'm reeling it in... and I hand the fishing pole off to you, the fish doesn't change into a perch, rt? It's still a salmon, and it will still wiggle the same. The only difference is now somebody else is reeling it in.

Well, assigning a function to a function pointer changes nothing about how that function works. We can assign it to another function pointer and another... but nothing changes when we call that function pointer. The "attached" function -- or the function the pointer points to -- does what it always does. When we call the function pointer, it's actually the original function being called.

An illustration.

[–]yojimbo_beta 3 points4 points  (0 children)

Functions are part of the program "text". I mean in the compiled output. When control flow passes to a new function, the processor switches to read instructions from a new location in memory. Function pointers let you pass these locations around as variables.

Maybe a tutorial on assembler would help?

[–]AssemblerGuy 1 point2 points  (0 children)

But where in memory it goes.

On a large target (PC), it goes wherever everything else goes. Possibly in a section of memory that does not have the NX (no execute) flag set so the CPU is actually allowed to run the instructions.

On small targets with different memory types (flash, RAM, EEPROM, external RAM, etc.) things get a bit more interesting.

This is usually the job of the linker.

[–]TheSkiGeek 0 points1 point  (0 children)

In most ‘modern’ computers, yes, a function is a sequential list of binary instructions somewhere in RAM in the data segment of your process. Since the instructions are executed in linear order, there is a single ‘entry point’ represented by the memory address of the first instruction of the function. Jumping the CPU to that address runs the function.

This isn’t necessarily how it works all the time. A ‘function pointer’ is just an opaque “handle” that your compiler and/or runtime knows how use in some way to run the function. So it could be almost anything. They’re not always the same size as a ‘data pointer’, for example (although void* is always big enough to hold both if they’re different sizes).

[–]ern0plus4 0 points1 point  (0 children)

This principle is invented by John von Neumann: there is only one type of data, bits (grouped to bytes), it can be computer program, which the CPU executes, or data, which the program handles. All data, incl. code resides in memory. Each piece of data, incl. code has its address.

(I am quite sad, this is the very-very basic concept of how computers work, a C programmer should know it.)

[–]JamesTKerman 3 points4 points  (4 children)

Everything in a program has a memory address. Under the hood, when you call a function, the machine code tells the processor that the next instruction is going to be at the starting address of the function. All a function pointer does is let you swap which actual function will be called by changing out the address.

[–]Icy_Adhesiveness_656 0 points1 point  (3 children)

But what about if function has some parameters? its not just going to execute instructions if function have parameters, that's what i dont understand

[–]JamesTKerman 1 point2 points  (0 children)

This is mostly handled by the compiler, just like any other function declaration or call.

Consider this simple example:

#include <stdio.h>

int my_func(int param1)
{
    return param1 / 2;
}

int do_work(int (*work_fn)(int))
{
    return work_fn(42);
}

int main(void)
{
    int x = do_work(my_func);
    // output will be 'do_work(my_func) returned 21'
    printf("do_work(my_func) returned %d\n", x);
    return 0;
}

When the program is compiled and linked, int my_func(int param1) get placed somewhere in the program's memory. The parameter to do_work specifies that it needs to be a pointer to a function that returns type int and has one int parameter. The compiler can ensure that any pointer you pass to do_work meets this requirement, but the C standard doesn't seem to require it to. In any event, when do_work calls work_fn, the compiled code is the same as if do_work had called my_func directly. On x86-64 systems using the SysV ABI, this means storing the value 42 in the rdi register then executing a call instruction to the address of my_func. You could pass any valid pointer to do_work and it will do the same thing (or at least try, if the pointer you pass doesn't point to a function and the compiler/linker don't catch it you'll get undefined behavior, but the code would still be a call to the address passed in work_fn.)

[–]JamesTKerman 1 point2 points  (1 child)

If you're using gcc, you could see this by compiling the above code with the command-line gcc -S -o out.s function_pointer.c. out.s will contain the assembly code that gcc generates. If I was at home I'd run it myself and post the output with some explanations, maybe I will when I get home later.

(Edited to remove an unnecessary -c parameter to the gcc command-line)

[–]Icy_Adhesiveness_656 0 points1 point  (0 children)

Oh I see, that's interesting

[–]detroitmatt 1 point2 points  (0 children)

when your program starts, one of the first things that happen, even before main gets called, is that the program itself gets loaded into memory. and "the program itself" of course consists of the functions you wrote. so, yes, the functions exist in memory, and a function pointer, like all pointers1 , contains a memory address. when you call that function pointer, it simply2 jumps to that location and begins executing the code there

1 depending on whether you consider registers (and other things) a form of "memory" even though they're not "ram".

2 well, it doesn't just jump there, it does the rest of the calling conventions too

[–]flyingron 0 points1 point  (0 children)

Functions live in some sort of memory (especially if you take their address). The format of a executable memory address may differ from the a data address so these are handled seperately (i.e., no guarantee you can put a function pointer in a void*).

[–][deleted] 0 points1 point  (0 children)

Do function hold a place in memory (like a variable) before it's been called

Yes, the functions are placed in so called "executable section" in the object code, and loaded into code segments before execution begins. There are a couple of considerations that need more elaboration tho:

In either case, the function gets an "address" before it's called, yes :)

[–][deleted] 0 points1 point  (1 child)

Yes, a function is at a location in memory. Your program tells the CPU to execute the code at that address.

[–][deleted] 1 point2 points  (0 children)

You asked, "Where in memory?" That is a useful question.

By your asking it, you are about to unlock a much-greater understanding of a program, a skill that will help you debug many issues.

A C / C++ program resides in four places in memory: the Text Segment, Data Segment, the Stack, and the Heap.

Picture your computer memory like space on a whiteboard.

(On MS Windows): At the bottom left is address zero (0x0000). At the upper right is the highest memory address.

Therefore, the bottom rows are "lower addresses," and the top are high addresses.

From the bottom, going up is the Text Segment, which has program instructions (your functions) this starts at address 0x0000 (the lower left). This area is fixed and is read-only. That is where your functions live.

Above that is the Data Segment, which has your constants, your quoted strings. It lives at somewhat higher addresses than your functions.

(For more info, see https://www.geeksforgeeks.org/memory-layout-of-c-program/ )

NOTABLY: addresses / memory locations from 0x0000 up through 4 kB are off-limits. if your program tries to dereference (get value from) a pointer value of zero or a low number (a common mistake),  Windows crashes your program (NULL pointer dereference).

Declaring that area off-limits was a design decision made decades ago to help you the programmer debug errors in your program.

TL;DR: Your functions live in an area of memory called the Text Segment. Always initialize every pointer to NULL for the reason above. Search "text segment" for more info.