使用一个默认值初始化普通数组

Mil*_*lan 230 c c++ arrays initialization default-value

C++注意事项:数组初始化有一个很好的列表初始化列表.我有一个

int array[100] = {-1};
Run Code Online (Sandbox Code Playgroud)

期望它充满-1,但它不是,只有第一个值,其余的是0与随机值混合.

代码

int array[100] = {0};
Run Code Online (Sandbox Code Playgroud)

工作正常,并将每个元素设置为0.

我在这里想念的是什么..如果值不为零,不能初始化它吗?

2:默认初始化(如上所述)是否比通过整个数组的通常循环更快并分配一个值还是做同样的事情?

Eva*_*ran 333

使用您使用的语法,

int array[100] = {-1};
Run Code Online (Sandbox Code Playgroud)

说"将第一个元素设置为-1其余部分0",因为所有省略的元素都设置为0.

在C++中,要将它们全部设置为-1,您可以使用类似std::fill_n(from <algorithm>)的内容:

std::fill_n(array, 100, -1);
Run Code Online (Sandbox Code Playgroud)

在便携式C中,您必须自己循环.有编译器扩展,或者您可以依赖实现定义的行为作为快捷方式,如果这是可接受的.

  • 这也回答了关于如何"轻松"使用默认值填充数组的间接问题.谢谢. (12认同)
  • @chessofnerd:不确切地说,`#include <algorithm>`是正确的标题,`<vector>`可能会或可能不会间接包含它,这取决于你的实现. (7认同)
  • 这是对其他一些问题的回答.`std :: fill_n`不是初始化. (7认同)
  • 您不必在运行时期间初始化数组.如果你真的需要静态地进行初始化,可以使用可变参数模板和可变序列来生成所需的`int`s序列并将其扩展到数组的初始化器中. (2认同)
  • @ontherocks,没有正确的方法可以使用一次调用`fill_n`来填充整个2D数组.您需要在一个维度上循环,同时填充另一个维度. (2认同)

Cal*_*lum 131

gcc编译器有一个扩展,它允许语法:

int array[100] = { [0 ... 99] = -1 };
Run Code Online (Sandbox Code Playgroud)

这会将所有元素设置为-1.

这被称为"指定初始化器",请参阅此处以获取更多信息.

请注意,这不适用于gcc c ++编译器.

  • 太棒了 此语法似乎也可以在clang中使用(因此可以在iOS / Mac OS X上使用)。 (2认同)

jal*_*alf 31

您链接到的页面已经给出了第一部分的答案:

如果指定了显式数组大小,但指定了较短的初始化列表,则未指定的元素将设置为零.

没有内置方法将整个数组初始化为某个非零值.

至于哪个更快,通常的规则适用:"给编译器最大自由的方法可能更快".

int array[100] = {0};
Run Code Online (Sandbox Code Playgroud)

只需告诉编译器"将这100个int设置为零",编译器可以自由优化.

for (int i = 0; i < 100; ++i){
  array[i] = 0;
}
Run Code Online (Sandbox Code Playgroud)

更具体.它告诉编译器创建一个迭代变量i,它告诉它应该初始化元素的顺序,依此类推.当然,编译器很可能会对此进行优化,但关键在于您在此处过度指定问题,迫使编译器更加努力地获得相同的结果.

最后,如果要将数组设置为非零值,则应该(至少在C++中)使用std::fill:

std::fill(array, array+100, 42); // sets every value in the array to 42
Run Code Online (Sandbox Code Playgroud)

同样,您可以对数组执行相同操作,但这更简洁,并为编译器提供更多自由.你只是说你想要整个数组填充值42.你没有说明它应该以什么顺序完成,或者其他任何事情.

  • 好答案.请注意,在C++中(不在C中),您可以执行int array [100] = {}; 并给编译器最自由:) (5认同)

Tim*_*mmm 11

C++ 11有另一个(不完美)选项:

std::array<int, 100> a;
a.fill(-1);
Run Code Online (Sandbox Code Playgroud)


0x6*_*015 9

使用{},您可以在声明时分配元素; 其余部分用0初始化.

如果没有= {}初始化,则内容未定义.


laa*_*lto 8

您链接的页面说明

如果指定了显式数组大小,但指定了较短的初始化列表,则未指定的元素将设置为零.

速度问题:对于这么小的阵列,任何差异都可以忽略不计.如果使用大型数组并且速度比大小重要得多,则可以使用默认值的const数组(在编译时初始化),然后将memcpy它们转换为可修改的数组.

  • memcpy不是一个好主意,因为这可以直接快速地设置值. (2认同)

Dav*_*one 8

使用std::array,我们可以在 C++14 中以一种相当简单的方式做到这一点。只能在 C++11 中完成,但稍微复杂一些。

我们的接口是一个编译时大小和一个默认值。

template<typename T>
constexpr auto make_array_n(std::integral_constant<std::size_t, 0>, T &&) {
    return std::array<std::decay_t<T>, 0>{};
}

template<std::size_t size, typename T>
constexpr auto make_array_n(std::integral_constant<std::size_t, size>, T && value) {
    return detail::make_array_n_impl<size>(std::forward<T>(value), std::make_index_sequence<size - 1>{});
}


template<std::size_t size, typename T>
constexpr auto make_array_n(T && value) {
    return make_array_n(std::integral_constant<std::size_t, size>{}, std::forward<T>(value));
}
Run Code Online (Sandbox Code Playgroud)

