在不同编译器上转换为 void**

Cap*_*n42 9 c c++ cuda

我一直在通过不同的编译器运行以下代码:

int main()
{
    float **a;
    void **b;
    b = a;
}
Run Code Online (Sandbox Code Playgroud)

从我已经能够收集,void **不是一个普通的指针,这意味着从另一个指针任何转换不应该编译或至少抛出一个警告。但是,这是我的结果(全部在 Windows 上完成):

  • gcc - 按预期抛出警告。
  • g++ - 正如预期的那样抛出一个错误(这是因为 C++ 的类型不太宽松,对吧?)
  • MSVC (cl.exe) - 即使指定了 /Wall,也不会发出任何警告。

我的问题是:我是否遗漏了整件事,MSVC 不产生警告有什么具体原因吗?MSVC转换时产生警告 void **float **

另一件值得注意的事情:如果我a = b用显式转换替换a = (void **)b,则编译器都不会发出警告。我认为这应该是一个无效的演员,那么为什么没有任何警告呢?

我问这个问题的原因是因为我开始学习 CUDA 和官方编程指南(https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#device-memory)可以找到以下代码:

// Allocate vectors in device memory
float* d_A;
cudaMalloc(&d_A, size);
Run Code Online (Sandbox Code Playgroud)

它应该执行到void **for的隐式转换&d_A,因为的第一个参数cudaMalloc是类型void **。类似的代码可以在整个文档中找到。这只是 NVIDIA 的草率工作还是我再次遗漏了什么?由于nvcc使用 MSVC,代码编译时没有警告。

dbu*_*ush 4

我是否遗漏了整个事情的某些内容?MSVC 没有发出警告有什么具体原因吗?从 void ** 转换为 float ** 时,MSVC 确实会产生警告

这种没有强制转换的赋值是一种约束违规,因此符合标准的编译器将打印警告或错误。然而,MSVC 并不是完全兼容的 C 实现。

另一件值得注意的事情是:如果我用显式转换 a = (void **)b 替换 a = b,则所有编译器都不会抛出警告。我认为这应该是一个无效的转换,那么为什么没有任何警告呢?

在某些情况下,允许通过强制转换进行指针转换。C 标准在第 6.3.2.3p7 节中有如下规定:

指向对象类型的指针可以转换为指向不同对象类型的指针。如果生成的指针未针对引用类型正确对齐,则行为未定义。否则,当再次转换回来时,结果应等于原始指针。当指向对象的指针转换为指向字符类型的指针时,结果指向该对象的最低寻址字节。结果的连续增量,直到对象的大小,产生指向对象的剩余字节的指针。

因此,只要不存在对齐问题,您就可以在指针类型之间进行转换,并且只需转换回来(除非目标是 a char *)。

float* d_A;
cudaMalloc(&d_A, size);
Run Code Online (Sandbox Code Playgroud)

...

这只是 NVIDIA 的草率工作还是我又错过了什么?

据推测,该函数正在取消引用给定的指针并写入某些已分配内存的地址。这意味着它试图float *像写入 a 一样写入 a void *。这与典型的void *. 严格来说,这看起来像是未定义的行为,尽管它“有效”,因为现代 x86 处理器(不在实模式下)对所有指针类型使用相同的表示形式。

  • 请注意如何解释它,如果使用 C++ 编译器编译 CUDA,则两者之间可能存在模板技巧 (3认同)