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

747

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

  • 12
    Ни один из ответов до сих пор не упоминает обозначенную обозначение инициализатора, которое выполнимо для 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.
  • 0
    На самом деле ответ abelenky использует назначенный инициализатор, но не полностью сформированный код инициализации
Показать ещё 2 комментария
Теги:
arrays
initialization

19 ответов

1022
Лучший ответ

Если это значение равно 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

В С++ пустой список инициализации также инициализирует каждый элемент равным 0. Это не разрешено с помощью C:

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

Помните, что объекты со статическим временем хранения инициализируются до 0, если нет Инициализатор указан:

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

И что "0" не обязательно означает "all-bits-zero", поэтому использование вышеописанного лучше и более переносимым, чем memset(). (Значения плавающей точки будут инициализируется до +0, указывает на нулевое значение и т.д.)

  • 23
    Читая стандарт C ++, вы также можете сделать int array [10] = {}; к нулю инициализировать. У меня нет стандарта C, чтобы проверить, что это действительный C, хотя.
  • 50
    Глядя на раздел 6.7.8 Инициализация стандарта C99, не похоже, что пустой список инициализатора разрешен.
Показать ещё 15 комментариев
346

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

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

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

  • 0
    @qrdl Я попытался инициализировать 2D-массив, динамически размещаемый в куче, используя malloc (sizeof (int [row] [col])) и получил ошибку времени компиляции в строке, где используется эта строка: arr [row] [col] = {[0 ... строка -1] [0 ... col-1] = -1}; Ошибка, которую я получил, была "ожидаемое выражение перед '{' токеном". Есть идеи по этому поводу? Спасибо
  • 1
    @abhi То, что вы пытаетесь сделать, это не инициализация, а присвоение, этот синтаксис не может быть использован в такой ситуации, вам будет лучше использовать memset()
Показать ещё 11 комментариев
169

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

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

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

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

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

Вы можете легко обобщить это с помощью:

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

имена макросов являются оборотными.

  • 11
    Я хотел бы рассмотреть это только в крайних случаях, конечно, memset - более элегантный способ выразить это.
  • 0
    Конечно, memset () - это путь. Я понял, что ОП ищет альтернативу.
Показать ещё 12 комментариев
57

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

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

нет.

  • 0
    это правильно ? int myPoints[10][] = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9} };
  • 8
    Нет. Вы опускаете самое внутреннее измерение, которое не допускается. Это даст ошибку компилятора.
Показать ещё 2 комментария
43

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

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

Это ведет к упорядочению, даже если вам не удалось написать некоторые из значений перечисления не в порядке.

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

  • 7
    Это синтаксис инициализатора C99, уже описанный в некоторых других ответах. Вы могли бы с пользой сделать объявление в char const *array[] = { ... }; или даже char const * const array[] = { ... }; не так ли?
20
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); , это в основном просто более медленная и более подробная версия memset(myArray, VALUE, ARRAY_SIZE);
  • 18
    Как бы вы использовали memset для инициализации массива int с некоторым значением больше 255? memset работает только если размер массива в байтах.
Показать ещё 1 комментарий
11

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

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

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

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-Extensions

Назначенные inits

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

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

5

Немного вялый ответ; напишите выражение

array = initial_value

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

5

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

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

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

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

3

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

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

Для массива 1 000 000 elements 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;
}
  • 2
    Извините, но это не так. Может быть, вы забыли включить оптимизацию компиляции во время тестов (проверено в режиме отладки?). Если я проверяю это, цикл почти всегда на 50% быстрее, чем memfill («всегда» из-за некоторых скачков нагрузки на моем компьютере). И используя memset (a, 0, sizeof (a)); даже в два раза быстрее, чем loopfill.
  • 2
    Как и в случае с любым другим кодом, вы должны быть предельно осторожны. Добавление цикла для выполнения временного кода 10 раз (и удвоение размера массива до 20M) показывает - для меня, запуск на MacBook Pro с macOS Sierra 10.12.3 и использование GCC 6.3.0 - что в первый раз, используя цикл занимает около 4600 мкс, а код memfill() около 1200 мкс. Однако на последующих итерациях цикл занимает около 900-1000 мкс, а код memfill() 1000-1300 мкс. На первую итерацию, вероятно, влияет время заполнения кеша. memfill() тесты и 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
  • 2
    Публикация кода на языке C ++ для вопросов C - это применимость. Предложите переделать на C.
  • 1
    <iostream> не является допустимым C как std::cout , std::cin и т. д. является частью std::namespace а C не поддерживает namespaces . Попробуйте вместо этого использовать <stdio.h> для printf(...) .
2

Вы можете использовать функцию memset.

void *memset(void *array, int value, unsigned sizeofarray);
1

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

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

Добавлен бонус: код действительно разборчивый:)

  • 6
    Вопрос специально задан для инициализации. Это явно не инициализация, а присвоение, выполненное после инициализации. Это может быть сделано немедленно, но это не инициализация.
  • 0
    Совершенно бесполезно для большой статической таблицы поиска внутри функции, вызываемой много раз.
Показать ещё 1 комментарий
1
  • Если ваш массив объявлен как статический или глобальный, все элементы в массиве уже есть значение по умолчанию 0.
  • Некоторые компиляторы задают массив по умолчанию равным 0 в режиме отладки.
  • По умолчанию установлено значение 0: int array [10] = {0};
  • Однако для других значений вы используете memset() или loop;

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

0

Я знаю, что пользователь Tarski ответил на этот вопрос аналогичным образом, но я добавил несколько подробностей. Простите некоторые из моих 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
#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

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

#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

  • 1
    Я сомневаюсь, является ли это решением. Я не уверен, является ли sizeof(void) действительным.
  • 3
    Не работает Только первые два инициализируются, остальные все неинициализированы. Я использую GCC 4.0 на Mac OS X 10.4.
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню