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

时间:2009-06-30 20:10:25

标签: c++ c arrays initialization default-value

C++ Notes: Array Initialization有一个很好的列表初始化列表。我有一个

int array[100] = {-1};

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

代码

int array[100] = {0};

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

我在这里缺少什么..如果值不为零,不能初始化它吗?

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

13 个答案:

答案 0 :(得分:320)

使用您使用的语法

int array[100] = {-1};

说“将第一个元素设置为-1,其余元素设置为0”,因为所有省略的元素都设置为0

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

std::fill_n(array, 100, -1);

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

答案 1 :(得分:127)

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

int array[100] = { [0 ... 99] = -1 };

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

这称为“指定初始值设定项”,请参阅here以获取更多信息。

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

答案 2 :(得分:31)

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

  

如果是显式数组大小   指定但更短   初始化列表是指定的   未指定的元素设置为零。

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

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

int array[100] = {0};

简单告诉编译器“将这100个int设置为零”,编译器可以自由优化。

for (int i = 0; i < 100; ++i){
  array[i] = 0;
}

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

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

std::fill(array, array+100, 42); // sets every value in the array to 42

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

答案 3 :(得分:10)

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

std::array<int, 100> a;
a.fill(-1);

答案 4 :(得分:9)

使用{}分配声明的元素;其余部分用0初始化。

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

答案 5 :(得分:8)

您关联的页面

  

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

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

答案 6 :(得分:4)

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

#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 )

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

#define LIST_MAX 6
static unsigned char List[ LIST_MAX ]= { DUP( 123, LIST_MAX ) };

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

答案 7 :(得分:3)

使用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));
}

第三个功能主要是为了方便,因此用户不必自己构造std::integral_constant<std::size_t, size>,因为这是一个非常冗长的构造。真正的工作是由前两个功能之一完成的。

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

第二次重载有点棘手。它沿着它作为源获得的值转发,并且它还构造了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

这通过复制我们传入的值来构造第一个size-1参数。这里,我们使用variadic参数包索引作为扩展的东西。有大小 - 该包中的1个条目(正如我们在make_index_sequence的构造中指定的那样),它们的值为0,1,2,3,...,大小 - 2.但是,我们没有关心价值观(因此我们将其转为无效,以消除任何编译器警告)。参数包扩展将我们的代码扩展为类似的东西(假设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) };

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

最后一个参数,即我们称之为std::forward的参数,是一个小优化。如果有人传入一个临时的std :: string并说&#34;制作一个包含其中5个&#34;的数组,我们希望有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.");
}

答案 8 :(得分:2)

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

有一个例子here

答案 9 :(得分:1)

1)当您使用初始化程序时,对于结构或类似的数组,未指定的值基本上是默认构造的。在像int这样的基本类型的情况下,这意味着它们将被置零。请注意,这适用于递归:您可以拥有包含数组的结构数组,如果只指定第一个结构的第一个字段,那么所有其余结构将使用零和默认构造函数进行初始化。

2)编译器可能会生成初始化代码,该代码至少与您手动完成的一样好。在可能的情况下,我倾向于让编译器为我做初始化。

答案 10 :(得分:1)

在C ++中,也可以使用元编程和可变参数模板。以下文章介绍了如何执行此操作:Programmatically create static arrays at compile time in C++

答案 11 :(得分:0)

在C ++编程语言V4中,Stroustrup建议在内置数组上使用向量或valarray。使用valarrary,在创建它们时,可以将它们初始化为特定值,如:

valarray <int>seven7s=(7777777,7);

使用“7777777”初始化数组7个成员。

这是使用C ++数据结构而不是“普通旧C”数组实现答案的C ++方法。

我转而使用valarray尝试使用C ++'isms v.C'isms ....

答案 12 :(得分:-4)

应该是一个标准功能但由于某些原因它不包含在标准C和C ++中......

#include <stdio.h>

 __asm__
 (
"    .global _arr;      "
"    .section .data;    "
"_arr: .fill 100, 1, 2; "
 );

extern char arr[];

int main() 
{
    int i;

    for(i = 0; i < 100; ++i) {
        printf("arr[%u] = %u.\n", i, arr[i]);
    }
}

在Fortran你可以做到:

program main
    implicit none

    byte a(100)
    data a /100*2/
    integer i

    do i = 0, 100
        print *, a(i)
    end do
end

但它没有无符号数字......

为什么C / C ++不能实现它。这真的很难吗?手动编写以达到相同的结果是如此愚蠢......

#include <stdio.h>
#include <stdint.h>

/* did I count it correctly? I'm not quite sure. */
uint8_t arr = {
    2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
    2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
    2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
    2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
    2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
    2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
    2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
    2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
    2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
    2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
};    

int main() 
{
    int i;

    for(i = 0; i < 100; ++i) {
        printf("arr[%u] = %u.\n", i, arr[i]);
    }
}

如果它是一个1,000,00字节的数组怎么办?我需要编写一个脚本来为我编写它,或者使用程序集等等。这是无稽之谈。

它非常便于携带,没有理由不使用该语言。

只需将其破解为:

#include <stdio.h>
#include <stdint.h>

/* a byte array of 100 twos declared at compile time. */
uint8_t twos[] = {100:2};

int main()
{
    uint_fast32_t i;
    for (i = 0; i < 100; ++i) {
        printf("twos[%u] = %u.\n", i, twos[i]);
    }

    return 0;
}

攻击它的一种方法是通过预处理......(下面的代码不包括边缘情况,但是为了快速演示可以做什么而编写。)

#!/usr/bin/perl
use warnings;
use strict;

open my $inf, "<main.c";
open my $ouf, ">out.c";

my @lines = <$inf>;

foreach my $line (@lines) {
    if ($line =~ m/({(\d+):(\d+)})/) {
        printf ("$1, $2, $3");        
        my $lnew = "{" . "$3, "x($2 - 1) . $3 . "}";
        $line =~ s/{(\d+:\d+)}/$lnew/;
        printf $ouf $line;
    } else {
        printf $ouf $line;
    }
}

close($ouf);
close($inf);