Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does this code compile?

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?

like image 591
Lior Kogan Avatar asked Aug 27 '10 06:08

Lior Kogan


3 Answers

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()
like image 134
dreamlax Avatar answered Oct 14 '22 11:10

dreamlax


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.

like image 40
Chubsdad Avatar answered Oct 14 '22 10:10

Chubsdad


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.

like image 30
Philip Potter Avatar answered Oct 14 '22 09:10

Philip Potter