通过取消指向指向不同类型或void
的指针的指针来访问指针变量是一种好习惯吗?这可以打破严格的别名规则吗? C和C ++在别名规则上有一些区别。在这个问题上,我们关注C ++。可以在here中找到考虑C的另一个问题。在以下示例中,将double*
作为void*
进行访问。
int create_buffer(void** ptr, ...)
{
*ptr = malloc(...);
...
}
int main(void)
{
double* buffer;
// The problematic code is here, double**
// is coerced to void**, which is later
// dereferenced by the function
create_buffer(reinterpret_cast<void**>(&buffer), ...);
...
}
如果这是导致UB的原因,那么该怎么办?
// process A
int* p; ...
printf("%p", p); // UB?
// process B
int* p;
scanf("%p", &p); // UB?
这看起来像是一个不好的例子,但是如果两个进程通过管道相互通信,最终一个进程将指向全局分配内存的指针传递给另一个进程,该怎么办?
答案 0 :(得分:2)
访问...是一种好习惯吗
不。即使没有考虑原始代码中的混叠问题,void*
也不是实现C ++多态性和重用的首选。借助丰富的模板机制,您可以使代码具有强类型性,并且可以更安全地启动。明显的改进是使用模板,并键入安全分配:
template<typename T>
int create_buffer(T** ptr, ...)
{
*ptr = new T[...];
...
}
但是要切线,这仍然不是C ++的外观。好的C ++可以正确地管理复杂性。跟踪缓冲区是一项复杂的任务。好的C ++方法不是手工完成,而是将其封装在专用的类(模板)中。实际上,这是一项常见的任务,标准库提供了解决方案。
好的做法是使用std::vector<double>
代替缓冲区创建功能。类型通用任务的类模板通常会胜过void*
的使用。由于始终使用正确的类型,因此可以完全避免任何混叠问题。
答案 1 :(得分:1)
这是UB,因为您要为其分配double*
变量,就好像它是一个void*
变量一样(在C ++中,其作用与reinterpret_cast<void*&>(buffer) = malloc(...);
相同。在大多数系统上都可以因为void*
和double*
通常是完全相同的,但是它仍然是UB,因此可能不适用于所有实现。
一种解决方案是将一个变量赋给另一个变量,然后重新赋值:
int main(void)
{
double* buffer;
{
void* result;
create_buffer(&result, ...);
buffer = (double*) result; // Cast needed for C++
}
...
}