将引用转换为指向 void* 的引用的指针

Casting a reference to a pointer to a reference to a void*

以下是定义明确的行为吗?

#include <cstdlib>
#include <iostream>

void reallocate_something(void *&source_and_result, size_t size) {
    void *dest = malloc(size);
    memcpy(dest, source_and_result, size);
    free(source_and_result);
    source_and_result = dest;
}

void reallocate_something(int *&source_and_result, size_t size) {
    // I the cast safe in this use case?
    reallocate_something(reinterpret_cast<void*&>(source_and_result), size);
}

int main() {
    const size_t size = 4 * sizeof(int);
    int *start = static_cast<int*>(malloc(size));
    *start = 0;

    std::cout << start << ' ' << *start << '\n';
    reallocate_something(start, size);
    std::cout << start << ' ' << *start << '\n';

    return 0;
}
  

该代码使用 reinterpret_cast 传递对指针的引用并重新分配它,释放它,并将它设置到分配的新区域。这个定义好吗?

特别是如果我不想传递引用,A static_cast 会起作用,而且定义明确。

标签是 C++,我问的是 C++ 标准中的代码。

其实我不确定,但我觉得这是正确的做法。

#include <iostream>
#include <cstring>

void reallocate_something(void *&source_and_result, size_t size) {
    void *dest = malloc(size);
    memcpy(dest, source_and_result, size);
    free(source_and_result);
    source_and_result = dest;
}

void reallocate_something(int *&source_and_result, size_t size) {
    // Is the cast safe in this use case?
    void *temp = static_cast<void*>(source_and_result);
    reallocate_something(temp, size);
    source_and_result = static_cast<int*>(temp);
}

int main() {
    const size_t size = 4 * sizeof(int);
    int *start = static_cast<int*>(malloc(size));

    std::cout << start << ' ' << *start << '\n';
    reallocate_something(start, size);
    std::cout << start << ' ' << *start << '\n';

    return 0;
}

这没有明确定义,因为 void*int* 不相似。请参阅 类型别名 部分 here

请注意,如下所示通过 void* 的指针往返是明确定义的。特别是这里没有类型别名

T* pt = ...;

void* p = pt;

auto pt2 = static_cast<T*>(p);
assert(pt2 == pt);

这与以下带有 类型别名 的代码不同,后者定义不明确。

T* pt = ...;

void* p = nullptr;
reinterpret_cast<T*&>(p) = pt; // or *reinterpret_cast<T**>(&p) = pt;

auto pt2 = static_cast<T*>(p);
assert(pt2 == pt);

因此,您的示例代码可以修改如下。

void reallocate_something(int *&source_and_result, size_t size) {
    void* p = source_and_result;
    reallocate_something(p, size);
    source_and_result = static_cast<int*>(p);
}

或者更好

void* reallocate_something(void *source_and_result, size_t size) {
    void *dest = malloc(size);
    memcpy(dest, source_and_result, size);
    free(source_and_result);
    return dest;
}

void reallocate_something(int *&source_and_result, size_t size) {
    source_and_result = static_cast<int*>(reallocate_something(source_and_result, size));
}

Is the following well defined behavior?

不,不是。您不能用 void * 句柄解释 int * 指针,intvoid 不是相似类型。您可以 一个 int * 指针转换为 void * 并返回。如果您的函数采用引用,要进行转换,您需要一个类型为 void * 的新临时变量来保存转换结果,然后您必须将其分配回去,就像其他答案 .

不管怎样,就把它做成一个模板,然后用新的placement写出漂亮的C++代码。沿途的东西:

template<typename T>
void reallocate_something(T *&pnt, size_t cnt) {
    T *dest = reinterpret_cast<T *>(malloc(cnt * sizeof(T)));
    if (dest == NULL) throw ...;
    for (size_t i = 0; i < cnt; ++i) {
       new (dest[i]) T(pnt[i]);
    }
    free(static_cast<void*>(pnt));
    pnt = dest;
}

存在 int*void* 的按位表示不兼容的平台。在这样的平台上,编译器通常不可能允许一种类型的引用有意义地作用于另一种类型的对象,因此标准避免要求实现这样做。

当然,绝大多数平台都对所有 PODS 指针类型使用相同的表示,并且在编写标准时,几乎每个人都清楚 (1) 它允许针对此类平台的编译器有效地处理引用类型转换,并且 (2) 编译器应该有效地处理此类转换,除非有令人信服的理由不这样做。预计唯一关心此类转换是否已定义行为的编译器编写者将是那些针对此类支持昂贵的平台(例如,要求使用相同的位模式存储其地址的任何 int*作为 void*,即使在使用它来获取 int 时需要改组它的位),编译器作者应该比委员会更了解这种支持的成本和收益。

大多数实现都可以配置为以编写标准时预期的方式处理此类转换,但标准不强制要求提供此类支持;当这样的配置时,构造的行为应该被视为由流行的语言扩展定义。