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


968

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

我可以发誓曾经有一种简单的方法可以做到这一点。我可以memset()在我的情况下使用,但是没有办法直接在C语法中内置吗?


16
到目前为止,没有一个答案提到在C99及更高版本中可行的指定初始化器符号。例如:enum { HYDROGEN = 1, HELIUM = 2, CARBON = 6, NEON = 10, … };struct element { char name[15]; char symbol[3]; } elements[] = { [NEON] = { "Neon", "Ne" }, [HELIUM] = { "Helium", "He" }, [HYDROGEN] = { "Hydrogen", "H" }, [CARBON] = { "Carbon", "C" }, … };。如果删除省略号,这些片段会在C99或C11下编译。
2014年

实际上abelenky的答案是使用指定的初始化程序,但并未完全形成初始化代码
Rob11311,2014年

memset()可以提供帮助,但取决于值。
尼克

Answers:


1238

除非该值为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 不允许这样做:

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

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

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

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


27
通过阅读C ++标准,您还可以执行int array [10] = {};。零初始化。我也没有C标准来检查它是否也是有效的C。
workmad3

54
查看C7.8标准的6.7.8节,似乎没有允许使用一个空的初始化列表。
乔纳森·莱夫勒

7
C99具有许多不错的结构和数组初始化功能;它没有的一个功能(但Fortran IV,1966年拥有)是一种为数组重复特定的初始化程序的方法。
乔纳森·莱夫勒

8
@CetinSert:您什么意思不起作用?它确实按照此答案说的应该做。它不会执行代码中的注释,但是注释是错误的。
本杰明·林德利

9
@CetinSert:您是唯一声明所有元素都将设置为-1的人。正确地,这个答案声称所有未指定的元素都设置为零。您的代码结果符合此声明。
本杰明·林德利

394

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

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

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


12
这种语法会导致已编译二进制文件的文件大小大大增加。对于N = 65536(而不是1024),我的二进制文件从15 KB跳到270 KB!
Cetin Sert

50
@CetinSert编译器必须将65536 ints 添加到静态数据中,即256 K-恰好是您观察到的大小增加。
qrdl

15
@CetinSert为什么?这是一种标准的编译器行为,不特定于指定的初始化程序。如果您静态初始化65536 ints,则int foo1 = 1, foo2 = 1, ..., foo65536 =1;您将获得相同的大小增加。
qrdl

27
更好的是:“ int array [] = {[0 ... 1023] = 5}”,数组的大小将自动设置为1024,修改起来更容易,更安全。
Francois

4
@Francois或2D数组,bool array[][COLS] = { [0...ROWS-1][0...COLS-1] = true}尽管我不确定这比完整的格式更具可读性。
g33kz0r 2013年

178

为了静态初始化具有相同值的大型数组,而无需进行多次复制粘贴,可以使用宏:

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

宏名称可以协商。


12
我只会在极端情况下考虑这一点,肯定会使用memset来表达它。
u0b34a0f6ae

47
如果数据必须是可ROM的,则不能使用memset。
Falken教授的合同

9
预处理器实际上将从#defines生成代码。数组尺寸越大,可执行文件的大小就会越大。但绝对+这个想法;)
列昂尼德(Leonid

7
@Alcott,在旧计算机上以及仍然在许多嵌入式系统上,最终将代码放置在EPROMROM中。ROM-able在嵌入式系统中也意味着“闪存中的代码”,因为它具有大致相同的含义,即无法在运行时写入内存。即不能使用memset或任何其他指令来更新或更改内存。但是,在程序启动之前,常量可以被表达,刷新或ROM化。
Falken教授的合同

4
@ u0b34a0f6ae:请记住,如果VAL_1X不是单个整数而是一个列表,也可以使用此方法。像可迁移状态一样,这也是您要定义EEPROM或闪存的初始化值的嵌入式系统的方法。在这两种情况下,您都不能使用memset()
Martin Scharrer

63

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

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} };

不是。


它是否正确 ?int myPoints[10][] = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9} };
Praveen Gowda IV '04年4

10
否。您省略了最里面的尺寸,这是不允许的。这将导致编译器错误。
Frank Szczerba 2012年

4
初始化程序和长度推断都在C99中引入。
Palec 2014年

3
@Palec:否-自标准C以来(自K&R 1st Edition发布以来,可能还有一段时间),长度推断就一直在C中进行。指定的初始值设定项是C99中的新增功能,但是未使用指定的初始值设定项。
乔纳森·勒夫勒

53

我看到了一些使用此语法的代码:

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

即使您碰巧不按顺序写入一些枚举值,也可以使事情保持秩序。

有关此技术的更多信息,请参见此处此处


8
这是C99初始化程序语法,其他一些答案已经涵盖了该语法。您可以有用地向char const *array[] = { ... };甚至声明char const * const array[] = { ... };,不是吗?
乔纳森·莱夫勒

22
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...

以防阵列大小改变。


12
根据记录,这基本上只是memset(myArray, VALUE, ARRAY_SIZE);
Benson

18
您将如何使用memset将int数组初始化为大于255的某个值?仅当数组为字节大小时,memset才有效。
马特

21
@Benson:在sizeof(int)> sizeof(char)的平台上,无法将以上代码替换为memset。尝试一下。
ChrisWue 2012年

13

