【问题标题】:Conversion error in CreateThreadCreateThread 中的转换错误
【发布时间】:2023-03-21 17:45:01
【问题描述】:

当我尝试使用CreateThread 函数时,我遇到了一个奇怪的错误。这是我的代码:

HANDLE threads[3];  //threads[0] is printer, threads[1] is receiver, [2] is serverconn
    DWORD  printer_id,  receiver_id, serverconn_id;
    if(
                      ((threads [0] = CreateThread(NULL, 0, printer_thread,    (LPVOID) thread_args, 0, &printer_id)) == NULL) ||
        ((recv_thread = threads [1] = CreateThread(NULL, 0, receiver_thread,   (LPVOID) thread_args, 0, &receiver_id)) == NULL) ||
                      ((threads [2] = CreateThread(NULL, 0, serverconn_thread, (LPVOID) thread_args, 0, &serverconn_id)) == NULL)
    )
    {
        IO_print_line("Initialization error");
        return FALSE;
    }

printer_threadreceiver_threadserverconn_thread 是这样定义的函数:

int serverconn_thread(LPVOID args);

Visual Studio 编译器给了我这个错误:

Error   C2440   'function': cannot convert from 'int (__cdecl *)(LPVOID)' to 'LPTHREAD_START_ROUTINE'

我真的不明白,因为我认为我已经完全做到了what the official documentation suggests

不,将线程函数的返回类型更改为DWORD 并不能解决任何问题,错误只会更改为:

Error   C2440   'function': cannot convert from 'DWORD (__cdecl *)(LPVOID)' to 'LPTHREAD_START_ROUTINE'

奇怪的是,如果我这样改变它:

if(
                      ((threads [0] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) &printer_thread,    (LPVOID) thread_args, 0, &printer_id)) == NULL) ||
        ((recv_thread = threads [1] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) &receiver_thread,   (LPVOID) thread_args, 0, &receiver_id)) == NULL) ||
                      ((threads [2] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) &serverconn_thread, (LPVOID) thread_args, 0, &serverconn_id)) == NULL)
    )

使用强制转换,编译器是可以的,但我认为以这种方式强制它不是一个好主意。我该如何解决这个问题?

【问题讨论】:

  • 那微软官方的例子为什么要配DWORD WINAPI MyThreadFunction( LPVOID lpParam )
  • @EärendilBaggins WINAPI 只是 __stdcall 的宏
  • WINAPI 宏扩展为__stdcall。你也可以在这里使用CALLBACK 宏。这一切都一样
  • 不推荐使用 WINAPI 吗?当我使用HANDLE WINAPI 时,我收到了来自编译器的警告
  • 因为 __stdcall 是调用约定。仅对功能有意义。当你声明HANDLE WINAPI h; - 这真的是不合时宜

标签: c visual-studio winapi


【解决方案1】:

在声明线程函数时,您没有遵循official documentation

您的原始声明使用int 作为返回值,并使用__cdecl(隐式)作为调用约定。当您将返回值更改为DWORD 时,您并没有更改调用约定。

但是,documentation 清楚地显示了正确的声明:

DWORD WINAPI ThreadProc( _In_ LPVOID lpParameter );

DWORDunsigned longWINAPI 是映射到 __stdcall 调用约定的宏(请参阅 Windows Data Types)。

所以,你的函数的正确声明应该是:

unsigned long __stdcall printer_thread(void *args);
unsigned long __stdcall receiver_thread(void *args);
unsigned long __stdcall serverconn_thread(void *args);

但是,由于CreateThread() 接受LPTHREAD_START_ROUTINE 作为输入,它被声明为DWORD (WINAPI*)(LPVOID)(请参阅winbase.h 中的实际声明),您应该声明您的函数以匹配:

DWORD WINAPI printer_thread(LPVOID args);
DWORD WINAPI receiver_thread(LPVOID args);
DWORD WINAPI serverconn_thread(LPVOID args);

【讨论】:

  • DWORD 真的有必要吗?因为int __stdcall 对编译器来说很好,我想知道它是否真的有什么不同,因为我并没有真正使用线程函数的返回值。
  • @EärendilBaggins:是的,DWORD 真的很有必要。 intunsigned long 是不同的数据类型。如果您使用int,则必须使用类型转换将您的函数传递给CreateThread,并且您根本不应该使用类型转换。如果你不得不诉诸类型转换来取悦编译器,那你就做错了。
  • 猜猜我将其更改为 DWORD。不想强制转换为更小的类型。
  • @EärendilBaggins:除非您的编译器在 Windows 上将 long 实现为 64 位(大多数使用 32 位,但有些使用 64 位),否则 DWORDint 通常大小相同,但不是相同的符号。 C 和 C++ 是强类型语言。你必须使用正确的类型。
  • C 和 C++ 都不是强类型编程语言。不过,它们是静态类型的,因此编译器可以针对不兼容的类型发出诊断。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-08-20
  • 2012-05-07
  • 2016-10-01
  • 2011-12-06
  • 2014-02-23
  • 2016-11-23
  • 1970-01-01
相关资源
最近更新 更多