54

Is there any way to print a pointer to a function in ANSI C? Of course this means you have to cast the function pointer to void pointer, but it appears that's not possible??

#include <stdio.h>

int main() {
    int (*funcptr)() = main;

    printf("%p\n", (void* )funcptr);
    printf("%p\n", (void* )main);

    return 0;
}

$ gcc -ansi -pedantic -Wall test.c -o test
test.c: In function 'main':
test.c:6: warning: ISO C forbids conversion of function pointer to object pointer type
test.c:7: warning: ISO C forbids conversion of function pointer to object pointer type
$ ./test
0x400518
0x400518

It's "working", but non-standard...

Jon Seigel
  • 12,251
  • 8
  • 58
  • 92

6 Answers6

52

The only legal way to do this is to access the bytes making up the pointer using a character type. Like this:

#include <stdio.h>

int main() {
    int (*funcptr)() = main;
    unsigned char *p = (unsigned char *)&funcptr;
    size_t i;

    for (i = 0; i < sizeof funcptr; i++)
    {
        printf("%02x ", p[i]);
    }
    putchar('\n');

    return 0;
}

Examining the bytes of the function pointer with an lvalue of type void *, or any non character type, is undefined behaviour.

What those bytes making up the function pointer actually mean is implementation-dependent. They could just represent an index into a table of functions, for example; or they could even be the first N characters of the function's name which is looked up in the symbol table when you call through the function pointer. The only operations that need be supported on a function pointer are calling the function through it and comparison against another function pointer or NULL for strict equality/inequality, so there is very wide latitude available in how they are implemented.

caf
  • 233,326
  • 40
  • 323
  • 462
  • 1
    Wouldn't it be better to use `uintptr_t` instead of `unsigned char` ? As in `int (*funcptr)() = main; uintptr_t *p = (uintptr_t *)&funcptr; printf("0x%" PRIxPTR "\n", *p);`, avoiding a loop? (Assuming C99 `stdint.h` and `inttypes.h` headers are supported.) – Chris Lutz Apr 30 '10 at 02:14
  • 10
    @Chris: `uintptr_t` has no relation with function pointers, and is optional. So from the POV of writing something totally portable no, it's not better. – Steve Jessop Apr 30 '10 at 02:32
  • 1
    caf's answer is correct. It's cute to see dreamlax's undefined code accepted as the answer while correct code languishes here. – Windows programmer Apr 30 '10 at 04:47
  • @Windows programmer: My answer was accepted before caf added his answer. – dreamlax Apr 30 '10 at 05:13
  • 1
    Yeah well this wont be as pretty as %p :( Not to mention endianess and no proper representation on segmented models. – L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳ Apr 30 '10 at 05:18
  • printf with no args is automatically converted to putchar on gcc btw :) even with -O0. – L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳ Apr 30 '10 at 05:20
  • It is true that endianness is a problem so this answer isn't perfect. On the other hand, where the standard defines %p for pointers to data, it doesn't require implementations to be sensible about endianness :-) – Windows programmer Apr 30 '10 at 07:01
  • 7
    Unfortunately, until they add a `printf` format specifier for printing function pointers, it's the best you can do in strictly conforming C. Endianness is a red herring - there's no guarantee that a "function pointer" is any kind of number at all (for example, it might be the first 64 characters of the function name, which is looked up in the symbol table when a call is made using the function pointer). This is why function pointers aren't convertible to and from other pointer types - it's so that the implementation has wide latitude in how it implements them. – caf Apr 30 '10 at 07:59
  • caf's right, no pointer is defined to be any sort of number, except that obviously it's made of bits, so it could be reinterpreted as a number, which is what this code does. It's also what casting to int normally does for pointers to objects, but that's not guaranteed either. Of course in a flat memory model it would be slightly irritating to be printing out addresses least-significant byte first, but if you've ever used a memory-level debugger on a le platform, you know that you can get used to that. – Steve Jessop Apr 30 '10 at 09:38
  • 1
    Well with ordinary object pointers, you can add number to a pointer and get another pointer, so there has to be at least some numerical component to them. Function pointers aren't like that though - the only things you can do with their value is compare them to other function pointers, compare them to `NULL`, or call the function they point to. – caf May 01 '10 at 01:37
  • 1
    to me,another very dark point of C std.Why should funct ptrs be treated as a special case is not clear;the same problems could be on "data pointers", e.g. given `int c;`, who can assure `&c` is the actual addr of the memory where the datum is,rather than an internal, implementation-defined "thing"?Maybe since the std says so, as usual, ok. But then why does not the std force implementations to give the "right" meaning to such an operation like printing the address of a func, where of course we're not interested in inspecting the impl,but to know a fact (the entry point of the actual code) – ShinTakezou Jul 27 '11 at 10:03
  • IIRC, `sizeof` can't applied on function types. So I doubt `sizeof funcptr` is legal. – P.P Aug 30 '15 at 21:41
  • 1
    @BlueMoon: `sizeof` can't be applied on function designators, but `funcptr` is not a function designator, it's a pointer to a function and applying `sizeof` to it is perfectly legal. – caf Aug 31 '15 at 00:35
  • 1
    "Punning the function pointer as a `void *`, or any non character type [...] is undefined behavior." Why punning to `char*` is valid whereas to `void*` is not? Both pointers have the same representation and alignment requirements. Also, as per Appendix A.6.2, when "A pointer to a function is converted to a pointer to an object or a pointer to an object is converted to a pointer to a function (3.3.4)", UB occurs. Are you sure it is at all valid to do what you did in the answer? – alecov Nov 19 '15 at 11:58
  • 1
    @Alek: By *"punning the function pointer as a `void *`"* I mean reading the bytes of a function pointer as if it were a `void *` object. `char *` is equally invalid - that's not a character type either. Punning as a character type would be reading the bytes of the function pointer as `char`s or `unsigned char`s. – caf Nov 19 '15 at 12:22
  • Your code emits a warning: `warning: comparison between signed and unsigned integer expressions [-Wsign-compare]`. I suggest changing `int i;` to `size_t i;` to fix it. – Spikatrix Mar 03 '16 at 12:59
  • @CoolGuy: Sure, I've updated it. The old one wasn't actually a problem - `i` could never be negative so the issue of promotion-to-unsigned never arises (and the compiler should be able to figure that out by seeing that `i` starts at zero and is only ever incremented). – caf Mar 04 '16 at 02:42
