用一个默认值初始化正常数组
C ++注释:数组初始化在数组初始化上有一个很好的列表。 我有一个
int array[100] = {-1};
期望它满-1,但不是,只有第一个值,剩下的是0和随机值混合在一起。
代码
int array[100] = {0};
工作得很好,并将每个元素设置为0。
我在这里错过了什么?如果值不是零,不能初始化它吗?
2:默认的初始化(如上)比通过整个数组的通常的循环更快,并分配一个值或做同样的事情?
使用你使用的语法,
int array[100] = {-1};
说“设置第一个元素为-1
,其余为0
”,因为所有的省略元素都设置为0
。
在C ++中,要将它们全部设置为-1
,可以使用类似std::fill_n
(来自<algorithm>
):
std::fill_n(array, 100, -1);
在便携式的C,你必须推出自己的循环。 有编译器扩展,或者你可以依赖实现定义的行为作为一个快捷方式,如果这是可以接受的。
有一个gcc编译器的扩展,它允许语法:
int array[100] = { [0 ... 99] = -1 };
这将设置所有的元素为-1。
这就是所谓的“指定初始化器” 在这里看到更多的信息。
注意这不是为gcc c ++编译器实现的。
你链接到的页面已经给了第一部分的答案:
如果指定了明确的数组大小,但指定了更短的初始化列表,则未指定的元素将设置为零。
没有内置的方法来将整个数组初始化为某个非零值。
至于哪一个更快,通常的规则是这样的:“给编译器最大自由的方法可能更快”。
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.你不要说它应该完成的顺序或其他任何事情。
您链接的页面状态
如果指定了明确的数组大小,但指定了更短的初始化列表,则未指定的元素将设置为零。
速度问题:对于这么小的数组,任何差异都可以忽略不计。 如果使用大型数组并且速度比size更重要,那么可以有一个默认值的const数组(在编译时初始化),然后将它们memcpy
到可修改的数组中。
C ++ 11有另一个(不完美的)选项:
std::array<int, 100> a; a.fill(-1);
用{}分配声明的元素; 其余的都是用0初始化的。
如果不存在= {}
来初始化,则内容未定义。
将数组初始化为一个公共值的另一种方法是实际生成一系列定义中的元素列表:
#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
1)当你使用一个初始化器时,对于一个像这样的结构或数组,未指定的值实质上是默认构造的。 在像int这样的基本类型的情况下,这意味着它们将被归零。 请注意,这是递归的:你可以有一个包含数组的结构体的数组,如果你只指定了第一个结构体的第一个字段,那么所有剩下的将用零和默认的构造函数初始化。
2)编译器可能会生成初始化代码,至少和你手动执行的一样好。 如果可能,我倾向于让编译器为我做初始化。
使用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
这通过复制我们传入的值来构造第一个大小-1的参数。在这里,我们使用可变参数包索引来扩展。 在这个包中有大小为1的条目(正如我们在make_index_sequence
的构造中指定的make_index_sequence
),它们的值为0,1,2,3,…,size – 2。但是,我们不关心值(所以我们把它放弃,以消除任何编译器警告)。 参数包扩展扩展了我们的代码,像这样(假定大小== 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并且说“创建一个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."); }
对于单字节元素数组的情况,可以使用memset将所有元素设置为相同的值。
这里有一个例子。
在C ++中,也可以使用元编程和可变参数模板。 下面的文章演示了如何做到这一点:以编程方式在C ++中以编程方式创建静态数组 。
在C ++编程语言V4中,Stroustrup建议在内建数组上使用向量或变量。 随着valarrary的,当你创建它们,你可以启动他们到一个特定的值,如:
valarray <int>seven7s=(7777777,7);
用“7777777”初始化7个成员的数组。
这是使用C ++数据结构而不是“普通的旧C”数组来实现答案的C ++方式。
我切换到使用valarray作为尝试在我的代码尝试使用C ++'isms诉C'isms ….
应该是一个标准功能,但由于某种原因,它不包含在标准的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);