views:

425

answers:

5

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...

+5  A: 

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;
    int i;

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

    return 0;
}

Punning the function pointer as a void *, or any non character type, as dreamlax's answer does, 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.

caf
Wouldn't it be better to use `uintptr_t` instead of `unsigned char` ? As in `int (*funcptr)() = main; uintptr_t *p = (uintptr_t *) printf("0x%" PRIxPTR "\n", *p);`, avoiding a loop? (Assuming C99 `stdint.h` and `inttypes.h` headers are supported.)
Chris Lutz
@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
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
@Windows programmer: My answer was accepted before caf added his answer.
dreamlax
Yeah well this wont be as pretty as %p :( Not to mention endianess and no proper representation on segmented models.
Longpoke
printf with no args is automatically converted to putchar on gcc btw :) even with -O0.
Longpoke
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
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
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
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
A: 

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
Useful information, but this doesn't really answer his question, it should have been a comment on my answer as opposed to a separate answer altogether.
dreamlax
@dreamlax: Sorry about the separate answer, but I've only 31 stackoverflow points, less than the 50 needed for comments. But I'll convert this answer to a comment ASAP.
Joseph Quinsey
@caf: Sorry. I see that you already made this point about dreamlax's answer in your answer.
Joseph Quinsey
+1  A: 

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
A: 

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
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
A: 

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
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
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
"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
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
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
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
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
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
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