您可以按照上面的详细说明进行整个静态初始化程序,但是当数组大小更改时,它可能是一个真正的麻烦(当数组包含符号时​​,如果不添加适当的额外初始化程序,则会产生垃圾)。

memset为您提供了执行工作所需的运行时命中率,但没有正确完成代码大小命中率不受阵列大小更改的影响。当数组大于几十个元素时,几乎在所有情况下都可以使用此解决方案。

如果对数组进行静态声明确实很重要,那么我将编写一个程序来为我编写该程序,并将其作为构建过程的一部分。


您能否在使用memset初始化数组方面添加一些示例?
Sopalajo de Arrierez,

8

这是另一种方式:

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扩展?

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


6

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

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

5

如果数组恰好是int或任何具有int大小的东西,或者您的mem-pattern的大小适合于int的精确时间(即全零或0xA5A5A5A5),则最好的方法是使用memset()

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


5

有点嘲讽的回答;写声明

array = initial_value

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


4

有一种快速的方法可以初始化具有给定值的任何类型的数组。它在大型阵列上效果很好。算法如下:

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

对于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;
}

2
抱歉,这不是事实。也许您忘记了在测试(使用调试模式进行测试?)期间打开编译优化。如果我对此进行测试,则循环几乎总是比memfill快50%(“总是”由于计算机上的某些负载抖动)。并使用memset(a,0,sizeof(a)); 甚至比循环填充快一倍。
RS1980

2
与任何基准测试代码一样,您需要格外小心。添加循环以执行10次计时代码(并将阵列大小加倍至20M),对我而言,这是第一次使用macOS Sierra 10.12.3并使用GCC 6.3.0在MacBook Pro上运行循环大约需要4600 µs,而memfill()代码大约需要1200 µs。但是,在后续迭代中,循环大约需要900-1000 µs,而memfill()代码需要1000-1300 µs。第一次迭代可能会受到填充缓存时间的影响。撤消测试,memfill()第一次速度很慢。
乔纳森·勒夫勒

2

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

#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;
}

输出为:

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

4
<iostream>无效,C因为std::coutstd::cin等是的一部分,std::namespace并且C不支持namespaces。尝试改用<stdio.h>for printf(...)
弗朗西斯·库格勒

2

贯穿所有讨论,简短的答案是,如果在编译时启用优化,那么您将做得更好:

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

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


7
该问题专门要求初始化。显然,这不是初始化,而是初始化完成分配。可能会立即完成,但仍未初始化。
安迪

对于多次调用的函数中的大型静态查找表完全没有帮助。
马丁·邦纳

...不要回想起原来问题一部分的函数中的静态查找表-保持简单。就是说,@ Community可能将其钉牢。
JWDN

1
  1. 如果将数组声明为静态或全局数组,则数组中的所有元素都已经具有默认默认值0。
  2. 一些编译器在调试模式下将数组的默认值设置为0。
  3. 容易将默认设置为0:int array [10] = {0};
  4. 但是,对于其他值,您可以使用memset()或loop;

例如:int array [10]; memset(array,-1,10 * sizeof(int));


0
#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 ......直到整个数组的大小


0

我知道用户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 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( " }\n" );
}

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


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


0

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

int a[4];

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

0

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

如果您的编译器支持fold expressions,则可以使用template magic并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中)


-1

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

#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


1
我不确定这是否是解决方案。我不确定这是否sizeof(void)有效。
克里斯·卢兹

3
没用 只有前两个被初始化,其余的都未被初始化。我在Mac OS X 10.4上使用GCC 4.0。
dreamlax

因为第二个中的源数据memcpy()与目标空间重叠,所以调用了未定义的行为。使用的天真的实现memcpy(),它可能会起作用,但是并不需要系统使其起作用。
乔纳森·勒夫勒

-1

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

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

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


那将无法可靠地工作。恕我直言,标准应提供类似的功能,memcpy但在重叠的情况下应指定自下而上或自上而下的复制顺序,但事实并非如此。
超级猫

就像我说的那样,这只是我们做不到的事情,但是那时候,我们更加关注效率,而不是避免使用未记录的功能。尽管向前复制内存效率更高,但规范中没有任何内容表明不能向后复制,以随机顺序或将其拆分到多个线程中。memmove()提供了无冲突复制的功能。
迈克

这等效于另一个答案中的代码,并且存在缺陷。使用memmove()不起作用。
乔纳森·莱夫勒

-2

如果您的意思是并行的,那么我认为将逗号运算符与表达式结合使用可以做到这一点:

a[1]=1, a[2]=2, ..., a[indexSize]; 

或者,如果您的意思是在单个结构中,则可以在for循环中执行此操作:

for(int index = 0, value = 10; index < sizeof(array)/sizeof(array[0]); index++, value--)
  array[index] = index;

///请注意,参数列表中的逗号运算符不是上述并行运算符;

您可以初始化一个数组清理:

array[] = {1, 2, 3, 4, 5};

您可以调用malloc / calloc / sbrk / alloca / etc来为对象分配固定的存储区域:

int *array = malloc(sizeof(int)*numberOfListElements/Indexes);

并通过以下方式访问成员:

*(array + index)

等等。


逗号运算符名义上可以保证从左到右的评估。如果表达式中没有副作用,则可以并行化操作,但是编译器通常不这样做。
乔纳森·莱夫勒
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.