【问题标题】:Is accessing a 2D array (T[N][M]) as a 1D array of size N*M guaranteed to work? [duplicate]将 2D 数组 (T[N][M]) 作为大小为 N*M 的 1D 数组访问是否可以保证工作? [复制]
【发布时间】:2019-08-16 21:28:01
【问题描述】:

我可以确定以下代码可以工作吗?

int sum_array(int *array, size_t size)
{
  int i;
  int sum = 0;
  for (i=0;i<size;i++)
    sum += *(array+i);
  return sum;
}

int main()
{
  int two_d_array[4][3] = {{1,2,3}, {4,5,6}, {7,8,9}, {10,11,12}};
  int sum = sum_array(&two_d_array[0][0], 12);
  printf("%d", sum);
}

虽然 4×3 数组在内存中的布局与 12 元素数组完全相同,但是否保证?由于我似乎在欺骗类型系统,我不太确定会出现问题(例如,将填充添加到 int[3])。

如果您能详细说明如果我在数组中使用非整数值会发生什么情况,并提供标准中的相关引用,则可以加分。

【问题讨论】:

  • 我正在为此寻找骗子。很确定结论是它在技术上是 UB
  • @NathanOliver 我也看到过回答说它在技术上不是 UB。
  • Franco,如果您实际上并不关心每个标准版本,如果您指定标准的特定版本,可能会更容易回答这个问题。 (在实践中,我发现“编译器不会崩溃,但这是 UB”仅在 最新 标准版本中值得关注,因为现有编译器不会说“我知道,回来在 C++14 中这是 UB,所以让我们围绕我们在 C++17 中禁用的 UB 添加一些优化")
  • 似乎this paragraph 表示一旦到达第一个数组的末尾,i++ 就是未定义的行为。对于数组数组的情况,我不确定是否有任何矛盾。
  • 看起来 thisthis 的欺骗关闭会回答 OP 的问题。

标签: c++ c++11 multidimensional-array language-lawyer


【解决方案1】:

例如,将填充添加到 int[3]

没有危险。数组保证没有填充。内存布局本身是有保证的,而指针算法在技术上是未定义的,根据 cmets 中提到的这句话:expr.add/4 因此:

通过指针 (T*) 遍历二维数组 (T[N][M]) 是否保证有效?

不幸的是,技术上不符合我对标准的理解。


这里是标准std::accumulate 的一个版本,它迭代任意维数的多维数组的最里面的元素,就好像它是一个平面数组一样:

template<class T, unsigned size, class BinOp = std::plus<>>
auto arr_accumulate(
    T (&arr)[size], std::remove_all_extents_t<T> init = {}, BinOp op = {})
{
    if constexpr (std::is_array_v<T>) {
        return std::accumulate(std::begin(arr), std::end(arr), init,
            [&](const auto& l, const auto& r) {
                return arr_accumulate(r, l, op);
            }
        );
    } else {
        return std::accumulate(std::begin(arr), std::end(arr), init, op);
    }
}

// usage
int sum = arr_accumulate(two_d_array, 0);

我没有对此进行基准测试以查看是否有任何开销。使用示例中的编译时间常量输入,总和是在编译时计算的。

另一种实现;仅适用于普通类型:

template<class T, unsigned size>
constexpr auto flat_size (T (&arr)[size]) {
    using E = std::remove_all_extents_t<T>;
    return sizeof arr / sizeof (E);
}

template<class T, unsigned size, class BinOp = std::plus<>>
auto arr_accumulate_trivial(
    T (&arr)[size], std::remove_all_extents_t<T> init = {}, BinOp op = {})
{
    using E = std::remove_all_extents_t<T>;
    static_assert(std::is_trivially_copyable_v<E>);
    std::array<E, flat_size(arr)> flat;
    std::memcpy(flat.data(), arr, sizeof arr);
    return std::accumulate(std::begin(flat), std::end(flat), init, op);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-28
    • 2016-07-02
    相关资源
    最近更新 更多