Объявление и инициализация массивов в C

Есть ли способ объявить сначала, а затем инициализировать массив в C?

До сих пор я инициализировал такой массив:

int myArray[SIZE] = {1,2,3,4....};

Но мне нужно сделать что-то вроде этого

int myArray[SIZE];

myArray = {1,2,3,4....};

Ответ 1

В C99 вы можете сделать это, используя сложный литерал в сочетании с memcpy

memcpy(myarray, (int[]) { 1, 2, 3, 4 }, sizeof myarray);

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

В C89/90 вы можете эмулировать это, объявив дополнительный "исходный" массив

const int SOURCE[SIZE] = { 1, 2, 3, 4 }; /* maybe `static`? */
int myArray[SIZE];
...
memcpy(myarray, SOURCE, sizeof myarray);

Ответ 2

Нет, вы не можете установить их в произвольные значения в одном выражении (если это не сделано как часть объявления).

Вы можете сделать это с помощью кода, например:

myArray[0] = 1;
myArray[1] = 2;
myArray[2] = 27;
:
myArray[99] = -7;

или (если есть формула):

for (int i = 0; i < 100; i++) myArray[i] = i + 1;

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

static const int onceArr[]  = {  0,  1,  2,  3,  4,..., 99};
static const int twiceArr[] = {  0,  2,  4,  6,  8,...,198};
:
int myArray[7];
:
memcpy (myArray, twiceArr, sizeof (myArray));

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


Вы можете даже локализовать его с помощью функции инициализации:

void initMyArray (int *arr, size_t sz) {
    static const int template[] = {2, 3, 5, 7, 11, 13, 17, 19, 21, ..., 9973};
    memcpy (arr, template, sz);
}
:
int myArray[100];
initMyArray (myArray, sizeof(myArray));

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

Ответ 3

Есть ли способ объявить первый и затем инициализируйте массив в C?

Есть! но не используя описанный вами метод.

Вы не можете инициализировать список с разделителями-запятыми, это разрешено только в объявлении. Однако вы можете инициализировать с помощью...

myArray[0] = 1;
myArray[1] = 2;
...

или

for(int i = 1; i <= SIZE; i++)
{
  myArray[i-1] = i;
}

Ответ 4

Это дополнение к принятому ответу AndreyT с комментариями Nyan о несоответствующих размерах массива. Я не согласен с их автоматической установкой пятого элемента на ноль. Вероятно, это будет 5 - число после 1,2,3,4. Поэтому я предлагаю оболочку memcpy() для создания ошибки времени компиляции при попытке скопировать массивы разных размеров:

#define Memcpy(a,b) do {                    /* copy arrays */       \
    ASSERT(sizeof(a) == sizeof(b) &&        /* a static assert */   \
           sizeof(a) != sizeof((a) + 0));   /* no pointers */       \
    memcpy((a), (b), sizeof (b));           /* & unnecesary */      \
    } while (0)                             /* no return value */

Этот макрос будет генерировать ошибку времени компиляции, если ваш массив имеет длину 1. Это, возможно, функция.

Поскольку мы используем макрос, для составного литерала C99 требуется дополнительная пара круглых скобок:

Memcpy(myarray, ((int[]) { 1, 2, 3, 4 }));

Здесь ASSERT() является "статическим утверждением". Если у вас еще нет своего собственного, я использую следующее на нескольких платформах:

#define CONCAT_TOKENS(a, b) a ## b
#define EXPAND_THEN_CONCAT(a,b) CONCAT_TOKENS(a, b)
#define ASSERT(e) enum {EXPAND_THEN_CONCAT(ASSERT_line_,__LINE__) = 1/!!(e)}
#define ASSERTM(e,m) /* version of ASSERT() with message */ \
    enum{EXPAND_THEN_CONCAT(m##_ASSERT_line_,__LINE__)=1/!!(e)}

Ответ 5

Почему вы не можете инициализировать при объявлении?

Какой компилятор C вы используете? Поддерживает ли он C99?

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

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

Ответ 6

Нет такого конкретного способа, в котором вы можете инициализировать массив после объявления его один раз.

Только три варианта:

1.) инициализировать их в разных строках:

int array[SIZE];

array[0] = 1;
array[1] = 2;
array[2] = 3;
array[3] = 4;
//...
//...
//...

Но это не то, что вы хотите, я думаю.

2.) Инициализировать их с помощью цикла for или while:

for (i = 0; i < MAX ; i++)  {
    array[i] = i;
}

Это ЛУЧШИЙ ПУТЬ, чтобы достичь своей цели.

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

И могу ли я спросить вас, почему именно вы хотите это сделать?

Ответ 7

OP оставил некоторую важную информацию из вопроса и только поместил ее в комментарий к ответу.

Мне нужно инициализировать после объявления, потому что будет отличаться в зависимости от условия, я имею в виду что-то вроде этого int туАггау [РАЗМЕР]; if (condition1) {myArray {x1, x2, x3,...}} else if (condition2) {myArray {y1, y2, y3,...}}., и так далее...

Учитывая это, все возможные массивы должны быть где-то сохранены в данных, поэтому memcpy не требуется (или не требуется), требуется только указатель и 2d-массив.

//static global since some compilers build arrays from instruction data
//... notably not const though so they can later be modified if needed
#define SIZE 8
static int myArrays[2][SIZE] = {{0,1,2,3,4,5,6,7},{7,6,5,4,3,2,1,0}};

static inline int *init_myArray(_Bool conditional){
  return myArrays[conditional];
}

// now you can use:
//int *myArray = init_myArray(1 == htons(1)); //any boolean expression

Нестрочная версия дает эту результирующую сборку на x86_64:

init_myArray(bool):
        movzx   eax, dil
        sal     rax, 5
        add     rax, OFFSET FLAT:myArrays
        ret
myArrays:
        .long   0
        .long   1
        .long   2
        .long   3
        .long   4
        .long   5
        .long   6
        .long   7
        .long   7
        .long   6
        .long   5
        .long   4
        .long   3
        .long   2
        .long   1
        .long   0

Для дополнительных условных выражений/массивов просто измените 2 в myArrays на нужный номер и используйте аналогичную логику, чтобы получить указатель на правый массив.

Ответ 8

Невозможно сразу присвоить значения массиву сразу после инициализации. Лучшей альтернативой было бы использование цикла.

for(i=0;i<N;i++)
{
     array[i] = i;
}

Вы можете жестко кодировать и назначать значения, такие как - array[0] = 1 и т.д.

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