第三个功能主要是为了方便,所以用户不必std::integral_constant<std::size_t, size>自己构造一个,因为这是一个很罗嗦的构造。真正的工作是由前两个函数之一完成的。

第一个重载非常简单:它构造一个std::array大小为 0 的a 。不需要复制,我们只是构造它。

第二个重载有点棘手。它沿着它作为源获得的值进行转发,并且它还构造了一个实例make_index_sequence并且只是调用了一些其他的实现函数。这个功能是什么样子的?

namespace detail {

template<std::size_t size, typename T, std::size_t... indexes>
constexpr auto make_array_n_impl(T && value, std::index_sequence<indexes...>) {
    // Use the comma operator to expand the variadic pack
    // Move the last element in if possible. Order of evaluation is well-defined
    // for aggregate initialization, so there is no risk of copy-after-move
    return std::array<std::decay_t<T>, size>{ (static_cast<void>(indexes), value)..., std::forward<T>(value) };
}

}   // namespace detail
Run Code Online (Sandbox Code Playgroud)

这通过复制我们传入的值构造了第一个 size - 1 参数。在这里,我们使用可变参数包索引作为扩展。该包中有 size - 1 个条目(正如我们在 的构造中指定的那样make_index_sequence),它们的值为 0、1、2、3、...、size - 2。但是,我们不关心这些值(所以我们将其强制转换为 void,以消除任何编译器警告)。参数包扩展将我们的代码扩展成这样(假设 size == 4):

return std::array<std::decay_t<T>, 4>{ (static_cast<void>(0), value), (static_cast<void>(1), value), (static_cast<void>(2), value), std::forward<T>(value) };
Run Code Online (Sandbox Code Playgroud)

我们使用这些括号来确保可变参数包扩展...扩展我们想要的内容,并确保我们使用逗号运算符。如果没有括号,看起来我们正在向数组初始化传递一堆参数,但实际上,我们正在评估索引,将其转换为 void,忽略该 void 结果,然后返回值,该值被复制到数组中.

我们呼吁的最后一个论点std::forward是一个小优化。如果有人传入一个临时的 std::string 并说“制作一个由 5 个组成的数组”,我们希望有 4 个副本和 1 个移动,而不是 5 个副本。在std::forward我们做这个保证。

完整代码,包括标题和一些单元测试:

#include <array>
#include <type_traits>
#include <utility>

namespace detail {

template<std::size_t size, typename T, std::size_t... indexes>
constexpr auto make_array_n_impl(T && value, std::index_sequence<indexes...>) {
    // Use the comma operator to expand the variadic pack
    // Move the last element in if possible. Order of evaluation is well-defined
    // for aggregate initialization, so there is no risk of copy-after-move
    return std::array<std::decay_t<T>, size>{ (static_cast<void>(indexes), value)..., std::forward<T>(value) };
}

}   // namespace detail

template<typename T>
constexpr auto make_array_n(std::integral_constant<std::size_t, 0>, T &&) {
    return std::array<std::decay_t<T>, 0>{};
}

template<std::size_t size, typename T>
constexpr auto make_array_n(std::integral_constant<std::size_t, size>, T && value) {
    return detail::make_array_n_impl<size>(std::forward<T>(value), std::make_index_sequence<size - 1>{});
}

template<std::size_t size, typename T>
constexpr auto make_array_n(T && value) {
    return make_array_n(std::integral_constant<std::size_t, size>{}, std::forward<T>(value));
}



struct non_copyable {
    constexpr non_copyable() = default;
    constexpr non_copyable(non_copyable const &) = delete;
    constexpr non_copyable(non_copyable &&) = default;
};

int main() {
    constexpr auto array_n = make_array_n<6>(5);
    static_assert(std::is_same<std::decay_t<decltype(array_n)>::value_type, int>::value, "Incorrect type from make_array_n.");
    static_assert(array_n.size() == 6, "Incorrect size from make_array_n.");
    static_assert(array_n[3] == 5, "Incorrect values from make_array_n.");

    constexpr auto array_non_copyable = make_array_n<1>(non_copyable{});
    static_assert(array_non_copyable.size() == 1, "Incorrect array size of 1 for move-only types.");

    constexpr auto array_empty = make_array_n<0>(2);
    static_assert(array_empty.empty(), "Incorrect array size for empty array.");

    constexpr auto array_non_copyable_empty = make_array_n<0>(non_copyable{});
    static_assert(array_non_copyable_empty.empty(), "Incorrect array size for empty array of move-only.");
}
Run Code Online (Sandbox Code Playgroud)


Ste*_*off 5

对于单字节元素数组的情况,可以使用 memset 将所有元素设置为相同的值。

这里有一个例子。


小智 5

将数组初始化为公共值的另一种方法是实际生成一系列定义中的元素列表:

#define DUP1( X ) ( X )
#define DUP2( X ) DUP1( X ), ( X )
#define DUP3( X ) DUP2( X ), ( X )
#define DUP4( X ) DUP3( X ), ( X )
#define DUP5( X ) DUP4( X ), ( X )
.
.
#define DUP100( X ) DUP99( X ), ( X )

#define DUPx( X, N ) DUP##N( X )
#define DUP( X, N ) DUPx( X, N )
Run Code Online (Sandbox Code Playgroud)

可以轻松地将数组初始化为公共值:

#define LIST_MAX 6
static unsigned char List[ LIST_MAX ]= { DUP( 123, LIST_MAX ) };
Run Code Online (Sandbox Code Playgroud)

注意:引入 DUPx 以启用 DUP 参数中的宏替换