【问题标题】:Fill 2D std::array of uint16_t in C++在 C++ 中填充 uint16_t 的二维 std::array
【发布时间】:2021-10-30 01:10:43
【问题描述】:

我在 C++ 中有一个矩阵,定义为 std::arraystd::array,我想将其统一设置为给定值。对于 C 样式数组(int a[10][10] 等...),我找不到像 C 样式 memset 这样简单的东西。

我使用 std::memset 尝试了一些东西,但没有奏效(数组中有奇怪的东西)。

#include <stdint.h> // uint16_t
#include <cstring>  // using std::memset ?
#include <iostream> // to print values

// To display the values
template <typename T, std::size_t SIZE>
void Display2D(const std::array< std::array<T, SIZE>, SIZE> &matrix)
{
    for (int l = 0; l < matrix.size(); l++)
    {
        for (int c = 0; c < matrix[l].size(); c++)
        {
            std::cout << (int)matrix[l][c] << " ";
        }
        std::cout << std::endl;
    }
}



// Initialize the matrix with some values
constexpr uint8_t n = 3;
std::array<uint16_t, n> l1{11, 12, 13};
std::array<uint16_t, n> l2{21, 22, 23};
std::array<uint16_t, n> l3{31, 32, 33};
std::array< std::array<uint16_t, n>, n> matrix{l1, l2, l3};

std::cout << "Initial Matrix:" << std::endl;
Display2D(matrix);

// Try to reset it uniformly to a given value
std::memset(&matrix, (uint16_t) 4, n * sizeof(matrix[0]));

std::cout << "Matrix reset:" << std::endl;
Display2D(matrix);

在输出中我得到了:

Initial Matrix:
11 12 13
21 22 23
31 32 33
Matrix reset:
1028 1028 1028
1028 1028 1028
1028 1028 1028

我不知道我的代码出了什么问题,以及我应该怎么做才能重置我的矩阵。

帮助您帮助我的其他调试信息:

  • 如果我使用 value = 0 进行 memset,我的代码将打印一个充满 0 的矩阵(酷 =))
  • 如果我的 memset 值 = 1,我的代码会打印一个充满 257 的矩阵
  • 如果 memset 的值 = 2,我的代码会打印一个充满 514 的矩阵
  • 如果 memset 的值 = 3,我的代码会打印一个充满 771 的矩阵

我可以看到2的幂,可能与我使用std ints(uint16_t)有关,我的大脑现在已经死了,我想不通。

【问题讨论】:

  • 您的 memset 显示 UB,因为矩阵的类型为 std::array,但您正尝试以 std::uint16_t 的形式访问其中的一部分。
  • @bitmask 你能详细说明一下吗?我不确定我是否理解这一点
  • 基本上matrix 不是std::uint16_t 类型的对象。因此,您无法访问它(无论是读取还是写入)。从语言的角度来看,编译器如何在内存中布置数据是无关紧要的。

标签: c++ std memset stdarray uint16


【解决方案1】:

您不能使用基于范围的 for 循环来单独设置每个元素吗?

for(auto& arr : matrix)
    for(auto& elem : arr)
        elem = 4;

或者,如果您想使用标准库算法,您可以对矩阵中的每个数组使用填充:

for(auto& arr : matrix)
    std::fill(arr.begin(), arr.end(), 4);

【讨论】:

  • 我可以,但我希望存在更直接的解决方案,因为 memset 通常比嵌套的 for 循环更有效
  • 编辑了我在单个数组上使用 std::fill 的答案,应该编译成与 memset 大致相同的机器代码,并且符合现代 C++ 标准(我希望)。
  • 这不是效率问题,所有这些对于现代编译器来说都是微不足道的优化。所以不管你怎么做,你最终都会得到相同的机器代码。严格来说你是对的,如果提供了类方法,你应该更喜欢类方法。
  • @PierreBaret Memset 仅在字节级别“高效”,因为它考虑了底层数据的对齐,否则会在字符缓冲区中丢失。 std::fill 在类型级别工作,它知道底层类型的精确对齐,并且可以推断底层的连续性,这允许编译器执行更好的优化,例如在 4 个 uint16_t 值之上就地分配 uint64_t 值, 如果不转换为 memset ifself 如果需要。
  • 一个好的编译器会将这两种方法编译成相同的指令。它们是相同的操作,所以我不认为效率会有任何差异。见这里:godbolt.org/z/coY1MEGf6
【解决方案2】:

C 的问题在于它的类型不是很安全,如果你阅读 memset 的description,你会发现它只在字节级别起作用。

您想使用可识别类型的std::fill,并且可以与任何类型一起使用。有些人被明显的开销吓坏了,但一个体面的编译器知道如何消除开销,请参阅here

【讨论】:

  • 所以最后一个单一的方法将是这一行:std::fill(matrix[0].begin(), matrix[2].end(), (uint16_t)4);但这不是有点脏吗? (看起来像 Python...urk)
【解决方案3】:

这一行:

std::memset(&matrix, (uint16_t) 4, n * sizeof(matrix[0]));

没有意义,因为 matrix 不像普通的 C 数组那样简单,您可以简单地填写。

这是因为您还涉及std::array&lt;T&gt; 类中存在的其他成员,因此您的代码表现出未定义行为,因此数组包含的值在不同的实现中可能不同。

对于一个简单的单行解决方案,我通常定义一个方便的辅助函数来递归地填充std::arrays。

#include <type_traits>

template <typename T>
struct is_std_array : std::false_type {};

template <typename T, size_t N>
struct is_std_array<std::array<T, N>> : std::true_type {};

template <typename T, typename X, size_t N>
std::enable_if_t<!is_std_array<T>::value> fill_arr(std::array<T, N>& arr, X const& val) {
    arr.fill(val);
}

template <typename T, typename X, size_t N>
std::enable_if_t<is_std_array<T>::value> fill_arr(std::array<T, N>& arr, X const& val) {
    for (auto& e : arr)
        fill_arr(e, val);
}

现在你可以这样做了:

fill_arr(matrix, 4);

作为奖励,它也适用于任何维度的std::arrays。

【讨论】:

    猜你喜欢
    • 2015-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-08
    • 1970-01-01
    • 2018-04-13
    • 2016-04-01
    • 2018-04-20
    相关资源
    最近更新 更多