首页 文章

使用一个默认值初始化普通数组[重复]

提问于
浏览
206

这个问题在这里已有答案:

C++ Notes: Array Initialization有一个关于数组初始化的很好的列表 . 我有一个

int array[100] = {-1};

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

代码

int array[100] = {0};

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

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

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

13 回答

  • 1

    使用您使用的语法,

    int array[100] = {-1};
    

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

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

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

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

  • 8

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

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

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

    这被称为"Designated Initializers",请参阅here以获取更多信息 .

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

  • 8

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

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

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

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

    int array[100] = {0};
    

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

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

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

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

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

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

  • 125

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

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

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

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

  • -4

    您链接的页面指出

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

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

  • 9

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

    #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

  • 30

    使用 std::array ,我们可以在C14中以相当简单的方式执行此操作 . 可以仅在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,...,size - 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并说"make an array of 5 of these",我们希望有4个副本和1个移动,而不是5个副本 . std::forward 确保我们这样做 .

    完整的代码,包括 Headers 和一些单元测试:

    #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.");
    }
    
  • 1

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

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

  • 4

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

    有一个例子here .

  • 1

    在C中,还可以使用元编程和可变参数模板 . 以下文章说明了如何操作:Programmatically create static arrays at compile time in C++ .

  • 304

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

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

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

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

    我转而使用valarray作为我的代码尝试使用C'isms v.C'isms ....

  • 3

    应该是标准功能但由于某种原因它不包含在标准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);
    

相关问题