views:

177

answers:

4

Last night, being too tired, I wrote this strange line:

::TerminateThread(::TerminateThread, 0);

To my surprise, the compiler does not complain (It even run...)

Since TerminateThread() is defined as

BOOL WINAPI TerminateThread(HANDLE hThread, DWORD dwExitCode);

I'm no sure why I am able to compile it.

Any explanation?

+7  A: 

HANDLE is a pointer to void, and Microsoft's compiler allows implicitly converting a function pointer to a pointer to void.

This tripped me up many times, especially with the heap functions:

HeapAlloc (GetProcessHeap, 0, size); // oops, should be GetProcessHeap()
dreamlax
Isn't that a dangerous thing for a compiler to do?
Jookia
@Jookia:The standard allows it. Compiler just follows the Standard.
Chubsdad
@chubsdad: Converting a function pointer to a pointer to void in C is undefined, and in 2003 a technical corrigendum was published to correct an error in the example code for `dlsym` in the POSIX specification because it did just that. It was explained that a function pointer might not fit inside a pointer to void type (for example, on systems with segmented addressing), and I imagine that this limitation exists in C++ too (but I wouldn't know because I'm not that knowledgable on C++).
dreamlax
@dreamlax: The actual correction to C and C++ is to _make_ the cast undefined, no diagnostic required. It used to be that a diagnostic (error/warning) was mandatory on such use of dlsym/GetProcAddress. But you're right, it might not fit, and a compiler is still perfectly _allowed_ to reject such casts.
MSalters
A: 

I guess HANDLE is defined as void*, so any pointer (even a function pointer) can be casted implicitly to HANDLE

nikie
Is there a specific reason why this was downvoted?
nikie
I didn't downvote, but you are wrong because function pointers are not guaranteed to be convertible (implicitly or explicitly) to `void *`. `void *` pointers are only guaranteed to be able to point to objects, not functions. (Here I use object in the sense of "singular piece of data in memory" as used in the C Standard, not "collection of data and methods" as used in OO programming.)
Philip Potter
@Philip: I never said the standard guaranteed that. That wasn't the question. The question, as I understand it, was why his specific compiler (I'm guessing VC++) accepts this line of code. And that's because the function pointer is implicitly casted to `void*`, and because `HANDLE` is defined as `void*`
nikie
+1  A: 

It takes the address of the function ::TerminateThread. This is of type

BOOL WINAPI (*)(HANDLE, DWORD).

HANDLE is defined as

typedef PVOID HANDLE;

So, the compiler wrote code to convert the 'function pointer' type to PVOID which is perfectly valid in C++ ($4.10/2)

"An rvalue of type “pointer to cv T,” where T is an object type, can be converted to an rvalue of type “pointer to cv void.” The result of converting a “pointer to cv T” to a “pointer to cv void” points to the start of the storage location where the object of type T resides, as if the object is a most derived object (1.8) of type T (that is, not a base class subobject)."

EDIT 2:

@dreamlax is correct. It appears that C++03 standard does not allow converting a function pointer to a void * as the program below shows

void f(){}

int main(){
   void (*p)(void) = f;
   void *p1 = p;
}

I wonder why.

Chubsdad
A function isn't an object-type though, is it?
dreamlax
@dreamlax: yes, a function is not an object.
Chubsdad
The excerpt you provided does not guarantee that it is safe to convert a function pointer to a pointer to void. §3.9 (9) says "An object type is a (possibly cv-qualified) type that is not a function type, not a reference type, and not a void type."9
dreamlax
@dreamlax: You are right buddy. I was not aware of this. I wonder someone more knowledgable bails us out on this
Chubsdad
+1  A: 

The real answer is: you got lucky. There are three types of code you can feed to a C or C++ compiler:

  • Code which is correct and whose behaviour is defined by the Standard or a compiler
  • Code which is totally wrong which will be rejected with an error
  • Code which is not correct enough to have defined behaviour, but not wrong enough to be rejected.

This last category is "Undefined behaviour" and is one of the worst things about C and C++. The compiler will accept the code, but it most likely won't do what you wanted. Converting a function pointer to a void pointer is not what you wanted, and you probably want a compiler warning telling you that you made a mistake.

Here's a list of possible undefined behaviour in C++. The best thing you can do is try to turn up the warning level of your compiler. In Visual C++ use /w3 or /w4 to turn the warning level up, and it will catch more undefined behaviour at compile time. In gcc, use -ansi -pedantic -W -Wall to turn up your warning level to full.

Philip Potter
It is a shame that compilers do not generate more diagnostics for code that causes undefined behavior, like: "Hey, I don't know what this code is supposed to be doing, you need to review it!".
Remy Lebeau - TeamB