Почему этот код компилируется?

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

::TerminateThread(::TerminateThread, 0);

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

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

BOOL WINAPI TerminateThread(HANDLE hThread, DWORD dwExitCode);

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

Есть объяснения?

7
задан Lior Kogan 27 August 2010 в 06:38
поделиться

4 ответа

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

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

HeapAlloc (GetProcessHeap, 0, size); // oops, should be GetProcessHeap()
7
ответ дан 7 December 2019 в 01:13
поделиться

Принимает адрес функции ::TerminateThread. Это тип

BOOL WINAPI (*)(HANDLE, DWORD).

HANDLE определяется как

typedef PVOID HANDLE;

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

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

EDIT 2:

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

void f(){}

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

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

2
ответ дан 7 December 2019 в 01:13
поделиться

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

0
ответ дан 7 December 2019 в 01:13
поделиться

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

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

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

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

1
ответ дан 7 December 2019 в 01:13
поделиться
Другие вопросы по тегам:

Похожие вопросы: