使用 std::fill 填充多维数组的安全方法是啥?

Posted

技术标签:

【中文标题】使用 std::fill 填充多维数组的安全方法是啥?【英文标题】:What is the safe way to fill multidimensional array using std::fill?使用 std::fill 填充多维数组的安全方法是什么? 【发布时间】:2011-04-26 06:51:27 【问题描述】:

这是我正在使用的:

class something

   char flags[26][80];
 a;

std::fill(&a.flags[0][0], &a.flags[0][0] + 26 * 80, 0);

(更新:我应该早点说清楚我在一个类中使用它。)

【问题讨论】:

【参考方案1】:

初始化为0数组的简单方法在定义中:

char flags[26][80] = ;

如果你想使用std::fill,或者你想重置数组,我觉得这样好一点:

char flags[26][80];
std::fill( &flags[0][0], &flags[0][0] + sizeof(flags) /* / sizeof(flags[0][0]) */, 0 );

以数组大小表示的fill 将允许您更改尺寸并保持fill 不变。在您的情况下,sizeof(flags[0][0])1 (sizeof(char)==1),但您可能希望将其保留在那里,以防您想随时更改类型。

在这种特殊情况下(flags --integral 类型的数组)我什至可以考虑使用memset,即使它是最不安全的替代方案(这会破坏如果数组类型改为非pod类型):

memset( &flags[0][0], 0, sizeof(flags) );

请注意,在所有三种情况下,数组大小只输入一次,其余的由编译器推断。这更安全一些,因为它为程序员错误留出了更少的空间(在一个地方更改大小,在其他地方忘记它)。

编辑:您已经更新了代码,因为它不会编译,因为数组是私有的并且您正在尝试在外部对其进行初始化。根据您的类是否实际上是一个聚合(并希望保留它)或者您是否想向该类添加一个构造函数,您可以使用不同的方法。

const std::size_t rows = 26;
const std::size_t cols = 80;
struct Aggregate 
   char array[rows][cols];
;
class Constructor 
public:
   Constructor() 
      std::fill( &array[0][0], &array[rows][0], 0 ); // [1]
      // memset( array, 0, sizeof(array) );
   
private:
   char array[rows][cols];
;
int main() 
   Aggregate a = ;
   Constructor b;

即使array 是公开的,使用构造函数可能是更好的方法,因为它可以保证array 在类的所有实例中正确初始化,而外部初始化取决于用户代码不要忘记设置初始值。

[1] 正如@Oli Charlesworth 在评论中提到的,对于必须在多个位置声明(并保持同步)大小的问题,使用常量是一种不同的解决方案。我在这里使用了这种方法,但组合却不同:可以通过请求二维数组之外的第一列的地址来获得指向二维数组之外的第一个字节的指针。我使用这种方法只是为了表明它是可以做到的,但它并不比&array[0][0]+(rows*cols)等其他方法更好

【讨论】:

+1。但是,使用sizeof 的替代方法是使用#define(或const int)尺寸。这还有一个额外的好处,如果您将 flags 作为函数参数传递,它将起作用,因此它会衰减为指针,因此 sizeof 将不再给出正确的结果。 我不确定我是否可以使用初始化大括号,因为二维数组在一个类中。在我接受之前,我会给你一个更新的机会。 @DavidRodriguez:您能解释一下为什么类似的语法(即fill(&arr[0], &arr[0] + sizeof(arr), 0) )不适用于一维数组吗? @DhruvMullick:sizeof 运算符为您提供以字节为单位的大小,而不是元素的数量,如果 arr 包含任何诸如 sizeof(*arr) != 1 以上具有未定义的行为并且可能会崩溃(尝试访问超出数组末尾的内容)。否则(即如果数组包含[unsigned|signed] char,它应该可以工作 其实我不知道在填充的语法中,我们使用了元素的个数。【参考方案2】:

使用std::fill填充多维数组的安全方法是什么

简单的默认初始化是using braced inilization。

char flags[26][80];

上面会将flags中的所有元素初始化为默认字符。


使用std::fillstd::fill_n 填充二维数组

