关于C#:如何将数组的所有成员初始化为相同的值?

How to initialize all members of an array to the same value?

我在C中有一个大数组(不是C++,如果有区别的话)。我想将所有成员初始化为相同的值。我可以发誓我曾经知道一个简单的方法。在我的例子中,我可以使用memset(),但是否有一种方法可以做到这一点,它是直接内置在C语法中的?


除非该值为0(在这种情况下,可以省略初始值设定项的某些部分相应的元素将被初始化为0),这不是一个简单的方法。

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

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

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

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

因此,这将把所有元素初始化为0:

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

在C++中,空初始化列表也将初始化每个元素到0。C:不允许这样做:

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

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

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

而"0"并不一定意味着"所有位都为零",因此使用上面的比memset()更好更便携。(浮点值将为初始化为+0,指向空值的指针等。)


如果编译器是gcc,则可以使用以下语法:

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

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


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

1
2
3
4
5
6
7
8
9
#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 };

如果需要更改值,则只需在一个位置进行替换。

编辑:可能的有用扩展

(由乔纳森·莱弗勒提供)

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

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

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

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

用于结构或复合数组。

1
2
3
4
5
#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) };

宏名称可以协商。


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

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

编译器将从初始值设定项列表中推导维度。不幸的是,对于多维数组,只能忽略最外层的维度:

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

可以,但是

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

不是。


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

1
2
3
4
5
char* array[] =
{
    [0] ="Hello",
    [1] ="World"
};

如果要创建一个使用枚举作为索引的数组,它将变得特别有用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
enum
{
    ERR_OK,
    ERR_FAIL,
    ERR_MEMORY
};

#define _ITEM(x) [x] = #x

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

这样可以保持事情的有序性,即使您碰巧编写了一些无序的枚举值。

关于这种技术的更多信息可以在这里和这里找到。


1
2
3
4
5
int i;
for (i = 0; i < ARRAY_SIZE; ++i)
{
  myArray[i] = VALUE;
}

我觉得这比

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

如果数组大小发生更改。


您可以像上面详细介绍的那样完成整个静态初始值设定项的工作,但是当数组大小发生变化时(当数组配置项出现变化时,如果不添加适当的额外初始值设定项,则会导致垃圾)。

memset为您提供了一个执行工作的运行时命中,但是没有一个代码大小命中是正确的,对数组大小的更改是免疫的。当数组大于几十个元素时,我几乎在所有情况下都会使用这个解决方案。

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


另一种方法是:

1
2
3
4
5
6
7
8
9
static void
unhandled_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扩展?

上面的代码示例在一个嵌入式系统中,永远不会看到来自另一个编译器的指示灯。


对于初始化"normal"数据类型(如int数组),可以使用括号表示法,但如果数组中仍有空间,则会在最后一个值之后将其归零:

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


有点厚着脸皮的回答;写下陈述

1
array = initial_value

在您最喜欢的支持数组的语言(我的是Fortran,但还有很多其他语言),并将其链接到您的C代码。你可能想把它包装成一个外部函数。


如果数组恰好是int或任何int大小或mem模式的大小正好与int的时间匹配(即所有零或0xa5a5a5 a5),最好的方法是使用memset()。

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


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

  • 初始化数组的第一个元素(常用方法)
  • 复制已设置为未设置的部分的部分,每进行一次复制操作,将大小翻倍。

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

loop runtime 0.004248 [seconds]

memfill() runtime 0.001085 [seconds]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#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]
"
,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]
"
,runTime);
    return 0;
}


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#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 most
                        21,     22,     23,     // basic
                        31,     32,     33  };  // format.

    int a2[][3] =   {   11,     12,     13,     // The first (outer) dimension
                        21,     22,     23,     // may be omitted. The compiler
                        31,     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;
}

输出是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
a11 = 11                a12 = 12                a13 = 13
a21 = 21                a22 = 22                a23 = 23
a31 = 31                a32 = 32                a33 = 33

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

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

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

11      12      13      21      22      23      31      32      33


  • 如果数组声明为静态或全局,则所有元素数组中已经有默认值0。
  • 有些编译器在调试模式下将数组的默认值设置为0。
  • 很容易将默认值设置为0:int数组[10]=0
  • 但是,对于其他值,可以使用memset()或loop;
  • 例子:INT阵列〔10〕;memset(数组,-1,10*sizeof(int));


    简而言之,如果您在编译时启用优化,则不会比这更好:

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

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


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

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

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


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

    1
    2
    3
    4
    5
    int a[4];

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

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    #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 be
        initialized 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(" }
    "
    );
    }

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

    您可以在这里找到一个在线编译器来尝试这个代码。


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #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
    "
    ,a[i]);
       return 0;
    }

    它将给O/P 5 5 5 5 5 5 5……直到整个数组的大小


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

    如果编译器支持折叠表达式,则可以使用模板magic和std::index_sequence生成具有所需值的初始值设定项列表。你甚至可以像个老板一样:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    #include

    /// [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)


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    #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'
    "
    ,i,j,array[i][j].a,array[i][j].b);
    }

    结果:

    1
    2
    3
    4
    5
    6
    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