5

There's the use of unions that can get around the warning/error, but the result is still (most likely) undefined behavior:

#include <stdio.h>

int
main (void)
{
  union
  {
    int (*funcptr) (void);
    void *objptr;
  } u;
  u.funcptr = main;

  printf ("%p\n", u.objptr);

  return 0;
}

You can compare two function pointers (e.g. printf ("%i\n", (main == funcptr));) using an if statement to test whether they're equal or not (I know that completely defeats the purpose and could very well be irrelevant), but as far as actually outputting the address of the function pointer, what happens is up to the vendor of your target platform's C library and your compiler.

Dustin
  • 1,956
  • 14
  • 14
  • 1
    Type-punning with unions is not *undefined* behavior. It is *implementation-defined* behavior, and on most compilers it does exactly what you expect. – Rufflewind Oct 05 '16 at 21:30
2

With gcc 4.3.4, using switches -O2 -Wstrict-aliasing, dreamlax's answer will produce:

warning: dereferencing type-punned pointer will break strict-aliasing rules

Added: I think the objections to caf's answer about endianness and size are reasonable, which his solution does not address (no pun intended). Dustin's suggestion for using a union cast is probably legal (although from what I read there seems to be some debate, but your compiler does is important than the law). But his code could be simplified (or obfuscated, depending on your taste) by the one-liner:

printf("%p\n", ((union {int (*from)(void); void *to;})funcptr).to);

This removes the gcc strict-aliasing warning (but is it 'correct'?).

Aggregate casts won't 'work' if you are using the -pedantic switch, or are using e.g. SGI IRIX, so you'll need to use:

printf("%p\n", ((union {int (*from)(void); void *to;} *)&funcptr)->to);

But regarding the original question: its origin lies in the use of -pedantic, which I think is slightly pedantic :).

Further edit: Note you cannot use main in the last example, as in:

printf("%p\n", ((union {int (*from)(void); void *to;})   main).to);  // ok
printf("%p\n", ((union {int (*from)(void); void *to;} *)&main)->to); // wrong!

because of course &main decays to main.

Joseph Quinsey
  • 9,553
  • 10
  • 54
  • 77
2

Try this:

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


int main() {
    int (*funcptr)() = main;
    unsigned char *p = (unsigned char *)&funcptr;
    int i;

    /* sample output: 00000000004005e0 */
    printf("%016"PRIxPTR"\n", (uintptr_t)main);
    /* sample output: 00000000004005e0 */
    printf("%016"PRIxPTR"\n", (uintptr_t)funcptr);

    /* reflects the fact that this program is running on little-endian machine
    sample output: e0 05 40 00 00 00 00 00 */
    for (i = 0; i < sizeof funcptr; i++)
    {
        printf("%02x ", p[i]);
    }
    putchar('\n');

    return 0;
}

Used this flags:

gcc -ansi -pedantic -Wall -O2 -Wstrict-aliasing c.c

No warnings emitted using those flags

