1

My source file name ends with cpp.

Below are 2 declarations:

void (*f1)(void);

void *f2(void);

I think:

  • f1 is a valid function pointer.
  • f2 is a function that returns a void * pointer.

Then I have another function:

void f3(void *p_func);

I think:

  • Despite the p_func parameter name, f3 is just a function that takes a void * pointer, i.e. a 32-bit unsigned integer on a 32 bit machine.

And below 2 statements:

f3(&f1); //<----------It works
f3(&f2); //<----------Compiler error

The compiler error is:

no known conversion from 'void *(*)()' to 'void *' for 2nd argument;

My questions:

  • &f1 should be the address of a function pointer, why it can be passed to f3 as void *?
  • Why &f2 is void *(*)()?
  • And what does void *(*)() even mean?
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
smwikipedia
  • 61,609
  • 92
  • 309
  • 482
  • 3
    Are you 100% sure you are not writing [tag:c++] code instead of [tag:c]? – Iharob Al Asimi Aug 25 '16 at 05:32
  • You think right. The first is a pointer to a function (it's a *variable* that is a pointer to a function), the second a function which returns a pointer. – Some programmer dude Aug 25 '16 at 05:33
  • 2
    `&f1` is not a function pointer, just a pointer to an object (the object it points to is the function pointer)... so it can convert to `void *` just fine. `&f2`, on the other hand, is a function pointer... and they're not necessarily compatible with `void *`. – Dmitri Aug 25 '16 at 05:35
  • @iharob Oh, yes, the source file ends with `cpp`. I don't know that matters. I updated my question. – smwikipedia Aug 25 '16 at 05:35
  • I don't think it's allowed to convert (explicitly or implicitly) [from function pointer to void](http://stackoverflow.com/a/5579907/995714) – phuclv Aug 25 '16 at 05:37
  • 1
    C++ is much more strongly typed than C. Types that can be implicitly converted between each other in C might not be that in C++. One prominent example is `void *`: In C it's a generic pointer and all other pointers (except pointers to functions) can be converted to it, or from it. In C++ that's not possible without an explicit cast. – Some programmer dude Aug 25 '16 at 05:37
  • 7
    Every pointer to *data* can be implicitly converted to `void*`. Functions are not data, but pointers to functions are. So the address of f1 is a pointer to data, and the address of f2 is not. – n. m. could be an AI Aug 25 '16 at 05:37
  • @LưuVĩnhPhúc It's not `void`, it's `void *`. All pointers can be converted and this code does compile as it is with *gcc*. Whether it's correct or not, is another thing. – Iharob Al Asimi Aug 25 '16 at 05:37
  • @iharob [tune up your gcc](http://ideone.com/uKATKW). – n. m. could be an AI Aug 25 '16 at 05:44
  • 1
    You are compiling with a C++ compiler. I retagged the question. It is important to know what language you are writing. C and C++ are not the same. – David Heffernan Aug 25 '16 at 06:07

3 Answers3

6

You're right that the first is a pointer to a function and the second is a function declaration.

That means that the compiler is correct about the warnings, too.

The first call (f3(&f1)) passes the address of the function pointer, which is convertible to void * (contrary to a previous comment of mine). So, there's no error required. (It's a pointer to a function pointer, and therefore a data object, not a function pointer. Omit the & and you get the same error as with the second call.)

The second call (f3(&f2)) passes a pointer to a function, and function pointers and void pointers are not inter-convertible. The & in front of the function name is superfluous — and mildly misleading in context. You can add an arbitrary number of *, or a single & (or omit them all) from a function name and it is treated the same — one of the weirder aspects of standard C. (See also Why do function pointer definitions work with any number of ampersands '&' or asterisks '*'?)

I note that I had to use -pedantic to get GCC to complain about it at all. That's a consequence of the standard documenting a common extension in Annex J:

J.5.7 Function pointer casts

¶1 A pointer to an object or to void may be cast to a pointer to a function, allowing data to be invoked as a function (6.5.4).

¶2 A pointer to a function may be cast to a pointer to an object or to void, allowing a function to be inspected or modified (for example, by a debugger) (6.5.4).

You ask what void *(*)() means. It is the 'cast' form of a pointer to a function that takes an indeterminate argument list (but not a variadic one with ellipsis ... at the end) and returns a pointer to a function.


haccks asked:

Could you please add a reference from C standard about function pointers and void pointers are not inter-convertible?

Yes — that's easy:

6.2.5 Types

¶1 … Types are partitioned into object types (types that describe objects) and function types (types that describe functions).

6.3 Conversions

6.3.2.3 Pointers

¶1 A pointer to void may be converted to or from a pointer to any object type. A pointer to any object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.

¶7 A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned68) for the referenced type, the behavior is undefined. Otherwise, when converted back again, the result shall compare equal to the original pointer. When a pointer to an object is converted to a pointer to a character type, the result points to the lowest addressed byte of the object. Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object.

¶8 A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer. If a converted pointer is used to call a function whose type is not compatible with the referenced type, the behavior is undefined.

Those are the only defined conversions for between pointer types. There's nothing there about converting between pointers to objects and pointers to functions, so it isn't allowed (but a diagnostic is not necessarily required; it isn't in a 'constraints' section of the standard).

Tested code

Variant 1 (pf19.c):

void (*f1)(void);
void *f2(void);

void f3(void *p_func);

int main(void)
{
    f3(&f1);
    f3(&f2);
}

Compilation warnings (errors because of -Werror and -pedantic):

$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes  -Wold-style-definition -pedantic -c pf19.c
pf19.c: In function ‘main’:
pf19.c:9:12: error: ISO C forbids passing argument 1 of ‘f3’ between function pointer and ‘void *’ [-Werror=pedantic]
         f3(&f2); //<----------Compiler error
            ^
pf19.c:4:6: note: expected ‘void *’ but argument is of type ‘void * (*)(void)’
 void f3(void *p_func);
      ^~
cc1: all warnings being treated as errors

Variant 2 (also pf19.c):

void (*f1)(void);
void *f2(void);

void f3(void *p_func);

int main(void)
{
    f3(f1);
    f3(&f2);
}

Compilation messages:

$ gcc -O3   -g      -std=c11   -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes  -Wold-style-definition      -pedantic -c pf19.c
pf19.c: In function ‘main’:
pf19.c:8:8: error: ISO C forbids passing argument 1 of ‘f3’ between function pointer and ‘void *’ [-Werror=pedantic]
     f3(f1);
        ^~
pf19.c:4:6: note: expected ‘void *’ but argument is of type ‘void (*)(void)’
 void f3(void *p_func);
      ^~
pf19.c:9:8: error: ISO C forbids passing argument 1 of ‘f3’ between function pointer and ‘void *’ [-Werror=pedantic]
     f3(&f2);
        ^
pf19.c:4:6: note: expected ‘void *’ but argument is of type ‘void * (*)(void)’
 void f3(void *p_func);
      ^~
cc1: all warnings being treated as errors
$

The wording of the message is different from a C compiler compared with a C++ compiler, but the intent is the same (pf17.cc is a simple copy of pf19.c):

$ g++ -O3 -g -I./inc -std=c++11 -Wall -Wextra -Werror    -c pf17.cc
pf17.cc: In function ‘int main()’:
pf17.cc:8:10: error: invalid conversion from ‘void (*)()’ to ‘void*’ [-fpermissive]
     f3(f1);
          ^
pf17.cc:4:6: note:   initializing argument 1 of ‘void f3(void*)’
 void f3(void *p_func);
      ^~
pf17.cc:9:8: error: invalid conversion from ‘void* (*)()’ to ‘void*’ [-fpermissive]
     f3(&f2);
        ^~~
pf17.cc:4:6: note:   initializing argument 1 of ‘void f3(void*)’
 void f3(void *p_func);
      ^~
$

Testing: GCC 6.2.0 on Mac OS X 10.11.6 El Capitan.


Thanks to Dmitri for noting that §6.3.2.3 ¶1 was relevant, and Annex J.5.7.

Community
  • 1
  • 1
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Could you please add a reference from C standard about function pointers and `void` pointers are not inter-convertible? – haccks Aug 25 '16 at 05:43
  • Thanks for references. – haccks Aug 25 '16 at 05:58
  • In 6.3.2.3 par. 1 seems relevant as well: *"A pointer to void may be converted to or from a pointer to any object type. A pointer to any object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer."* And 3.15 defines "object" as a *"region of data storage in the execution environment, the contents of which can represent values"*... which suggests functions aren't considered objects. – Dmitri Aug 25 '16 at 05:59
  • @Dmitri: functions are definitely different from objects within the terms of the standard — hence the separate paragraphs about 'pointer to an object type' and about 'pointer to a function'. §6.2.5 says _Types are partitioned into object types (types that describe objects) and function types (types that describe functions)._ – Jonathan Leffler Aug 25 '16 at 06:03
  • Just to be clear, I agree with your answer... I just thought those other parts of the standard also help support it. – Dmitri Aug 25 '16 at 06:07
2

The error message doesn't make sense if this is a program. Because in every pointer is convertible to void * and back without the need for a cast. Your compiler seems to think that there is no way to convert from a pointer to void * which is how works.

Although comments on your question are correct too, your code should compile in both cases provided it's compiled as code. The standard strictly forbids this kind of conversion but the error message still is the c++ error not the c compiler error, and also the conversion from/to void */function pointers is a common extension

J.5.7 Function pointer casts

  1. A pointer to an object or to void may be cast to a pointer to a function, allowing data to be invoked as a function (6.5.4).
  2. A pointer to a function may be cast to a pointer to an object or to void, allowing a function to be inspected or modified (for example, by a debugger) (6.5.4).

though it's not guaranteed that all systems support this, most systems do.

Possible reasons to be compiliing as are

  1. You called the c++ compiler directly, for example g++
  2. You named your source file with .cpp extension instead of .c

If it is source code, the error makes sense because does not allow such conversions but in c this might compile correctly (and will most of the time) even though it's strictly forbidden.

Also note that function pointers in are not the usual way to do things AFAIK. Because when you have an object, you can only create pointers to static members, and that's not too different than having a pointer to a normal function. In callbacks are not so common, and in fact a library that I've used extensively Qt needed (before the c++11 standard) a tool called moc in order to make "callbacks" work. The tool generated code from header files to allow callbacks or (a simulation of them) to work in .

Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97
  • 3
    C only guarantees that pointers to *objects* are convertible to/from `void *` -- that doesn't include function pointers... though the ability to cast between function pointers and object pointers is noted as a common extension. – Dmitri Aug 25 '16 at 05:48
  • Thanks for J.5.7 reference. – haccks Aug 25 '16 at 06:11
0
  • &f1 should be the address of a function pointer, why is it can be passed to f3 as void *?

That is correct, and that is exactly why it succeeds. Because a pointer-to-a-pointer can be implicitly converted to void *, irrespective of what data type its target points to.

  • Why &f2 is void *(*)()? And what does void *(*)() even mean?

You may be misunderstanding function pointer notation. void *(*)() is the notation for a function pointer. void *f2(void) is a function that returns a void pointer. &f2 is a function pointer (to that function). The compiler is telling you that it can't implicitly convert a function pointer to void * (per the C standard, this is not guaranteed, and your compiler apparently isn't capable of doing it).

That's the scoop.

Larry McQueary
  • 416
  • 3
  • 9