相关疑难解决方法(0)

限制指针算术或比较的基本原理是什么?

在C/C++中,仅当结果指针位于原始指向的完整对象内时,才定义指针的加法或减法.此外,只有当两个指向对象是唯一完整对象的子对象时,才能执行两个指针的比较.

这种限制的原因是什么?

我认为分段内存模型(参见这里 §1.2.1)可能是其中一个原因,但由于编译器实际上可以定义所有指针的总顺序,如本答案所示,我对此表示怀疑.

c c++ pointers pointer-arithmetic language-lawyer

26
推荐指数
3
解决办法
1195
查看次数

`reinterpret_cast`是一个'T*`到'T(*)[N]`是不确定的行为?

请考虑以下情形:

std::array<int, 8> a;
auto p = reinterpret_cast<int(*)[8]>(a.data());
(*p)[0] = 42;
Run Code Online (Sandbox Code Playgroud)

这是未定义的行为吗?我觉得是这样的.

  • a.data()返回一个int*,不一样的int(*)[8]

  • cppreference上的类型别名规则似乎表明它无效reinterpret_cast

  • 作为程序员,我知道指向的内存位置a.data()是一个8 int对象数组

是否有任何我失踪的规则使这个reinterpret_cast有效?

c++ strict-aliasing language-lawyer reinterpret-cast c++17

25
推荐指数
2
解决办法
1298
查看次数

具有基本类型的单个数组成员的标准布局结构的保证内存布局

考虑以下简单结构:

struct A
{
    float data[16];
};
Run Code Online (Sandbox Code Playgroud)

我的问题是:

假设平台上float有32位IEEE754浮点数(如果很重要),那么C ++标准是否可以保证预期的内存布局struct A?如果不是,它将提供什么保证和/或执行保证的方式是什么?

预期存储器布局我的意思是该结构占用16*4=64在存储器字节,每个连续4字节占用由单个floatdata阵列。换句话说,预期的内存布局意味着以下测试通过:

static_assert(sizeof(A) == 16 * sizeof(float));
static_assert(offsetof(A, data[0]) == 0 * sizeof(float));
static_assert(offsetof(A, data[1]) == 1 * sizeof(float));
...
static_assert(offsetof(A, data[15]) == 15 * sizeof(float));
Run Code Online (Sandbox Code Playgroud)

offsetof这里是合法的,因为A是标准布局,请参见下文)

万一这困扰您,测试实际上会通过 gcc 9 HEAD在wandbox上通过。我从未遇到过平台和编译器的结合,它们会提供证据证明该测试可能会失败,并且我很想了解它们的存在。

为什么还要关心:

  • 类似于SSE的优化需要某些内存布局(和对齐方式,我可以在此问题中忽略它,因为可以使用标准alignas说明符来处理它)。
  • 这样的结构的序列化将简单地归结为一个不错的和可移植的write_bytes(&x, sizeof(A))
  • 一些API(例如OpenGL,特别是glUniformMatrix4fv)期望这种确切的内存布局。当然,可以仅将指针传递给data数组以传递这种类型的单个对象,但是对于其中的一系列对象(例如,用于上传矩阵类型的顶点属性),仍然需要特定的内存布局。 …

c++ memory-layout language-lawyer standard-layout

20
推荐指数
1
解决办法
713
查看次数

为什么使用 `std::aligned_storage` 据称会因无法“提供存储”而导致 UB?

灵感来源:为什么 std::aligned_storage 在 C++23 中被弃用以及使用什么替代?

\n

链接的提案P1413R3(不赞成使用std::aligned_storage)表示:

\n
\n

使用aligned_*调用未定义的行为(类型无法提供存储。)

\n
\n

这是指[intro.object]/3

\n
\n

如果在与N \xe2\x80\x9d 类型的 \xe2\x80\x9carray或N \xe2 \ 类型 \xe2\x80\x9carray 的另一个对象e关联的存储中创建了完整对象 ([expr.new]) x80\x9d ([cstddef.syn]),该数组为创建的对象提供存储,如果: ... unsigned char std\xe2\x80\x8b::\xe2\x80\x8bbyte

\n
\n

然后,该标准在一些定义中继续使用术语“提供存储”,但我没有看到它在任何地方说使用不同类型作为新放置的存储(无法“提供存储”)会导致 UB 。

\n

那么,问题是:std::aligned_storage当用于放置新时,是什么导致了UB?

\n

c++ language-lawyer c++23

19
推荐指数
2
解决办法
1563
查看次数

放置new的返回值与其操作数的转换值之间是否存在(语义)差异?

放置new的返回值与其操作数的转换值之间是否存在(语义)差异?

struct Foo { ... };
char buffer[...];

Foo *a = new(buffer) Foo;
Foo *b = reinterpret_cast<Foo *>(buffer);
Run Code Online (Sandbox Code Playgroud)

是否ab以某种方式有什么不同?


编辑:根据DaBler的评论,这个问题告诉我,如果使用const/reference成员则存在差异:使用const成员放置新类和赋值

所以,我的小位的更新问题:是否ab以任何方式不同,如果Foo没有const或引用成员?

c++ strict-aliasing placement-new language-lawyer reinterpret-cast

13
推荐指数
2
解决办法
488
查看次数

可以使用std :: launder将对象指针转换为其封闭的数组指针吗?

目前的标准草案(可能是C++ 17)在[basic.compound/4]中说:

[注意:数组对象及其第一个元素不是指针可互换的,即使它们具有相同的地址. - 结束说明]

因此,指向对象的指针不能reinterpret_cast获得其封闭的数组指针.

现在,有std::launder,[ptr.launder/1]:

template<class T> [[nodiscard]] constexpr T* launder(T* p) noexcept;

要求:p表示内存中字节的地址A. 在其生命周期内且其类型类似于T的对象X位于地址A处.可通过结果到达的所有存储字节都可通过p(见下文)到达.

和的definion 可达是在[ptr.launder/3] :

备注:只要可以在核心常量表达式中使用其参数的值,就可以在核心常量表达式中使用此函数的调用.如果对象Y位于Y所占用的存储区内,则指向存储的字节可以到达,如果Y是指针可互换的对象,则指向对象Y,或者如果Y是数组元素,则指向立即封闭的数组对象.如果T是函数类型或cv void,则程序格式错误.

现在,乍一看,似乎std::launder可以用来做上述转换,因为我强调的部分.

但.如果p指向数组的对象,则根据此定义可以访问数组的字节(即使p不是指针可互换为数组指针),就像清洗的结果一样.因此,该定义似乎没有说明这个问题.

那么,可以std::launder用来将对象指针转换为其封闭的数组指针吗?

c++ language-lawyer c++17

12
推荐指数
1
解决办法
576
查看次数

Is converting a reinterpret_cast'd derived class pointer to base class pointer undefined behavior?

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 …

c++ language-lawyer

11
推荐指数
1
解决办法
346
查看次数

std::launder 与放置 - 新的可达性条件

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

c++ placement-new language-lawyer stdlaunder

6
推荐指数
0
解决办法
382
查看次数

为什么c ++标准不保证对象数组与其第一个元素之间的指针可互换性?

根据这个

http://eel.is/c++draft/basic.compound#4

如果出现以下情况,则两个对象a和b是指针可互换的:

  • 它们是同一个对象,或者
  • 一个是union对象,另一个是该对象的非静态数据成员([class.union]),或
  • 一个是标准布局类对象,另一个是该对象的第一个非静态数据成员,或者,如果该对象没有非静态数据成员,则该对象的第一个基类子对象([class.mem]) ), 要么
  • 存在对象c,使得a和c是指针可互换的,并且c和b是指针可互换的.

如果两个对象是指针可互换的,则它们具有相同的地址,并且可以通过reinterpret_cast从指向另一个的指针获得指向一个对象的指针.[  注意:数组对象及其第一个元素不是指针可互换的,即使它们具有相同的地址. - 结束说明]

为什么c ++标准不保证对象数组与其第一个元素之间的指针可互换性,而保证类及其第一个成员?

为什么这是未定义的行为?

char carr[8];
char& ch0 = carr[0];
auto& carr2 = reinterpret_cat<char (&) [8]>(ch0); // Is this considered undefined behavior?

// Because ch0 (char&) and carr2 (char(&)[8]) are not "pointer-interconvertible".
// So they can not use reinterpret_cast "definedly".
// Array and its first element are not "pointer-interconvertible"
// eventhough they share the same address.
Run Code Online (Sandbox Code Playgroud)

虽然这是标准本身的一个例子.

http://www.eel.is/c++draft/basic.types#2

#define N sizeof(T)
char buf[N];
T obj;                          // obj initialized …
Run Code Online (Sandbox Code Playgroud)

c++ undefined-behavior reinterpret-cast c++11 c++17

5
推荐指数
0
解决办法
155
查看次数