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

1160

我在 C 中有一个大型数组(如果这有区别的话,不是 C++)。我希望将所有成员初始化为相同的值。

我记得曾经知道一种简单的方法来做到这一点。虽然我可以在我的情况下使用 memset(),但是否有一种内置于 C 语法中的方法来实现这一点呢?


25
迄今为止,没有任何答案提到在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下编译通过。 - Jonathan Leffler
实际上,abelenky的答案使用了指定初始化器,但并不是完全形成的初始化代码。 - Rob11311
memset()可以帮助,但取决于数值。 - Nick
2
memset()特定讨论:https://dev59.com/qmw05IYBdhLWcg3wfx80 我认为它只适用于0。 - Ciro Santilli OurBigBook.com
@Nick memset() 只适用于值为 0UINT_MAX 和元素大小为 char 的数组。 - user16217248
1
@user16217248 也适用于“循环”数字,例如0x1212、0x0303等。;) 但是是的,大多数情况下您需要零。 - Nick
27个回答

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
有一种快速的方法可以使用给定值初始化任何类型的数组。它非常适用于大型数组。算法如下:
  • 初始化数组的第一个元素(通常方式)
  • 将已设置部分复制到未设置部分,每次复制操作时加倍大小

对于包含1 000 000个元素的int数组,与常规循环初始化相比,它的速度快4倍(i5,2核,2.3 GHz,4GiB内存,64位):

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));甚至比loopfill快两倍。 - RS1980
3
与任何基准测试代码一样,你需要非常小心。将循环添加到执行计时代码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()的首次运行较慢。 - Jonathan Leffler

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

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


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

上面的代码可以正常工作,但请确保...之间没有空格。

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语言不支持命名空间。建议使用<stdio.h>头文件中的printf(...)函数代替。 - Francis Cugler

2

不废话,简短回答是:如果你在编译时打开了优化选项,那么你不可能做得比这更好:

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

额外的好处是:代码实际上是可读的 :)


9
这个问题特别要求进行初始化。这显然不是初始化,而是在初始化之后进行的赋值。它可能会立即执行,但仍然不是初始化。 - Andy
在一个被多次调用的函数中,对于一个大的静态查找表来说完全没有帮助。 - Martin Bonner supports Monica
不记得在函数内部使用静态查找表是原始问题的一部分 - 保持简单。话虽如此,@Community可能已经解决了它。 - JWDN

1

我知道用户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];在声明时不会直接初始化,但下一个代码块或函数调用会将数组的每个元素初始化为您想要的相同值。另一个注意事项是,您必须为每个您支持的类型编写一个初始化函数,并且还必须修改printArray()函数以支持这些类型。


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

1

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

int a[4];

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

1

要用零初始化 -

  char arr[1000] = { 0 };

最好使用普通的“for循环”来进行初始化,而不是0。
  char arr[1000];
  for(int i=0; i<arr.size(); i++){
     arr[i] = 'A';
  }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接