使用c 中的static_cast施放数组参考的指针是合法的吗?

Is it legal to cast a pointer to array reference using static_cast in C++?

本文关键字:指针 参考 数组 中的 static 施放 cast 使用      更新时间:2023-10-16

我有一个指针T * pValues,我想将其视为 T (&values)[N]

在此答案中

T (&values)[N] = *static_cast<T(*)[N]>(static_cast<void*>(pValues));

我对此的担忧。在他的示例中,pValues以以下方式初始化

初始化
T theValues[N];
T * pValues = theValues;

我的问题是,如果pValues来自以下任何构造,则铸造构建体是否合法:

1:

T theValues[N + M]; // M > 0
T * pValues = theValues;

2:

T * pValues = new T[N + M]; // M >= 0

简短答案:您是对的。仅当pValuesT[N]型,并且您提到的两种情况(不同的大小,动态分配的数组)很可能导致不确定的行为。。

时,铸件才安全。

关于static_cast的好处是,在编译时间内进行了一些其他检查,因此,如果您做错了什么,编译器会抱怨它(与丑陋的C风格铸件相比,您几乎可以做任何事情),例如:

struct A { int i; };
struct C { double d; };
int main() {
    A a;
    // C* c = (C*) &a; // possible to compile, but leads to undefined behavior
    C* c = static_cast<C*>(&a);
}

会给你: invalid static_cast from type ‘A*’ to type ‘C*’

在这种情况下,您将其投入到void*中,从编译时间可以进行的支票的角度来看,几乎对任何事物都是合法的,反之亦然:void*也可以恢复到几乎所有物品,也可以将static_cast完全没有用,因为这些检查变得毫无用处。

对于上一个示例:

C* c = static_cast<C*>(static_cast<void*>(&a));

不比:

更好
C* c = (C*) &a;

,并且很可能会导致该指针的使用不正确,不确定的行为


换句话说:

A arr[N];
A (&ref)[N] = *static_cast<A(*)[N]>(&arr);

是安全的,就可以了。但是,一旦您开始滥用static_cast<void*>,就无法确保实际发生的事情,因为甚至类似:

C *pC = new C;
A (&ref2)[N] = *static_cast<A(*)[N]>(static_cast<void*>(&pC));

成为可能。

,因为C 17至少所显示的表达式不安全,即使pValues是指向数组的第一个元素的指针,并且数组与完全匹配的类型(包括axcat尺寸),无论是从变量声明或拨打new的调用中获得的。(如果不满足这些标准,无论以下情况如何。)

数组及其第一个元素不是 pointer-interconvertible ,因此reinterpret_cast(相当于两个static_castsvoid*)不能将一个指针值施加到另一个指针值的指针值。p>因此,static_cast<T(*)[N]>(static_cast<void*>(pValues))仍将指向数组的第一个元素,而不是数组对象本身。

由于类型/值不匹配,因此该指针的限制是未定义的。

可以使用std::launder对此进行纠正,这可能会更改reinterpret_cast不能的指针值。具体而言,以下可能是明确的:

T (&values)[N] = *std::launder(static_cast<T(*)[N]>(static_cast<void*>(pValues)));

或等效

T (&values)[N] = *std::launder(reinterpret_cast<T(*)[N]>(pValues));

但是,只有当std::launder返回的指针不能用于访问无法通过原始pValues指针访问的任何字节。如果数组是一个完整的对象,则可以满足这一点,例如如果阵列是二维数组的子阵列,则不满意。

有关确切的可及性条件,请参见https://en.cppreference.com/w/cpp/utility/launder。