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

StackOverflow https://stackoverflow.com/questions/201101

Вопрос

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

Это было полезно?

Решение

Если это значение не составляет 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.Это не допускается с С:

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

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

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

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

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

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

если размер массива изменится.

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

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

Видеть:

C-расширения

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

Тогда задайте вопрос:Когда можно использовать расширения 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 или размер вашего мем-шаблона соответствует точному времени в int (т.е.все нули или 0xA5A5A5A5), лучше всего использовать мемсет().

В противном случае вызовите 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;
}

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

#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

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

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

Добавлен бонус:код на самом деле читаем :)

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

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

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

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


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

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

int a[4];

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

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

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

Я знаю, что в исходном вопросе явно упоминается 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)

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top