20

I let gcc compile the following example using -Wall -pedantic:

#include <stdio.h>

int main(void)
{
  printf("main: %p\n", main); /* line 5 */
  printf("main: %p\n", (void*) main); /* line 6 */

  return 0;
}

I get:

main.c:5: warning: format ‘%p’ expects type ‘void *’, but argument 2 has type ‘int (*)()’
main.c:6: warning: ISO C forbids conversion of function pointer to object pointer type

Line 5 made my change the code like in line 6.

What am I missing to remove the warning when printing a function's address?

alk
  • 69,737
  • 10
  • 105
  • 255
  • 1
    Not sure if it's available to you, but you might investigate using register_printf_function to [define your own special format character and converter](http://www.gnu.org/software/libc/manual/html_node/Customizing-Printf.html#Customizing-Printf). – Bob Jarvis - Слава Україні Jun 07 '12 at 13:46
  • The issue is not that it's "dangerous". The issue is that the conversion is not defined by the C language, and thus can't be used in conforming C code. You could cast through an intermediate integer type (implementation-defined results) as long as you know one exists that can hold both function and object pointers). – R.. GitHub STOP HELPING ICE Jun 07 '12 at 14:06
  • @BobJarvis Do you have any idea how to get around `gcc` complaining about the newly introduced conversion type character (`warning: unknown conversion type character ‘P’ in format`) when compiling with option `-Wall`? But this also is another story ... – alk Jun 07 '12 at 15:55
  • 6
    @R..: This is C. We do dangerous. – Bob Jarvis - Слава Україні Jun 07 '12 at 16:53
  • @alk: try adding the -Wno-format option after -Wall, which (if memory serves) will turn off printf/scanf format checking. – Bob Jarvis - Слава Україні Jun 07 '12 at 16:59
  • @BobJarvis If could do as you proposed, I'd not had to pose this orignal question ... |-}. Anyway I found this here: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=47781 telling me that what I'd liked to have does not (yet?) exist. – alk Jun 07 '12 at 17:47

4 Answers4

17

This is essentially the only portable way to print a function pointer.

size_t i;
int (*ptr_to_main)() = main;
for (i=0; i<sizeof ptr_to_main; i++)
    printf("%.2x", ((unsigned char *)&ptr_to_main)[i]);
putchar('\n');
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • 2
    I like this, although it's silently ignoring any endianess issues... ;-) – alk Jun 07 '12 at 14:26
  • 1
    Trying to generalise this approach, I wonder if function pointers of different types are guaranteed to have the same size, although an answer (http://stackoverflow.com/a/189126/694576) to the question referenced by larsman makes me doubt this ... - anyway this is another story though. – alk Jun 07 '12 at 14:37
  • 3
    Conversion between any two functions pointers is defined and is round-trip-value-preserving, per the C standard; see the end of 6.3.2.3. – R.. GitHub STOP HELPING ICE Jun 07 '12 at 15:03
  • I would cast once, outside the loop, but this is the best approach. – Ben Voigt Jun 07 '12 at 15:27
  • Thanks for the edit. I had originally misread OP's question as using the address of `printf`, and forgot to fix one reference. :-) – R.. GitHub STOP HELPING ICE Jun 07 '12 at 16:55
  • @R.. Did I miss someting, but how is that portable? You still are converting a function pointer (`&ptr_to_main`) to an object pointer type (`unsigned char *`) but C does not allow conversions between function pointer types and object pointer types. – ouah Dec 23 '14 at 22:49
  • 1
    @ouah: Indeed you missed something. `ptr_to_main` is an object, and `&ptr_to_main` is an object pointer. – Ben Voigt Dec 23 '14 at 22:52
  • @BenVoigt Thanks yes agree, I must be tired :) – ouah Dec 23 '14 at 22:57
  • This assumes `CHAR_BIT==8`. With `CHAR_BIT > 8`, it will still print the full value of each byte, but the output can be ambiguous; `12345` could be either `12:345` or `123:45`. (This is practically never an issue.) – Keith Thompson Feb 17 '15 at 18:19
  • @Keith Thompson Could use `printf("%.*x", (CHAR_BIT+3)/4, ((unsigned char *)&ptr_to_main)[i]);` This avoids the magic number 2. – chux - Reinstate Monica Jul 19 '16 at 15:07
6

This whole idea is indeed non-portable, since some systems use different sized pointers to code and data.

What you really need is platform-specific knowledge of how big a function pointer is, and a cast to an integral type of that size. Unfortunately, I don't think anyone has standardized a intfuncptr_t analagous to intptr_t which can hold any data pointer.


As R. notes in his answer, you can always treat the pointer as an array of (possibly signed or unsigned) char, this way you don't need any integral type of the correct size.

Community
  • 1
  • 1
Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • Not any integral data type; only `unsigned char` (or probably `char` tho that's messy and iffy). Any other type is an aliasing violation and modern compilers are likely to emit bad code that doesn't do what you wanted it to. – R.. GitHub STOP HELPING ICE Jun 07 '12 at 16:54
  • @R..: A conversion can't be an aliasing violation. Only if you have two lvalues of different type referring to the same location, can there be an aliasing violation. Note that I'm talking about `reinterpret_cast(fnptr)`, not `*reinterpret_cast(&fnptr)`. Your answer involves legal aliasing, mine involves no aliasing at all. Oh, now I see, you're talking about my explanation of your method. Yes, `char` is needed there, I'll update. – Ben Voigt Jun 07 '12 at 17:22
  • 1
    There is `(uintmax_t)&main`, perhaps with a `static_assert( sizeof(uintmax_t) >= sizeof(&main) );` to hopefully ensure no undiagnosed UB due to out-of-range conversion – M.M Dec 23 '14 at 22:30
  • Re: "you can always treat the pointer as an array ...": can you please point out the place in the C standard where it is said? – pmor Jan 10 '22 at 16:53
  • 1
    FYI: relevant to "you can always treat the pointer as an array ...": "Variables must be accessed through their declared type or through a pointer to a character type" ([source](https://gustedt.wordpress.com/2016/08/17/effective-types-and-aliasing/)). – pmor Jan 10 '22 at 17:08
5

It's right there in the warning: ISO C forbids conversion of a function pointer to an object pointer type, which includes void*. See also this question.

You simply can't print the address of a function in a portable way, so you can't get rid of the warning.

You can print a function pointer using @R..'s suggestion.

Community
  • 1
  • 1
Fred Foo
  • 355,277
  • 75
  • 744
  • 836
  • 2
    Per POSIX, the representation of all pointers is the same, so on a POSIX system, you can `memcpy` from a function pointer variable to a data pointer variable and then print that. And of course you can *always* print the representation of any type byte-by-byte, with no POSIX dependency. – R.. GitHub STOP HELPING ICE Jun 07 '12 at 14:00
4

While converting a function pointer to a void pointer is technically dangerous, converting function pointers to void pointers is used in the POSIX standard, so it is almost sure to work on most compilers.

Look up dlsym().