但是,为了提供不同的值来初始化以上是不够的。选项为std::fillstd::fill_n。 (假设你的类中数组flagspublic

std::fill(
   &a.flags[0][0],
   &a.flags[0][0] + sizeof(a.flags) / sizeof(a.flags[0][0]),
   '0');

// or using `std::fill_n`
// std::fill_n(&a.flags[0][0], sizeof(a.flags) / sizeof(a.flags[0][0]), '1');

为了将这一点推广到具有任何初始化值的任何类型的任何 2d-array,我建议a templated function 如下。这也将避免 sizeof 计算数组中的总元素。

#include <algorithm> // std::fill_n, std::fill
#include <cstddef>   // std::size_t

template<typename Type, std::size_t M, std::size_t N>
constexpr void fill_2D_array(Type(&arr2D)[M][N], const Type val = Type) noexcept

   std::fill_n(&arr2D[0][0], M * N, val);
   // or using std::fill
   // std::fill(&arr2D[0][0], &arr2D[0][0] + (M * N ), val);

现在你可以初始化你的flagslike

fill_2D_array(a.flags, '0'); // flags should be `public` in your class!

(See Live Online)


使用std::fillstd::fill_n 填充3-D 数组

在上面的模板函数中再增加一个非模板大小参数,这个也可以带入到3d-arrays中

#include <algorithm> // std::fill_n
#include <cstddef>   // std::size_t

template<typename Type, std::size_t M, std::size_t N, std::size_t O>
constexpr void fill_3D_array(Type(&arr3D)[M][N][O], const Type val = Type) noexcept

   std::fill_n(&arr3D[0][0][0], M * N * O, val);

(See Live Online)

【讨论】:

【参考方案3】:

是安全的,二维数组是数组的数组。由于数组占用了连续的存储空间,所以整个多维的东西也会。所以,是的,它没关系,安全且便携。假设您不询问样式,其他答案已涵盖(因为您使用的是标志,我强烈推荐 std::vector&lt;std::bitset&lt;80&gt; &gt; myFlags(26)

【讨论】:

您确定该位集适合吗?我只是在二维数组的每个空间内存储零或一。这些标志用于跟踪控制台上的哪些位置已使用我的 Floodfill 例程进行了更新。 @Truncheon:你在一个字符中存储了 8 个值,对吧?为了在第 6 个字符中获得第 7 个值,您必须进行一些位移/与运算等。bitset 将为您完成。也就是说,它将存储每个标志位,您可以通过其索引设置/取消设置每个标志,而不必担心位模式。唯一的缺点是位集的大小必须是编译时常量。如果你熟悉 boost,他们有 dynamic_bitset 这几乎就是它的名字。 我相信他在每个字节中存储了一个位。这意味着使用 26*80 位置的位集和适当的(row,col)-&gt;index 代数使用单个bitset 将更有效地消耗内存。 @David:在内存方面肯定会更高效,但可读性会受到影响,也就是说他将不得不写 btst[numCols*row+col] 与 v[ 相比,它的可读性较差行][列]。该决定取决于 OP,并取决于他的优先级。【参考方案4】:
char flags[26][80];
std::fill((char*)flags, (char*)flags + sizeof(flags)/sizeof(char), 0);

【讨论】:

【参考方案5】:

char[80] 是否应该替代真正的字符串类型?在这种情况下,我建议如下:

std::vector<std::string> flags(26);
flags[0] = "hello";
flags[1] = "beautiful";
flags[2] = "world";
// ...

或者,如果您有支持初始化列表的 C++ 编译器,例如最近的 g++ 编译器:

std::vector<std::string> flags  "hello", "beautiful", "world" /* ... */ ;

【讨论】:

这不是试图回答问题,只是给出没有人要求的建议。

以上是关于使用 std::fill 填充多维数组的安全方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

在 2D int 向量上使用 std::fill

PHP填充多维关联数组 - 最简单的方法

csharp 如何使用随机数填充多维数组并使用方法打印它的示例。

csharp 如何使用随机数填充多维数组并使用方法打印它的示例。

使用带有std :: fill等算法的emplace

填充临时数组以避免在动态多维数组上使用 Preserve