Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/blackberry/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 这段代码为什么要编译?_C++_Visual Studio 2008_Winapi_Compilation - Fatal编程技术网

C++ 这段代码为什么要编译?

C++ 这段代码为什么要编译?,c++,visual-studio-2008,winapi,compilation,C++,Visual Studio 2008,Winapi,Compilation,昨晚,我太累了,写了一句奇怪的话: ::TerminateThread(::TerminateThread, 0); 令我惊讶的是,编译器没有抱怨(它甚至运行…) 因为TerminateThread()定义为 BOOL WINAPI TerminateThread(HANDLE hThread, DWORD dwExitCode); 我不知道为什么我能编译它 有什么解释吗?HANDLE是指向void的指针,Microsoft的编译器允许隐式地将函数指针转换为指向void的指针 这让我多次犯错

昨晚,我太累了,写了一句奇怪的话:

::TerminateThread(::TerminateThread, 0);
令我惊讶的是,编译器没有抱怨(它甚至运行…)

因为TerminateThread()定义为

BOOL WINAPI TerminateThread(HANDLE hThread, DWORD dwExitCode);
我不知道为什么我能编译它


有什么解释吗?

HANDLE
是指向void的指针,Microsoft的编译器允许隐式地将函数指针转换为指向void的指针

这让我多次犯错,尤其是在堆函数方面:

HeapAlloc (GetProcessHeap, 0, size); // oops, should be GetProcessHeap()

我猜
HANDLE
被定义为
void*
,因此任何指针(甚至函数指针)都可以隐式地强制转换为
HANDLE

,它接受函数的地址
::TerminateThread
。这是一种

BOOL WINAPI (*)(HANDLE, DWORD).
句柄定义为

BOOL WINAPI TerminateThread(HANDLE hThread, DWORD dwExitCode);
typedef PVOID手柄

<> >,编译器编写代码将“函数指针”类型转换为pVAL,在C++(4.10美元/ 2)

中完全有效。 “指向cv T的指针”类型的右值 其中T是对象类型,可以是 已转换为类型为的右值 “指向cv void的指针。”的结果 将“指向cv T的指针”转换为 “指向cv void的指针”指向 存储位置的开始,其中 类型为T的对象驻留,就好像 该对象是最派生的对象 (1.8)T型(即不是底座 类子对象)

编辑2:

@dreamlax是正确的。似乎C++03标准不允许将函数指针转换为void*,如下面的程序所示

void f(){}

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

我想知道为什么。

真正的答案是:你很幸运。有三种代码可以供C或C++编译器使用:

  • 正确的代码,其行为由标准或编译器定义
  • 完全错误的代码将被错误拒绝
  • 不正确到不能定义行为,但错误到不能拒绝的代码

这最后一个类别是“未定义行为”,是C和C++最差的事情之一。编译器将接受代码,但很可能不会执行您想要的操作。将函数指针转换为空指针不是您想要的,而且您可能希望编译器警告您犯了错误


下面列出了您可以做的最好的事情,即尝试提高编译器的警告级别。在VisualC++中使用<代码> /W3 或<代码> /W4 < /代码>将警告级别提高,并且在编译时会捕获更多未定义的行为。在gcc中,使用
-ansi-pedantic-W-Wall
将警告级别调到最大。

对于编译器来说,这不是一件危险的事情吗?@Jookia:标准允许这样做。编译器只是遵循标准。@chubsdad:在C中,将函数指针转换为指向void的指针是未定义的,2003年发布了一份技术勘误表,以更正POSIX规范中
dlsym
示例代码中的一个错误,因为它正是这样做的。有人解释说,函数指针可能不适合于空类型指针(例如,在分段式寻址系统),并且我也认为C++中存在这种限制(但我不知道,因为我对C++没有那么多知识)。对C和C++的实际修正是使转换不明确,不需要诊断。过去,在使用dlsym/GetProcAddress时,必须进行诊断(错误/警告)。但你是对的,它可能不适合,编译器仍然完全可以拒绝此类强制转换。函数不是对象类型,是吗?@dreamlax:是的,函数不是对象。你提供的摘录并不保证将函数指针转换为指向void的指针是安全的。§3.9(9)规定“对象类型是一种(可能是cv限定的)类型,它不是函数类型,不是引用类型,也不是无效类型。”9@dreamlax:你说得对,伙计。我没有意识到这一点。我想知道有没有更博学的人来帮我们解决这个问题在加入
之前,你有没有一个
#定义严格的
?它会将一大类句柄错误转化为编译时错误。@MSalters“感谢您的宝贵提示。但是,在这种情况下,它不会有帮助。我没有投反对票,但您错了,因为函数指针不能保证是可转换的(隐式或显式的)to
void*
void*
指针只能保证指向对象,而不能指向函数(这里我使用的对象是C标准中使用的“内存中的单个数据块”,而不是OO编程中使用的“数据和方法的收集”)@菲利普:我从来没有说过标准能保证这一点。这不是问题。据我所知,问题是为什么他的特定编译器(我猜是VC++)接受这一行代码。这是因为函数指针被隐式转换为
void*
,并且
HANDLE
被定义为
void*
,编译器没有为导致未定义行为的代码生成更多诊断,这是一个遗憾,比如:“嘿,我不知道这段代码应该做什么,你需要复习一下!".