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

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

28 ответов

Решение

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

Если ваш компилятор 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 };

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

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

(любезно предоставлено 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) };

имена макросов являются предметом переговоров.

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

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

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

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

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

Я знаю, что в первоначальном вопросе явно упоминается C, а не C++, но если вы (как и я) пришли сюда в поисках решения для массивов C++, вот интересный трюк:

Если ваш компилятор поддерживает складные выражения, вы можете использовать шаблон магии и 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)

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

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-расширения

Назначенные единицы

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

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

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

array = initial_value

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

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

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

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

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

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

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

За 1 000 000 элементы int В массиве это в 4 раза быстрее обычной инициализации цикла (i5, 2 ядра, 2,3 ГГц, 4 ГБ памяти, 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;
}
  1. Если ваш массив объявлен как статический или является глобальным, все элементы в массиве уже имеют значение по умолчанию 0 по умолчанию.
  2. Некоторые компиляторы устанавливают для массива значение по умолчанию 0 в режиме отладки.
  3. По умолчанию легко установить значение 0: int array[10] = {0};
  4. Однако для других значений вы должны использовать memset() или loop;

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

int array[1024] = {[0 ... 1023] = 5}; Поскольку приведенное выше работает нормально, но убедитесь, что между точками нет пробелов.

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

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

      int a[5] = {3,3,3,3,3}; 

формальная методика инициализации.

способ 2:

      int a[100] = {0};

но стоит отметить, что

      int a[10] = {1}; 

не инициализирует все значения до 1

этот способ инициализации исключительно для 0

если ты просто сделаешь

      int a[100];

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

      int a[1000] = {0};

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

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

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

Если размер массива известен заранее, можно использовать макрос препроцессора Boost C_ARRAY_INITIALIZE, чтобы выполнить грязную работу за вас:

#include <boost/preprocessor/repetition/enum.hpp>
#define C_ARRAY_ELEMENT(z, index, name) name[index]
#define C_ARRAY_EXPAND(name,size) BOOST_PP_ENUM(size,C_ARRAY_ELEMENT,name)
#define C_ARRAY_VALUE(z, index, value) value
#define C_ARRAY_INITIALIZE(value,size) BOOST_PP_ENUM(size,C_ARRAY_VALUE,value)

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

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

Для инициализации нулями -

        char arr[1000] = { 0 };

Лучше делать с обычным "циклом for" для инициализации, отличным от 0.

        char arr[1000];
  for(int i=0; i<arr.size(); i++){
     arr[i] = 'A';
  }

Для отложенной инициализации (т.е. инициализации конструктора члена класса) рассмотрите:

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++, но здесь это идет.


Если вы знаете размер массива раньше времени...

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


Вы можете попробовать этот код с онлайн-компилятором, найденным здесь.

Как продолжение ответа Клеменса Силаффа. Эта версия требует C++17.

      template <size_t Cnt, typename T>                                               
std::array<T, Cnt> make_array_of(const T& v)                                           
{                                                                               
    return []<size_t... Idx>(std::index_sequence<Idx...>, const auto& v)        
    {                                                                           
        auto identity = [](const auto& v, size_t) { return v; };                
        return std::array{identity(v, Idx)...};                                 
    }                                                                           
    (std::make_index_sequence<Cnt>{}, v);                                       
}

Вы можете увидеть это в действии здесь .

#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 ...... до размера всего массива

В свое время (и я не говорю, что это хорошая идея), мы установили первый элемент, а затем:

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

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

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

#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

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

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)

И т.п.

Другие вопросы по тегам