如何将数组的所有成员初始化为相同的值?

我在C中有一个大数组(如果有区别,则不是C++)。我想初始化相同值的所有成员。

我可以发誓我曾经知道一个简单的方法来做到这一点。在我的情况下,我可以使用memset(),但没有一种方法可以直接构建在C语法中吗?

2171347 次浏览

除非该值为0(在这种情况下,您可以省略初始化器的某些部分并且相应的元素将被初始化为0),没有简单的方法。

但不要忽视显而易见的解决方案:

int myArray[10] = { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 };

缺少值的元素将被初始化为0:

int myArray[10] = { 1, 2 }; // initialize to 1,2,0,0,0...

所以这将所有元素初始化为0:

int myArray[10] = { 0 }; // all elements 0

在C++,一个空的初始化列表也会将每个元素初始化为0。这是不允许,C直到C23:

int myArray[10] = {}; // all elements 0 in C++ and C23

请记住,如果没有,具有静态存储持续时间的对象将初始化为0指定初始化器:

static int myArray[10]; // all elements 0

而“0”并不一定意味着“所有位-零”,因此使用上面的是比memset()更好,更可移植。(浮点值将是初始化为+0,指向空值的指针等)

对于初始化“正常”数据类型(如int数组),您可以使用括号表示法,但如果数组中仍有空间,它将在最后一个之后的值为零:

// put values 1-8, then two zeroesint list[10] = {1,2,3,4,5,6,7,8};

你可以像上面详述的那样做整个静态初始化器,但是当你的数组大小发生变化时(当你的数组变大时,如果你不添加适当的额外初始化器,你会得到垃圾),这可能是一个真正的麻烦。

memset为您提供了执行这项工作的运行时命中,但没有正确完成的代码大小命中不受数组大小变化的影响。当数组大于几十个元素时,我几乎在所有情况下都会使用这个解决方案。

如果静态声明数组真的很重要,我会写一个程序来为我编写程序,并使其成为构建过程的一部分。

int i;for (i = 0; i < ARRAY_SIZE; ++i){myArray[i] = VALUE;}

我觉得这比

int myArray[10] = { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5...

incase数组的大小发生变化。

如果数组恰好是int或任何大小为int的东西,或者您的mem模式的大小适合一个int(即全零或0xA5A5A5A5),最好的方法是使用memset()

否则,在移动索引的循环中调用memcpy()。

如果您想确保数组的每个成员都被显式初始化,只需从声明中省略维度:

int myArray[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

编译器将从初始化器列表中推断出维度。不幸的是,对于多维数组,只有最外层的维度可以省略:

int myPoints[][3] = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9} };

可以,但是

int myPoints[][] = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9} };

不是。

这里有另一种方法:

