2

The behavior of my code is different in c and c++.

void *(*funcPtr)() = dlsym(some symbol..) ; // (1) works in c but not c++

int (*funcPtr)();
*(void**)(&funcPtr) = dlsym(some symbol..) ; // (2) works in c++

I dont understand how the 2nd casting works in c++ while the 1st casting doesnt work in c++. The error message for (1) show is invalid conversion from void* to void*() in c++.

9
  • 3
    Because C and C++ are different languages, maybe? Commented Apr 5, 2019 at 6:40
  • sorry for the second one, i forget to include the initialisation of funcPtr - int (*funcPtr)(); Commented Apr 5, 2019 at 6:45
  • 1
    C is not C++. There are many things you can do in C that are compiler errors in C++. Commented Apr 5, 2019 at 6:50
  • @Lundin There is a cast of the variable to "pointer to pointer to void" in the C++ snippet. Commented Apr 5, 2019 at 7:05
  • Sorry about that @LorinczyZsigmond Commented Apr 5, 2019 at 7:08

2 Answers 2

4

The problem is that dlsym returns a void* pointer. While in C, any such pointer is implicitly convertible into any other (object!) pointer type (for comparison: casting the result of malloc), this is not the case in C++ (here you need to cast the result of malloc).

For function pointers, though, this cast is not implicit even in C. Apparently, as your code compiles, your compiler added this implicit cast for function pointers, too, as a compiler extension (in consistency with object pointers); however, for being fully standard compliant, it actually should issue a diagnostic, e. g. a compiler warning, mandated by C17 6.5.4.3:

Conversions that involve pointers, other than where permitted by the constraints of 6.5.16.1, shall be specified by means of an explicit cast.

But now instead of casting the target pointer, you rather should cast the result of dlsym into the appropriate function pointer:

int (*funcPtr)() = reinterpret_cast<int(*)()>(dlsym(some symbol..));

or simpler:

auto funcPtr = reinterpret_cast<int(*)()>(dlsym(some symbol..));

or even:

int (*funcPtr)() = reinterpret_cast<decltype(funcPtr)>(dlsym(some symbol..));

(Last one especially interesting if funcPtr has been declared previously.)

Additionally, you should prefer C++ over C style casts, see my variants above. You get more precise control over what type of cast actually occurs.

Side note: Have you noticed that the two function pointers declared in your question differ in return type (void* vs. int)? Additionally, a function not accepting any arguments in C needs to be declared as void f(void); accordingly, function pointers: void*(*f)(void). C++ allows usage of void for compatibility, while skipping void in C has totally different meaning: The function could accept anything, you need to know from elsewhere (documentation) how many arguments of which type actually can be passed to.

Sign up to request clarification or add additional context in comments.

18 Comments

I noticed that they are different. I didn't realise that C++ doesn't implicitly convert void* into any other pointer type. So i reckon i didnt handle that correctly in C.
This is not correct. You cannot implicitly convert between a function pointer and void* in C. Implicit conversions only happens for pointers to object type.
Well, you need to clarify that the first example does not compile in C, contrary to what the OP claims.
@Lundin GCC 8.1 (Linux) accepted nicely void(*f)(void) = malloc(sizeof(void*)); f(); without even issuing a warning...
@Aconcagua Yes, it is a non-comforming compiler when using default settings. See my answer. A conforming compiler must give a diagnostic message for this code.
|
3

Strictly speaking, none of this code is valid in either language.


void *(*funcPtr)() = dlsym(some symbol..) ;

dlsym returns type void* so this is not valid in either language. The C standard only allows for implicit conversions between void* and pointers to object type, see C17 6.3.2.3:

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.

Specifically, your code is a language violation of one of the rules of simple assignment, C17 6.5.16.1, emphasis mine:

  • the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) one operand is a pointer to an object type, and the other is a pointer to a qualified or unqualified version of void, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;

The reason why it might compile on certain compilers is overly lax compiler settings. If you are for example using gcc, you need to compile with gcc -std=c17 -pedantic-errors to get a language compliant compiler, otherwise it defaults to the non-standard "gnu11" language.


int (*funcPtr)();
*(void**)(&funcPtr) = dlsym(some symbol..) ; // (2) works in c++

Here you explicitly force the function pointer to become type void**. The cast is fine syntax-wise, but this may or may not be a valid pointer conversion on the specific compiler. Again, conversions between object pointers and function pointers are not supported by the C or C++ standard, but you are relying on non-standard extensions. Formally this code invokes undefined behavior in both C and C++.

In practice, lots of compilers have well-defined behavior here, because most systems have the same representation of object pointers and function pointers.

Given that the conversion is valid, you can of course de-reference a void** to get a void* and then assign another void* to it.

2 Comments

I'm pretty sure it's not Undefined Behavior in C++17. I recall the discussion in WG21 around 2002 or so, where dlsym was the explicit reason to add conditionally-supported. Importantly, implementations who do not support it must give a diagnostic.
@MSalters Regardless, you cannot implicitly convert from a void* to a function pointer. It should have been written as funcPtr = (int(*)()) dlsym(.... Pretending that a function pointer in the application is a void pointer, then access it as such, is always wrong.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.