为什么一个函数接受一个空指针?

Why does a function accept a void pointer?

我正在尝试了解 pthread。

但是需要创建线程方法如下:

void *SomeMethod(void* x)
{
    //Do Something
}

为什么需要创建一个接受 void 指针的函数?我们不能将 pthread 与这样的函数一起使用吗?

void SomeMethod()
{
}

因为 pthread_create 函数接受类型为 void* (*)(void*) 的参数,这是一个接受 void* 并返回 void* 的函数,所以要使用 pthread_create 这就是你需要使用的。

pthread_create API 允许您将数据传递到新线程并再次取回数据。如果你不想传入任何东西,你仍然必须满足该接口,但只需将其传递为 NULL。

仅仅因为 现在不想向你的新线程传递任何参数并不意味着 API 应该被设计成只支持你当前的线程用例。根据接受 void*(可以选择传递 NULL)的函数编写 API 比根据不接受参数且要求的函数编写 API 要好得多用户想出自己的解决方案来将数据传递到新线程。

在 C++ 中,您可以为新线程使用任何类型的函数,并向其传递您需要的任何参数:

std::thread t(&SomeMethod);

因为他们只想编写一个用于创建线程的函数 - 获取和 return 数据。

否则他们至少要写出四种可能性。

你可以忽略输入参数和 return 值。

只是简单的问题

根据pthread_create()man page,我们可以看到,函数的签名是

 int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
               void *(*start_routine) (void *), void *arg);

其中第三个参数的类型为

void *(*start_routine) (void *)

这意味着,它需要一个指向 return 类型 void * 并接受 void * 参数的函数的指针。所以,我们需要相应地定义线程函数。

也就是说,关于 void 指针用于参数传递的用法,引用 C11,第 §6.3.2.3

A pointer to void may be converted to or from a pointer to any object type. A pointer to any object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.

我们可以看到,void指针用来传递any类型的数据给线程函数,前提是,它被转换回实际类型在函数内部。

另外,FWIW,不要试图偏离函数指针所需的签名,因为标准明确要求

[....] If a converted pointer is used to call a function whose type is not compatible with the referenced type, the behavior is undefined. In case you don't want (need) to pass any valid argument value, you can always pass NULL.

因为很多时候,我们想给线程一些工作(或与,或关闭,或其他)。一个很典型的例子就是传入一个class的实例,这样就可以调用class的成员函数

但它可能是其他各种东西 - 结构或指向一些简单数据的指针。

当然,使用 std::thread 无论如何都会隐藏大部分此类内容,您无需担心。我强烈建议一般使用 std::thread 而不是 pthread

void *SomeMethod(void* x) 用作新线程的入口点。 如果您想向新线程传递一些数据(结构、缓冲区、常规整数等),您应该如何操作?专门为此 入口点 方法接收 void* 参数,它可以指向任何你想要的东西。然后在函数体内你可以将它转换回正确的类型并由工作线程使用它。

如果您的线程不需要任何其他数据,您可以将 NULLnullptr(对于 C++11)传递给它。

不,您建议的方法签名不能用作 pthreads 线程的启动函数。如果您将指向此类函数的指针传递给 pthread_create(),那么编译器应该发出警告。如果您 运行 结果程序,那么您将调用未定义的行为,因为指针没有正确的类型。实际上,pthreads 库会尝试将参数传递给 start 函数,并期望从中得到一个 return 值;如果您提供一个指向函数的指针,该函数既不使用参数也不使用 return 值(都是适当的类型),那么程序很可能会崩溃。

至于为什么 start 函数需要具有它所具有的签名,void * 参数可用于传达任何类型的实际参数,包括包含多个不同值的复合类型,类似地,void * return 类型可用于传达任何类型的 return 值。这几乎是最通用的函数类型。