我一直在通过不同的编译器运行以下代码:
int main()
{
float **a;
void **b;
b = a;
}
Run Code Online (Sandbox Code Playgroud)
从我已经能够收集,void **
是不是一个普通的指针,这意味着从另一个指针任何转换不应该编译或至少抛出一个警告。但是,这是我的结果(全部在 Windows 上完成):
我的问题是:我是否遗漏了整件事,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,代码编译时没有警告。
我是否遗漏了整个事情的某些内容?MSVC 没有发出警告有什么具体原因吗?从 void ** 转换为 float ** 时,MSVC 确实会产生警告
这种没有强制转换的赋值是一种约束违规,因此符合标准的编译器将打印警告或错误。然而,MSVC 并不是完全兼容的 C 实现。
另一件值得注意的事情是:如果我用显式转换 a = (void **)b 替换 a = b,则所有编译器都不会抛出警告。我认为这应该是一个无效的转换,那么为什么没有任何警告呢?
在某些情况下,允许通过强制转换进行指针转换。C 标准在第 6.3.2.3p7 节中有如下规定:
指向对象类型的指针可以转换为指向不同对象类型的指针。如果生成的指针未针对引用类型正确对齐,则行为未定义。否则,当再次转换回来时,结果应等于原始指针。当指向对象的指针转换为指向字符类型的指针时,结果指向该对象的最低寻址字节。结果的连续增量,直到对象的大小,产生指向对象的剩余字节的指针。
因此,只要不存在对齐问题,您就可以在指针类型之间进行转换,并且只需转换回来(除非目标是 a char *
)。
Run Code Online (Sandbox Code Playgroud)float* d_A; cudaMalloc(&d_A, size);
...
这只是 NVIDIA 的草率工作还是我又错过了什么?
据推测,该函数正在取消引用给定的指针并写入某些已分配内存的地址。这意味着它试图float *
像写入 a 一样写入 a void *
。这与典型的void *
. 严格来说,这看起来像是未定义的行为,尽管它“有效”,因为现代 x86 处理器(不在实模式下)对所有指针类型使用相同的表示形式。