在C ++

时间:2019-05-18 14:19:43

标签: c++ pointers strict-aliasing

通过取消指向指向不同类型或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?

这看起来像是一个不好的例子,但是如果两个进程通过管道相互通信,最终一个进程将指向全局分配内存的指针传递给另一个进程,该怎么办?

2 个答案:

答案 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++
    }
    ...
}