Как инициализировать все члены массива одним и тем же значением?

У меня есть большой массив в C (не c++, если это имеет значение). Я хочу инициализировать все члены с одинаковым значением. Я мог бы поклясться, что когда-то знал простой способ сделать это. Я мог бы использовать memset() в моем случае, но нет ли способа сделать это, который встроен прямо в синтаксис C?

18 ответов


если это значение не равно 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, указатели на значение null и т. д.)


Если ваш компилятор GCC вы можете использовать следующий синтаксис:

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

проверить подробное описание: http://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Designated-Inits.html


для статической инициализации большого массива с одинаковым значением, без многократного копирования-вставки, можно использовать макросы:

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

Если вам нужно изменить значение, то вы должны сделать замену на только одном месте.

Edit: возможные полезные расширения

(предоставлено Джонатан Леффлер)

вы можете легко обобщить это:

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

имена макросов договорная.


Если вы хотите убедиться, что каждый член массива явно инициализирован, просто опустите измерение из объявления:

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

нет.


Я видел код, который использовал этот синтаксис:

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

это держит вещи в порядке, даже если вы случайно пишете некоторые из значений перечисления из порядка.

подробнее об этой технике можно найти здесь и здесь.


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 размер массива изменяется.


вы можете сделать всю статическую вещь инициализатора, как описано выше, но это может быть настоящий облом, когда ваш размер массива изменяется (когда Ваш массив embiggens, если вы не добавляете соответствующие дополнительные инициализаторы, вы получаете мусор).

memset дает вам хит времени выполнения для выполнения работы, но ни один размер кода не хит сделано правильно невосприимчив к изменениям размера массива. Я бы использовал это решение почти во всех случаях, когда массив был больше, чем, скажем, несколько десятков элементов.

Если бы это было очень важно, чтобы массив был статически объявлен, я бы написал программу, чтобы написать программу для меня и сделать ее частью процесса сборки.


вот еще один способ:

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

посмотреть:

С-С Расширениями

обозначенные inits

затем задайте вопрос: когда можно использовать расширения C?

приведенный выше пример кода находится во встроенной системе и никогда не увидит свет от другого компилятора.


для инициализации "нормальных" типов данных (например, массивов int) вы можете использовать скобочную нотацию, но она будет обнулять значения после последнего, если в массиве все еще есть пространство:

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

слегка насмешливый ответ; напишите заявление

array = initial_value

на вашем любимом языке с поддержкой массива (мой-Fortran, но есть много других) и свяжите его с вашим кодом C. Вероятно, вы захотите обернуть его внешней функцией.


Если массив оказывается int или что-либо с размером int или размером вашего mem-шаблона вписывается точное время в int (т. е. все нули или 0xA5A5A5A5), лучший способ-использовать memset ().

в противном случае вызовите memcpy() в цикле, перемещающем индекс.


существует быстрый способ инициализации массива любого типа с заданным значением. Он очень хорошо работает с большими массивами. Алгоритм выглядит следующим образом:

  • инициализировать первый элемент массива (обычным способом)
  • копировать часть, которая была установлена в часть, которая не была установлена, удваивая размер с каждой следующей операцией копирования

на 1 000 000 элементов int массив это в 4 раза быстрее, чем инициализация регулярного цикла (i5, 2 ядра, 2,3 ГГц, Память 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;
}

никто не упомянул порядок индекса для доступа к элементам инициализированного массива. Мой пример кода даст ему иллюстративный пример.

#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

  1. если Ваш массив объявлен статическим или глобальным, все элементы в массиве уже есть значение по умолчанию 0.
  2. некоторые компиляторы устанавливают массив по умолчанию 0 в режиме отладки.
  3. легко установить значение по умолчанию 0 : массив int[10] = {0};
  4. однако для других значений вы используете memset () или loop;

пример: массив int[10]; memset (array, -1, 10 * sizeof(int));


прорезая всю болтовню, короткий ответ заключается в том, что если вы включите оптимизацию во время компиляции, вы не сделаете лучше, чем это:

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 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() функции для поддержки этих типов.


вы можете попробовать этот код с помощью онлайн компилятора нашли здесь.


Я не вижу никаких требований в вопросе, поэтому решение должно быть общим: инициализация неопределенного, возможно, многомерного массива, построенного из неопределенных, возможно, элементов структуры с начальным значением члена:

#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'

EDIT:start+element_size изменено на (char*)start+element_size