Michael Buen
  • 38,643
  • 9
  • 94
  • 118
  • On a machine where more bytes are needed to store a pointer to a function than a pointer to a UINT, your first two printf's will output truncated information. To get gcc to warn you about casts that potentially truncate (even though maybe not on your machine), maybe you need a flag to warn about potential truncations rather than about aliases. – Windows programmer Apr 30 '10 at 07:48
  • I think that's the most strictest flag that can be passed to a C compiler. Tomorrow I'll download 32 bit Ubuntu and check if that same codebase will run. I will test also in Visual C++ 2008 if it would run the same – Michael Buen Apr 30 '10 at 07:54
  • "On a machine where more bytes are needed to store a pointer to a function than a pointer to a UINT" <-- hmm.. (sans the segmented memory of pre-386, where the compiler has three flavor of pointers: near, far, huge(huge is just more of a compiler magic than the real architecture of machine)) i remember in assembly language, there's no variable size pointer, regardless of where it points at (UINT, char, short, function, etc) – Michael Buen Apr 30 '10 at 08:46
  • 2
    A Harvard architecture could have different-sized instruction and data pointers, and that's why C treats them as incompatible. I can't off-hand name a processor which has different sizes (it's much easier in C++, where pointer-to-member usually is a different and often variable size), but I also can't conclude from my own ignorance that there aren't any, and the question is about "in ANSI C", not "in ANSI C on x86". – Steve Jessop Apr 30 '10 at 23:01
  • hmm.. I read http://en.wikipedia.org/wiki/Modified_Harvard_architecture. It's not a Von Neumann machine; it says C was not designed for Hardvard Architecture, so any implementation of C language on that machine will surely be very non-standard, check the footnote. In essence, you cannot make C program that can run on all machine architectures. In Von Neumann architecture, memory can contain both instruction and data, thus have only one address space, hence same pointer size, the runtime can do sort of things like patching its own code during runtime and running runtime-generated instructions – Michael Buen May 01 '10 at 00:48
  • I guess for other esoteric architectures which are non-Von Neumann, they has special printf("%p") for instruction address(14 bit address) than its data address(8 bit only); say "%ip". Or the programmer would have to contend with scattering #ifdefs. I can't even wrap my mind on that 14 bit address, how we can implement looping on byte size so the instruction address can be printed. Making C implementations on those architectures hardly conforms to ANSI C – Michael Buen May 01 '10 at 01:21
  • I don't know the history or success of C implementations on Harvard architectures (and different "Harvard" architectures look different anyway, so maybe it's doable on some but not in general). I don't think your loop necessarily poses any problems on a Harvard architecture: it's `funcptr` that you're treating as char data, not the code of `main`, and `funcptr` is an automatic variable and so is data even on a Harvard architecture. I just dispute that your observation "i remember in assembly language, there's no variable size pointer" is enough to conclude anything about legal, portable, C. – Steve Jessop May 01 '10 at 17:35
  • The thing which standard C expressly prevents you doing is (a) assuming that data and code pointers are the same, and (b) converting a code pointer to a data pointer or otherwise doing anything that could allow you to address the machine code itself. A *pointer to* a code pointer is a data pointer, so your conversion of it to `char*` is fine, regardless of what code pointers look like. Just beware that function pointers can legally be a different size from void* in a conforming C implementation (on a von Neumann architecture or otherwise). There's no guarantee uinptr_t holds a function ptr. – Steve Jessop May 01 '10 at 17:38
  • Btw, that article linked to in the Wikipedia footnote talks about the difficulty of storing data in program space in a C implementation on a Harvard architecture. As I understand it, that doesn't mean you can't implement C, since data in code space isn't required to implement C. It's just bad news if your read-only static data, string literals, and so on, has to occupy valuable RAM in order to be addressable as data from C. So C is potentially inefficient with separate addressing. C *was* primarily "designed for" von Neumann architectures, but AFAIK it doesn't guarantee one. – Steve Jessop May 01 '10 at 17:46
  • `(uintptr_t)main` is undefined behaviour (or to put it another way, relies on compiler extensions not mentioned by the C Standard) – M.M Mar 04 '16 at 04:35
1

Cast the function pointer to an integer, then cast it to a pointer again to use "%p".

#include <stdio.h>

int main() {
    int (*funcptr)() = main;

    printf("%p\n", (void *)(size_t) funcptr);
    printf("%p\n", (void *)(size_t) main);

    return 0;
}

Note that on some platforms (e.g. 16-bit DOS in the "medium" or "compact" memory models), pointers to data and pointers to functions are not the same size.

dan04
  • 87,747
  • 23
  • 163
  • 198
  • 1
    Yes, "16-bit DOS" ran on a segmented memory architecture. For example, the 80286 would have a 4-byte `void *` and 2-byte `size_t`. – Judge Maygarden Apr 30 '10 at 13:59
-1

I'm not sure if this is considered good practice but it achieves the effect without a loop, unions, casts to non-pointer types, or extra dependencies besides string.h

int (*funcptr)() = main;
void* p = NULL;
memcpy(&p, (void**) &funcptr, sizeof(funcptr));
printf("%p", p);

No warnings with gcc -ansi -pedantic -Wall -Wstrict-aliasing on GCC 7.1.1

Jon Deaton
  • 3,943
  • 6
  • 28
  • 41
  • Maybe that is "-Wpedantic" not "-pedantic" because I get an error. error: ISO C forbids conversion of function pointer to object pointer type [-Werror=pedantic] – KANJICODER Apr 18 '20 at 00:14