到现在为止,我认为数组与指针相同.但我发现了一个奇怪的案例:
int array[5] = { 10,11,12,13,14};
std::cout << array << std::endl;
std::cout << &array << std::endl;
std::cout << &array[0] << std::endl;
int *pArray = new int[5];
std::cout << pArray << std::endl;
std::cout << &pArray << std::endl;
std::cout << &pArray[0] << std::endl;
Run Code Online (Sandbox Code Playgroud)
0x7ffeed730ad0
0x7ffeed730ad0
0x7ffeed730ad0
0x7f906d400340
0x7ffeed730a30
0x7f906d400340
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,array并且&array具有相同的价值.但是pArray并且&pArray有不同的价值.如果数组与指针相同,则数组的地址应与数组不同.怎么可能array和&array是一样的?如果array和&array相同,保存数组值的内存的地址是什么?
[basic.compound]
如果两个对象是指针可互换的,那么它们具有相同的地址
然后注意到
数组对象及其第一个元素不是指针可互换的,即使它们具有相同的地址
使数组对象及其第一个元素非指针可互换的基本原理是什么?更一般地说,区分指针 - 可互换性概念与具有相同地址的概念的理由是什么?在那里某处不存在矛盾吗?
看来,给定这一系列陈述
int a[10];
void* p1 = static_cast<void*>(&a[0]);
void* p2 = static_cast<void*>(&a);
int* i1 = static_cast<int*>(p1);
int* i2 = static_cast<int*>(p2);
Run Code Online (Sandbox Code Playgroud)
p1 == p2但是,我们已经i1明确定义并且使用i2会导致UB.
该std::launder函数要求通过结果可以访问的每个字节都可以通过参数访问。“可达到”的定义如下:
通过一个指向对象Y的指针值可以到达一个存储字节,如果该对象位于Y所占的存储空间之内,则该对象可以与Y进行指针可互转换;如果Y是一个数组元素,则该数组将立即包含在数组对象中。
根据对另一个问题的回答,此限制“ ...意味着您将无法launder获得一个指针,该指针将允许您访问比源指针值允许更多的字节,这将带来不确定的行为。”
这对于TC给出的示例是有意义的,但是在原始对象已被新对象替换的情况下,我不理解如何解释它,这是预期的原始目的std::launder。该标准具有以下示例:
struct X { const int n; };
X *p = new X{3};
const int a = p->n;
new (p) X{5}; // p does not point to new object (6.8) because X::n is const
const int b = p->n; // undefined behavior
const int c = std::launder(p)->n; // OK
Run Code Online (Sandbox Code Playgroud)
在这种情况下,到时间std::launder被调用时,由p- 指向的对象--原始X对象--已经不存在,因为在其所占用的存储中创建新对象已隐式结束了其生命周期([基本。生活] /1.4)。因此,似乎有通过任何字节可达p因为 …
Have a look at is simple example:
struct Base { /* some virtual functions here */ };
struct A: Base { /* members, overridden virtual functions */ };
struct B: Base { /* members, overridden virtual functions */ };
void fn() {
A a;
Base *base = &a;
B *b = reinterpret_cast<B *>(base);
Base *x = b;
// use x here, call virtual functions on it
}
Run Code Online (Sandbox Code Playgroud)
Does this little snippet have Undefined Behavior?
的reinterpret_cast定义明确,它返回的值不变base,只是类型为B …
今天在回答" 基于堆的分配数组的循环范围 "的问题时,我偶然发现了使用reinterpret_cast从指针类型转换为数组类型的建议std::launder(经过一些讨论我们决定发布一个新问题,因为我们不能找到结论.)演员如下完成(例如,为了能够使用基于范围的循环迭代数组):
int* ptr = new int[3];
auto arr_ptr = std::launder(reinterpret_cast<int (*)[3]>(ptr));
Run Code Online (Sandbox Code Playgroud)
我几乎可以肯定这是UB,但我不太确定.有没有人在C++中足够坚定地回答这个问题?
我认为这可以解释为什么reinterpret_cast在从ptr-type转换为array-type(我认为是UB)时是否有效,并且无论我是否使用它都没有std::launder.
我正试图称这种方法
#define SIZE 16
void DoSomething(char(&value)[SIZE])
{
}
Run Code Online (Sandbox Code Playgroud)
从这个方法:
void BeforeDoingSomething(char* value, int len)
{
if (len == SIZE)
{
DoSomething(value);
}
}
Run Code Online (Sandbox Code Playgroud)
试图这样做会给我这个错误:
不能使用"char*"类型的值初始化类型为"char(&)[16]"(非const限定)的引用
有关如何让编译器接受value函数传递的任何提示BeforeDoingSomething?
std::launder有一个先决条件:从将要返回的指针可到达的所有字节都可以通过传递的指针到达。
我的理解是,这是为了允许编译器优化,以便例如
struct A {
int a[2];
int b;
};
void f(int&);
int g() {
A a{{0,0},2};
f(a.a[0]);
return a.b;
}
Run Code Online (Sandbox Code Playgroud)
可以优化为始终返回2。(请参阅指针可互换性与具有相同地址和能否使用 std::launder 将对象指针转换为其封闭数组指针?)
这是否意味着可达性先决条件也应该适用于placement-new?否则有什么阻止我写f如下吗?:
void f(int& x) {
new(&x) A{{0,0},0};
}
Run Code Online (Sandbox Code Playgroud)
的地址与 的地址a.a[0]相同,a并且新A对象可以透明地替换为旧A对象,因此a.bing现在应该是0。
struct X { int n; };
const X *p = new const X{3}; // #1
new (const_cast<X*>(p)) const X{5}; // #2
const int c = std::launder(p)->n;
Run Code Online (Sandbox Code Playgroud)
假设在 处创建的对象#1名为 ,obj1而在 处创建的对象#2名为obj2。的前提std::launder是
[ptr.launder] p2链接
p代表内存中一个字节的地址A。处于其生命周期内且类型与 T 类似的对象 X 位于地址 A 处。通过结果可访问的所有存储字节都可通过 p (见下文)访问。
如果存在对象 Z,则可以通过指向对象 Y 的指针值访问存储 b 的字节,并且该对象可与 Y 进行指针互换,这样 b 位于 Z 占用的存储空间内,或者如果 Z 则位于直接封闭的数组对象内是一个数组元素。
这个规则有点晦涩难懂。以下解释正确吗?
obj2sizeof(X)将占用以 开头的字节数A。将Y(指向的对象std::launder(p)) 和 …