Почему этот код компилируется? - PullRequest
7 голосов
/ 27 августа 2010

Прошлой ночью, будучи слишком уставшим, я написал эту странную строку:

::TerminateThread(::TerminateThread, 0);

К моему удивлению, компилятор не жалуется (он даже запускается ...)

Поскольку TerminateThread () определяется как

BOOL WINAPI TerminateThread(HANDLE hThread, DWORD dwExitCode);

Я не уверен, почему я могу его скомпилировать.

Любое объяснение?

Ответы [ 4 ]

7 голосов
/ 27 августа 2010

HANDLE - это указатель на void, и компилятор Microsoft позволяет неявно преобразовывать указатель на функцию-указатель на void.

Это приводило меня в замешательство много раз, особенно с функциями кучи:

HeapAlloc (GetProcessHeap, 0, size); // oops, should be GetProcessHeap()
2 голосов
/ 27 августа 2010

Требуется адрес функции ::TerminateThread. Это типа

BOOL WINAPI (*)(HANDLE, DWORD).

РУЧКА определяется как

typedef PVOID HANDLE;

Итак, компилятор написал код для преобразования типа «указатель на функцию» в PVOID, что совершенно правильно в C ++ ($ 4.10 / 2)

«Значение типа указатель на cv T» где T - тип объекта, может быть преобразован в значение типа «Указатель на cv void.» Результат преобразование «указатель на cv T» в «Указатель на cv void» указывает на начало места хранения, где объект типа T находится, как если бы объект является наиболее производным объектом (1.8) типа T (то есть не основание подобъект класса). "

РЕДАКТИРОВАТЬ 2:

@ Dreamlax правильный. Похоже, что стандарт C ++ 03 не позволяет преобразовывать указатель функции в пустоту *, как показано в программе ниже

void f(){}

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

Интересно, почему.

1 голос
/ 27 августа 2010

Реальный ответ: вам повезло.Существует три типа кода, которые вы можете передать в компилятор C или C ++:

  • Код, который является правильным и поведение которого определяется Стандартом или компилятором
  • Код, который полностьюнеправильный, который будет отклонен с ошибкой
  • Код, который недостаточно точен, чтобы иметь определенное поведение, но не настолько неверен, чтобы быть отклоненным.

Эта последняя категория "Неопределенное поведение"и это одна из худших вещей в C и C ++.Компилятор примет код, но, скорее всего, он не будет делать то, что вы хотели.Преобразование указателя на функцию в пустой указатель - не то, что вам нужно, и вы, вероятно, хотите, чтобы предупреждение компилятора сообщало вам, что вы допустили ошибку.

Вот список возможных неопределенных действий в C ++. Лучшее, что вы можете сделать, это попытаться повысить уровень предупреждения вашего компилятора.В Visual C ++ используйте /w3 или /w4, чтобы повысить уровень предупреждения, и во время компиляции он обнаружит более неопределенное поведение.В gcc используйте -ansi -pedantic -W -Wall, чтобы увеличить уровень предупреждения до полного.

0 голосов
/ 27 августа 2010

Полагаю, HANDLE определяется как void*, поэтому любой указатель (даже указатель на функцию) может быть неявно приведен к HANDLE

...