相关疑难解决方法(0)

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

考虑以下简单结构:

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
查看次数

从C++ 17开始,使用std :: launder"验证"非"指向对象的指针"指针值

根据这个答案,从C++ 17开始,即使指针具有正确的地址,并且正确的类型解除引用它也会导致未定义的行为.

 alignas(int) unsigned char buffer[2*sizeof(int)];
 auto p1=new(buffer) int{};
 auto p2=new(p1+1) int{};
 *(p1+1)=10; // UB since c++17
Run Code Online (Sandbox Code Playgroud)

原因是指针值p1+1一个指针过去的对象.可以使用以下示例将此示例恢复为已定义的行为std::launder:

 *std::launder(p1+1)=10; // still UB?  
Run Code Online (Sandbox Code Playgroud)

其次,在下列情况下它是否也有用?

alignas(int) unsigned char buffer[3*sizeof(int)];
auto pi = new (buffer) int{};
auto pc = reinterpret_cast<unsigned char*>(pi);//not a "pointer to" an element of buffer 
                                               //since buffer[0] and *pc 
                                               //are not pointer interconvertible
//pc+2*sizeof(int) would be UB
auto pc_valid = std::launder(pc) //pc_valid is a pointer to an element of buffer …
Run Code Online (Sandbox Code Playgroud)

c++ pointers c++17

19
推荐指数
1
解决办法
608
查看次数

对于包含包装类型和类型本身的联合,是否有任何保证?

我可以把T和一个包裹Tunion并检查他们,因为我喜欢?

union Example {
    T value;
    struct Wrapped { 
       T wrapped;
    } wrapper;
};
Run Code Online (Sandbox Code Playgroud)
// for simplicity T = int

Example ex;
ex.value = 12;
cout << ex.wrapper.wrapped; // ?
Run Code Online (Sandbox Code Playgroud)

C++ 11标准只保证保存对常见初始序列的检查,但value不是struct.我答案是否定的,因为包装类型甚至不能保证与其解包对应的内存兼容,并且访问非活动成员只能在常见的初始序列上明确定义.

c++ struct types unions language-lawyer

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

使用std :: launder从指向非活动对象的指针获取指向活动对象成员的指针?

这个问题followes这一个

我们来考虑这个示例代码:

struct sso
  {
  union{
    struct {
      char* ptr;
      char size_r[8];
      } large_str;
    char short_str[16];
    };

  bool is_short_str() const{
    return *std::launder(short_str+15)=='\0'; //UB?
    }
  };
Run Code Online (Sandbox Code Playgroud)

如果short_str不是取消引用指针的活动成员而std::launder不是UB.让我们考虑ABI已经明确指定,并且我们知道size_r [7]与short_str [15]位于同一地址.并std::launder(short_str+15)返回一个指针size_r[7]的时候short_str是不是工会的活跃成员?


Nota:我认为情况就是这样,因为[ptr.launder]/3

如果对象Y位于Y所占用的存储区内,则指向存储的字节可以到达,如果Y是指针可互换的对象,则指向对象Y,或者如果Y是数组元素,则指向立即封闭的数组对象.

c++ pointers unions language-lawyer c++17

10
推荐指数
1
解决办法
456
查看次数

为什么C++编译器不会优化对结构数据成员的读写,而不是独特的局部变量?

我正在尝试创建一些POD值的本地数组(例如double),max_size在编译时已知固定,然后读取运行size时值(size <= max_size)并处理size该数组中的第一个元素.

现在的问题是,为什么不编译器消除堆读取和写入时arr,并size放置到同一个struct/ class,而不是那里的情况arrsize是独立的局部变量?

这是我的代码:

#include <cstddef>
constexpr std::size_t max_size = 64;

extern void process_value(double& ref_value);

void test_distinct_array_and_size(std::size_t size)
{
    double arr[max_size];
    std::size_t arr_size = size;

    for (std::size_t i = 0; i < arr_size; ++i)
        process_value(arr[i]);
}

void test_array_and_size_in_local_struct(std::size_t size)
{
    struct
    {
        double arr[max_size];
        std::size_t size;
    } array_wrapper;
    array_wrapper.size = size;

    for (std::size_t i = 0; i …
Run Code Online (Sandbox Code Playgroud)

c++ arrays optimization boost compilation

7
推荐指数
1
解决办法
302
查看次数

C++20 中的 std::launder 用例

[1]

\n\n

是否有任何情况不需要将p0593r6添加到 C++20 ( \xc2\xa7 6.7.2.11对象模型[intro.object] ) std::launder,而需要 C++17 中的相同用例std::launder,或者它们是完全正交?

\n\n
\n\n

[2]

\n\n

[ptr::launder]规范中的示例是:

\n\n
struct X { int n; };\nconst X *p = new const X{3};\nconst int a = p->n;\nnew (const_cast<X*>(p)) const X{5}; // p does not point to new object ([basic.life]) because its type is const\nconst int b = p->n;                 // undefined behavior\nconst int c = std::launder(p)->n;   // OK\n
Run Code Online (Sandbox Code Playgroud)\n\n

@Nicol Bolas在这个 SO 答案中给出了另一个例子,使用指向有效存储但类型不同的指针:

\n\n …

c++ language-lawyer c++20 stdlaunder

7
推荐指数
1
解决办法
2009
查看次数

使用二维数组作为一维数组是否正确?可能会导致未定义的行为吗?

这段代码合适吗?由于某种原因,使用2维数组作为1维弃用了吗?

char tab1[3][3];

for(int i = 0; i < 3; i++)
    for(int j = 0; j < 3; j++)
       tab1[i][j] = (char)i;

printf("%c", ((char*)tab1)[3]); // == tab1[1][0]
Run Code Online (Sandbox Code Playgroud)

c++ arrays

5
推荐指数
1
解决办法
106
查看次数

C++17 中仍然允许 XOR 链表吗?

考虑到 C++17 中引入的语义变化(例如在具有正确地址和类型的指针自 C++17 以来是否仍然始终是有效指针中进行了讨论) ,XOR 链接列表以一种对我来说看起来很可疑的方式使用指针算术。)。它们现在会导致未定义的行为吗?如果是这样,可以通过洗涤来拯救它们吗?

编辑:

维基百科文章仅包含有关指针和整数之间转换的简短注释。我默认(现在明确声明)指针首先被转换为足够大小的整数类型以适合它们,然后对整数进行异或。因此,操作理论中列出的 XOR 属性保证只有从指针获得一次的整数才会被转换回它们。根据标准,从指针到整数的实际映射可以是任意注入。除此之外我不依赖任何假设。

标准是否允许使用它们并访问仍然存在的对象?C++17 之前?从 C++17 开始?

c++ pointer-arithmetic xor-linkedlist c++17

4
推荐指数
1
解决办法
201
查看次数

为什么C++不允许从有效地址和类型创建有效指针?

如果你知道两条信息:

  1. 内存地址.
  2. 存储在该地址中的对象类型.

然后你逻辑上拥有引用该对象所需的一切:

#include <iostream>
using namespace std;

int main()
{
    int x = 1, y = 2;
    int* p = (&x) + 1;
    if ((long)&y == (long)p)
        cout << "p now contains &y\n";
    if (*p == y)
        cout << "it also dereference to y\n";
}
Run Code Online (Sandbox Code Playgroud)

但是,根据C++标准,这是不合法的.它适用于我尝试过的几个编译器,但它是未定义的行为.

问题是:为什么?

c++

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