static voidunhandled_interrupt(struct trap_frame *frame, int irq, void *arg){//this code intentionally left blank}
static struct irqtbl_s vector_tbl[XCHAL_NUM_INTERRUPTS] = {[0 ... XCHAL_NUM_INTERRUPTS-1] {unhandled_interrupt, NULL},};

见:

C扩展

指定inits

然后问一个问题:什么时候可以使用C扩展?

上面的代码示例位于嵌入式系统中,永远不会看到其他编译器的光。

如果您的编译器是GCC,您可以使用以下“GNU扩展”语法:

int array[1024] = {[0 ... 1023] = 5};

查看详细描述:http://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Designated-Inits.html

我在问题中没有看到任何要求,因此解决方案必须是通用的:初始化一个未指定的可能多维数组,该数组由具有初始成员值的未指定可能结构元素构建:

#include <string.h>
void array_init( void *start, size_t element_size, size_t elements, void *initval ){memcpy(        start,              initval, element_size              );memcpy( (char*)start+element_size, start,   element_size*(elements-1) );}
// testing#include <stdio.h>
struct s {int a;char b;} array[2][3], init;
int main(){init = (struct s){.a = 3, .b = 'x'};array_init( array, sizeof(array[0][0]), 2*3, &init );
for( int i=0; i<2; i++ )for( int j=0; j<3; j++ )printf("array[%i][%i].a = %i .b = '%c'\n",i,j,array[i][j].a,array[i][j].b);}

结果:

array[0][0].a = 3 .b = 'x'array[0][1].a = 3 .b = 'x'array[0][2].a = 3 .b = 'x'array[1][0].a = 3 .b = 'x'array[1][1].a = 3 .b = 'x'array[1][2].a = 3 .b = 'x'

编辑:start+element_size更改为(char*)start+element_size

对于静态初始化具有相同值的大数组,无需多次复制粘贴,您可以使用宏:

#define VAL_1X     42#define VAL_2X     VAL_1X,  VAL_1X#define VAL_4X     VAL_2X,  VAL_2X#define VAL_8X     VAL_4X,  VAL_4X#define VAL_16X    VAL_8X,  VAL_8X#define VAL_32X    VAL_16X, VAL_16X#define VAL_64X    VAL_32X, VAL_32X
int myArray[53] = { VAL_32X, VAL_16X, VAL_4X, VAL_1X };

如果您需要更改值,则必须仅在一个位置进行替换。

编辑:可能有用的扩展

(由Jonathan Leffler提供)

你可以很容易地将其概括为:

#define VAL_1(X) X#define VAL_2(X) VAL_1(X), VAL_1(X)/* etc. */

可以使用以下方法创建变体:

#define STRUCTVAL_1(...) { __VA_ARGS__ }#define STRUCTVAL_2(...) STRUCTVAL_1(__VA_ARGS__), STRUCTVAL_1(__VA_ARGS__)/*etc */

它适用于结构或复合数组。

#define STRUCTVAL_48(...) STRUCTVAL_32(__VA_ARGS__), STRUCTVAL_16(__VA_ARGS__)
struct Pair { char key[16]; char val[32]; };struct Pair p_data[] = { STRUCTVAL_48("Key", "Value") };int a_data[][4] = { STRUCTVAL_48(12, 19, 23, 37) };

宏名称是可协商的。

略带开玩笑的回答;写陈述

array = initial_value

使用您最喜欢的支持数组的语言(我的是Fortran,但还有很多其他语言),并将其链接到您的C代码。您可能希望将其包装为外部函数。

我看到一些使用这种语法的代码:

char* array[] ={[0] = "Hello",[1] = "World"};

它变得特别有用的地方是,如果您正在制作一个使用枚举作为索引的数组:

enum{ERR_OK,ERR_FAIL,ERR_MEMORY};
#define _ITEM(x) [x] = #x
char* array[] ={_ITEM(ERR_OK),_ITEM(ERR_FAIL),_ITEM(ERR_MEMORY)};

这使事情保持有序,即使您碰巧将一些枚举值按顺序编写。

有关此技术的更多信息,请参阅这里这里

有一种快速的方法可以使用给定的值初始化任何类型的数组。它适用于大型数组。算法如下:

  • 初始化数组的第一个元素(通常的方式)
  • 将已设置的部分复制到未设置的部分,每次复制操作将大小加倍

对于1 000 000元素int阵列,它比常规循环初始化(i5,2核,2.3 GHz,4GiB内存,64位)快4倍:

loop runtime 0.004248 [seconds]

memfill() runtime 0.001085 [seconds]


#include <stdio.h>#include <time.h>#include <string.h>#define ARR_SIZE 1000000
void memfill(void *dest, size_t destsize, size_t elemsize) {char   *nextdest = (char *) dest + elemsize;size_t movesize, donesize = elemsize;
destsize -= elemsize;while (destsize) {movesize = (donesize < destsize) ? donesize : destsize;memcpy(nextdest, dest, movesize);nextdest += movesize; destsize -= movesize; donesize += movesize;}}int main() {clock_t timeStart;double  runTime;int     i, a[ARR_SIZE];
timeStart = clock();for (i = 0; i < ARR_SIZE; i++)a[i] = 9;runTime = (double)(clock() - timeStart) / (double)CLOCKS_PER_SEC;printf("loop runtime %f [seconds]\n",runTime);
timeStart = clock();a[0] = 10;memfill(a, sizeof(a), sizeof(a[0]));runTime = (double)(clock() - timeStart) / (double)CLOCKS_PER_SEC;printf("memfill() runtime %f [seconds]\n",runTime);return 0;}
  1. 如果数组被声明为静态或全局,则所有元素在数组中已经有默认默认值0。
  2. 一些编译器在调试模式下将数组的默认值设置为0。
  3. 很容易将默认值设置为0:int数组[10]={0};
  4. 但是,对于其他值,您可以使用memset()或loop;

例子:int数组[10];memset(数组,-1,10*sizeof(int));

没有人提到访问初始化数组元素的索引顺序。我的示例代码将给出一个说明性示例。

#include <iostream>
void PrintArray(int a[3][3]){std::cout << "a11 = " << a[0][0] << "\t\t" << "a12 = " << a[0][1] << "\t\t" << "a13 = " << a[0][2] << std::endl;std::cout << "a21 = " << a[1][0] << "\t\t" << "a22 = " << a[1][1] << "\t\t" << "a23 = " << a[1][2] << std::endl;std::cout << "a31 = " << a[2][0] << "\t\t" << "a32 = " << a[2][1] << "\t\t" << "a33 = " << a[2][2] << std::endl;std::cout << std::endl;}
int wmain(int argc, wchar_t * argv[]){int a1[3][3] =  {   11,     12,     13,     // The most21,     22,     23,     // basic31,     32,     33  };  // format.
int a2[][3] =   {   11,     12,     13,     // The first (outer) dimension21,     22,     23,     // may be omitted. The compiler31,     32,     33  };  // will automatically deduce it.
int a3[3][3] =  {   {11,    12,     13},    // The elements of each{21,    22,     23},    // second (inner) dimension{31,    32,     33} };  // can be grouped together.
int a4[][3] =   {   {11,    12,     13},    // Again, the first dimension{21,    22,     23},    // can be omitted when the{31,    32,     33} };  // inner elements are grouped.
PrintArray(a1);PrintArray(a2);PrintArray(a3);PrintArray(a4);
// This part shows in which order the elements are stored in the memory.int * b = (int *) a1;   // The output is the same for the all four arrays.for (int i=0; i<9; i++){std::cout << b[i] << '\t';}
return 0;}

输出是:

a11 = 11                a12 = 12                a13 = 13a21 = 21                a22 = 22                a23 = 23a31 = 31                a32 = 32                a33 = 33
a11 = 11                a12 = 12                a13 = 13a21 = 21                a22 = 22                a23 = 23a31 = 31                a32 = 32                a33 = 33
a11 = 11                a12 = 12                a13 = 13a21 = 21                a22 = 22                a23 = 23a31 = 31                a32 = 32                a33 = 33
a11 = 11                a12 = 12                a13 = 13a21 = 21                a22 = 22                a23 = 23a31 = 31                a32 = 32                a33 = 33
11      12      13      21      22      23      31      32      33

通过所有的喋喋不休,简短的答案是,如果你在编译时打开优化,你不会做得比这更好:

int i,value=5,array[1000];for(i=0;i<1000;i++) array[i]=value;

额外的好处:代码实际上是清晰的:)

#include<stdio.h>int main(){int i,a[50];for (i=0;i<50;i++){a[i]=5;// set value 5 to all the array index}for (i=0;i<50;i++)printf("%d\n",a[i]);return 0;}

它将给出o/p 5 5 5 5 5 5……直到整个数组的大小

我知道用户Tarski以类似的方式回答了这个问题,但我添加了更多细节。原谅我的一些C,因为我更倾向于使用C++,所以我有点生疏,但它来了。


如果您提前知道数组的大小…

#include <stdio.h>
typedef const unsigned int cUINT;typedef unsigned int UINT;
cUINT size = 10;cUINT initVal = 5;
void arrayInitializer( UINT* myArray, cUINT size, cUINT initVal );void printArray( UINT* myArray );
int main() {UINT myArray[size];/* Not initialized during declaration but can beinitialized using a function for the appropriate TYPE*/arrayInitializer( myArray, size, initVal );
printArray( myArray );
return 0;}
void arrayInitializer( UINT* myArray, cUINT size, cUINT initVal ) {for ( UINT n = 0; n < size; n++ ) {myArray[n] = initVal;}}
void printArray( UINT* myArray ) {printf( "myArray = { " );for ( UINT n = 0; n < size; n++ ) {printf( "%u", myArray[n] );
if ( n < size-1 )printf( ", " );}printf( " }\n" );}

上面有一些警告;一个是UINT myArray[size];在声明时不会直接初始化,但是下一个代码块或函数调用确实会将数组的每个元素初始化为您想要的相同值。另一个警告是,您必须为您将支持的每个type编写一个initializing function,并且您还必须修改printArray()函数以支持这些类型。


您可以使用找到的在线编译器尝试此代码这里

对于延迟初始化(即类成员构造函数初始化),请考虑:

int a[4];
unsigned int size = sizeof(a) / sizeof(a[0]);for (unsigned int i = 0; i < size; i++)a[i] = 0;

回到过去(我不是说这是个好主意),我们会设置第一个元素,然后:

memcpy (&element [1], &element [0], sizeof (element)-sizeof (element [0]);

甚至不确定它是否还能工作(这取决于memcpy的实现),但它通过反复将初始元素复制到下一个元素来工作——甚至适用于结构数组。

我知道最初的问题明确提到了C而不是C++,但是如果你(像我一样)来这里寻找C++数组的解决方案,这里有一个巧妙的技巧:

如果你的编译器支持折叠表达式,你可以使用模板魔法和std::index_sequence来生成一个初始化器列表,其中包含你想要的值。你甚至可以constexpr它,感觉自己像个老板:

#include <array>
/// [3]/// This functions's only purpose is to ignore the index given as the second/// template argument and to always produce the value passed in.template<class T, size_t /*ignored*/>constexpr T identity_func(const T& value) {return value;}
/// [2]/// At this point, we have a list of indices that we can unfold/// into an initializer list using the `identity_func` above.template<class T, size_t... Indices>constexpr std::array<T, sizeof...(Indices)>make_array_of_impl(const T& value, std::index_sequence<Indices...>) {return {identity_func<T, Indices>(value)...};}
/// [1]/// This is the user-facing function./// The template arguments are swapped compared to the order used/// for std::array, this way we can let the compiler infer the type/// from the given value but still define it explicitly if we want to.template<size_t Size, class T>constexpr std::array<T, Size>make_array_of(const T& value) {using Indices = std::make_index_sequence<Size>;return make_array_of_impl(value, Indices{});}
// std::array<int, 4>{42, 42, 42, 42}constexpr auto test_array = make_array_of<4/*, int*/>(42);static_assert(test_array[0] == 42);static_assert(test_array[1] == 42);static_assert(test_array[2] == 42);static_assert(test_array[3] == 42);// static_assert(test_array[4] == 42); out of bounds

你可以看看工作中的代码(在Wandbox)

如果事先知道数组的大小,可以使用Boost预处理器C_ARRAY_INITIALIZE宏为您完成肮脏的工作:

#include <boost/preprocessor/repetition/enum.hpp>#define C_ARRAY_ELEMENT(z, index, name) name[index]#define C_ARRAY_EXPAND(name,size) BOOST_PP_ENUM(size,C_ARRAY_ELEMENT,name)#define C_ARRAY_VALUE(z, index, value) value#define C_ARRAY_INITIALIZE(value,size) BOOST_PP_ENUM(size,C_ARRAY_VALUE,value)
int array[1024] = {[0 ... 1023] = 5};

如上所述工作正常,但请确保...点之间没有空格。

方法一:

int a[5] = {3,3,3,3,3};

形式初始化技术。

方法二:

int a[100] = {0};

但值得注意的是

int a[10] = {1};

不会将所有值初始化为1

这种初始化方式专门用于0

如果你只是做

int a[100];

一些编译器倾向于采用垃圾值,因此它总是更喜欢这样做

int a[1000] = {0};

用零初始化-

  char arr[1000] = { 0 };

最好使用普通的“for loop”进行初始化,而不是0。

  char arr[1000];for(int i=0; i<arr.size(); i++){arr[i] = 'A';}

就像克莱门斯·西拉夫的回答一样。这个版本需要17C++。

template <size_t Cnt, typename T>std::array<T, Cnt> make_array_of(const T& v){return []<size_t... Idx>(std::index_sequence<Idx...>, const auto& v){auto identity = [](const auto& v, size_t) { return v; };return std::array{identity(v, Idx)...};}(std::make_index_sequence<Cnt>{}, v);}

你可以在这里中看到它。