使用 struct 分块处理数组,然后将其转换为平面数组 - 如何避免 UB(严格别名)?

Oli*_*ock 5 c++ strict-aliasing undefined-behavior language-lawyer

外部 API 需要一个指向值数组(此处为 int 作为简单示例)的指针加上一个大小。

以 4 为一组处理元素在逻辑上更清晰。

因此,通过“4 组”结构处理元素,然后使用指针转换将这些结构的数组传递到外部 API。请参阅下面的代码。

蜘蛛感知说:=> 可能的 UB 中存在“严格混叠违规” reinterpret_cast

  1. 以下内容static_asserts是否足以确保:a) 这在实践中有效 b) 这实际上符合标准而不是 UB?

  2. 否则,我需要做什么,才能使其“不是UB”。工会?请问具体怎么样?

  3. 或者,是否有一种不同的、更好的方法?


#include <cstddef>

void f(int*, std::size_t) {
    // external implementation
    // process array
}

int main() {

    static constexpr std::size_t group_size    = 4;
    static constexpr std::size_t number_groups = 10;
    static constexpr std::size_t total_number  = group_size * number_groups;

    static_assert(total_number % group_size == 0);

    int vals[total_number]{};

    struct quad {
        int val[group_size]{};
    };

    quad vals2[number_groups]{};
    // deal with values in groups of four using member functions of `quad`

    static_assert(alignof(int) == alignof(quad));
    static_assert(group_size * sizeof(int) == sizeof(quad));
    static_assert(sizeof(vals) == sizeof(vals2));

    f(vals, total_number);
    f(reinterpret_cast<int*>(vals2), total_number); /// is this UB? or OK under above asserts?
}

Run Code Online (Sandbox Code Playgroud)

Nic*_*las 2

再多的static_asserts 也无法使UB 的东西变成符合标准的明确定义的行为您没有创建 s 数组int;您创建了一个包含ints 数组的结构。这就是你所拥有的。

\n

将指向 a 的指针转换quad为指向 an 的指针是合法的int[group_size](尽管您需要适当地更改代码。或者您可以直接访问数组并将其转换为int*.

\n

无论如何获得指向第一个元素的指针,在该数组中进行指针算术都是合法的。但是,当您尝试在该对象内quad的数组边界之外进行指针算术时,就会实现未定义的行为。指针运算是根据数组的存在来定义的:[expr.add]/4

\n
\n

当整型表达式 J 与指针类型表达式 P 相加或相减时,结果的类型为 P。

\n
    \n
  • 如果 P 的计算结果为空指针值且 J 的计算结果为 0,则结​​果为空指针值。
  • \n
  • 否则,如果 P 指向具有 n 个元素的数组对象 x ([dcl.array]) 的数组元素 i,则表达式 P + J 和 J + P(其中 J 的值为 j)指向(可能假设的) x 的数组元素 i+j if 0\xe2\x89\xa4i+j\xe2\x89\xa4n 并且表达式 P - J 指向 x 的(可能是假设的)数组元素 i\xe2\x88\x92j if 0\xe2\x89\xa4i\xe2\x88\x92j\xe2\x89\xa4n。
  • \n
  • 否则,行为是未定义的。
  • \n
\n
\n

指针不为空,因此情况 1 不适用。上面ngroup_size(因为数组是 内的数组quad),所以如果索引是 > group_size,则情况 2 不适用。

\n

因此,每当有人尝试访问索引 4 之后的数组时,都会发生未定义的行为。没有任何强制转换可以覆盖它。

\n
\n
\n

否则,我需要做什么,才能使其“不是UB”。工会?请问具体怎么样?

\n
\n

你不知道。您尝试做的事情对于 C++ 对象模型来说根本无效。您需要一个 s 数组int,因此必须创建一个ints 数组。您不能将除 s 之外的其他数组int视为 s 数组int(好吧,除了字节数组的少数例外,但这对您没有帮助)。

\n
\n

分组处理数组的最简单有效方法就是......执行一些嵌套循环:

\n
int arr[total_number];\nfor(int* curr = arr; curr != std::end(arr); curr += 4)\n{\n  //Use `curr[0]` to `curr[3]`;\n  //Or create a `std::span<int, 4> group(curr)`;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

  • `reinterpret_cast` 本身迂腐地也不起作用,因为数组的元素不能与数组进行指针相互转换。但是,数组对象本身可以与“quad”对象进行指针互换,因此“*reinterpret_cast&lt;int(*)[group_size]&gt;(vals2)”可以工作。或者实际上只是“vals2[0].val”而不是那种奇怪的东西。不确定这是否应该被视为标准中的缺陷。 (2认同)