像(void **)和device_array这样的转换有什么问题?

关于使用cudaMalloc((void**)&device_array, num_bytes)另一个问题有这个答案 ,它使用void**作为输出参数,而不是像标准malloc那样传递void*作为返回值。

它批评了NVIDIA的API并声明:

如(void **)和device_array中的转换是无效的C并导致未定义的行为。

并且已被多次投票(截至目前为8),因此我认为其中有一些道理。

我不明白在那里投掷有什么问题。

  • 什么是无效的C?
  • 在什么情况下会导致未定义的行为?

我所知道的是,它在没有警告的情况下进行编译,并以我的预期行为运行。 但是我不了解C达到标准规格水平。

问题是void*在C中具有特殊含义,具有特殊规则(1)。 它是唯一可以安全地转换任何其他指针类型的指针类型。 但是,这些特殊规则不会递归地应用于void**

意思是像int* ptr = malloc(x);这样的代码int* ptr = malloc(x); 很好,但是

 int* ptr; cudaMalloc(&ptr, x); // bad 

不行! 从int**void**指针转换没有明确定义。 从理论上讲,这可能会导致不确定的行为和错位(2)。

此外,指针别名也可能存在问题。 编译器可以自由地假设void**的内容永远不会通过int**访问,因此可能以意想不到的方式优化代码,从而导致违反严格别名规则(6.5)的未定义行为。

这意味着您必须编写这样的代码才能安全地使用该函数:

 void* vptr; int* iptr; cudaMalloc(&vptr, x); iptr = vptr; 

(1)C11 6.3.2.3/1:

指向void的指针可以转换为指向任何对象类型的指针。 指向任何对象类型的指针可以转换为指向void的指针,然后再返回; 结果应该等于原始指针。

(2)C11 6.3.2.3/7:

指向对象类型的指针可以转换为指向不同对象类型的指针。 如果生成的指针未针对引用的类型正确对齐,则行为未定义。