C инициализировать массив в структуре

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

struct Grid {
  int rows;
  int cols;
  int grid[];
}

int main() {
  struct Grid testgrid = {1, 3, {4, 5, 6}};
}

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

13.10.2009 03:08:24
Я отредактировал вопрос выше, чтобы проиллюстрировать ...
user35288 13.10.2009 03:16:52
Какой компилятор, на какой платформе?
Jonathan Leffler 13.10.2009 04:14:14
Итак, вы хотите иметь возможность создавать два разных объекта GRid с массивами grid [] различной длины. Какой номер должен sizeof Gridдать вам? Если я хочу создать массив объектов Grid, сколько байтов должен выделить компилятор для каждого элемента? Вам нужно сделать grid указатель на int.
PaulMurrayCbr 13.05.2016 04:11:42
5 ОТВЕТОВ
РЕШЕНИЕ

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

#include <stdlib.h>
#include <stdarg.h>

typedef struct Grid {
  int rows;
  int cols;
  int grid[];
} *Grid;

Grid newGrid(int, int, ...);

Grid newGrid(int rows, int cols, ...)
{
Grid g;
va_list ap;
int i, n = rows * cols;

  if((g = malloc(sizeof(struct Grid) + rows * cols * sizeof(int))) == NULL)
    return NULL;
  g->rows = rows;
  g->cols = cols;
  va_start(ap, cols);
  for(i = 0; i < n; ++i)
    g->grid[i] = va_arg(ap, int);
  va_end(ap);
  return g;
}
.
.
.
Grid g1, g2, g3;
g1 = newGrid(1, 1, 123);
g2 = newGrid(2, 3, 1, 1, 1,
                   2, 2, 2);
g3 = newGrid(4, 5, 1,  2,  3,  4,  5,
                   6,  7,  8,  9, 10,
                  11, 12, 13, 14, 15,
                  16, 17, 18, 19, 20);
12
13.10.2009 18:25:49
Я буду +1, если вы выделите это немного, чтобы оно было немного более читабельным. : P
Chris Lutz 13.10.2009 03:13:36
Я хотел разделить их на отдельные утверждения, но это тоже хорошая идея.
Chris Lutz 13.10.2009 03:19:41
Я считаю, что вы застряли с ограничением staticкласса хранения, потому что просто нет способа заставить компилятор создать структуру переменной длины с autoклассом хранения. В C99 есть массивы переменной длины, поэтому вы можете выделить место в стеке массивом переменной длины, а затем инициализировать указатель на struct Gridего адрес. Вероятно, это будет даже соответствующая программа, если доступ к памяти осуществляется исключительно через struct *. Вы могли бы также mallocэто. Тогда вы можете memcpyиз staticструктуры в autoодну.
DigitalRoss 13.10.2009 03:42:16
Извините, но это не C. Я не знаю, какой компилятор вы используете и какие языковые расширения он поддерживает, но то, что вы написали выше, не является ни C, ни C89 / 90, ни C99. В C агрегатные инициализаторы нельзя использовать для «создания» гибких элементов массива в структурах, независимо от того, являются ли эти структуры статическими или автоматическими.
AnT 13.10.2009 03:44:03
@DigitalRoss: операторы в 6.7.8 применяются только к автономным массивам, но не к элементам гибкого массива (FAM) в структурах. Технически, FAM не является «массивом» для целей 6.7.8. Кроме того, если требования к 6.7.8 применяются к FAM, они не будут ограничены только статическими массивами, не так ли?
AnT 13.10.2009 04:02:01

Версия, использующая malloc:

#include <stdio.h>
#include <stdlib.h>

typedef struct Grid {
  int rows;
  int cols;
  int *grid;
} Grid;

/* Should validate params */
Grid
buildGrid(int rows, int cols, int vec[]) {

    Grid grid;
    grid.rows = rows;
    grid.cols = cols;
    int i;

    if ( (grid.grid = malloc(sizeof(vec))) == NULL ) {
        /* do something.*/
    }

    for(i = 0; i < sizeof(vec) ; i++ ) {
        grid.grid[i] = vec[i];
    }

    return grid;
}
-1
13.10.2009 03:37:51
sizeof(vec)не будет работать так, как ты думаешь. Массивы уменьшаются до указателей при передаче в виде функции, поэтому эта строка будет такой же, как и sizeof(int *)не то, что вы хотите.
Chris Lutz 13.10.2009 03:41:52
Кроме того, есть ли причина не идти вперед и сделать его реальным двумерным массивом?
Chris Lutz 13.10.2009 03:43:51
Крис: Вы правы. Спасибо за указание на это. Правильным путем будет передача и дополнительного параметра sizeof (vec) / sizeof (vec [0]) в качестве размера vec.
Macarse 13.10.2009 12:48:43
Поскольку вы перешли int[] gridна другое, int *gridвы также можете использовать статический инициализатор в C99 и забыть всю чушь, приведенную выше: struct Grid testgrid = {1, 3, (int[3]){4, 5, 6}};(извините за оживление, но об этом тривиальном решении следует упомянуть ...)
Simon Urbanek 6.02.2012 15:17:07

Я не верю, что это возможно или поддерживается. Как указывает DigitalRoss , вы можете инициализировать с литерала в случае staticмассивов ... хотя я все еще не уверен, включено ли это в стандарт или просто в общее расширение. Кажется, я не могу найти в Стандарте пункт, который поддерживает буквальную инициализацию гибких массивов, хотя я вижу, что gcc явно поддерживает его .

1
23.05.2017 10:29:57
@ Андрей - Comeau - это компилятор C ++, а не компилятор C, поэтому ожидается отклонение кода C99, поскольку стандарт C99 не включен в C ++.
Chris Lutz 13.10.2009 03:55:23
@ Chris Lutz: Нет. Comeau - это компилятор C ++, C89 / 90 и C99, в зависимости от того, как вы его вызываете.
AnT 13.10.2009 04:08:23
Ах. Я думал, что это «Comeau C ++», но оказалось, что это «Comeau C / C ++». Я думаю, что знал это в какой-то момент, но забыл это.
Chris Lutz 13.10.2009 14:09:02

Вы не имеете массив переменной длины (VLA) в вашей структуре. То, что у вас есть в вашей структуре, называется членом гибкого массива . Гибкий член массива не имеет абсолютно никакого отношения к VLA. Гибкие члены массива в C существуют для легализации и поддержки старой доброй идиомы "struct hack", которая основана на динамическом распределении памяти для объектов struct с конечными массивами различного размера.

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

Между тем, текст сообщения об ошибке, сгенерированного вашим компилятором, предполагает, что он поддерживает что-то вроде этого как расширение. Это может быть правдой, но имейте в виду, что это никоим образом не является стандартной функцией C.

4
13.10.2009 13:43:13
Опять же, не относится к гибким членам массива. Также обратите внимание, что GCC, один из компиляторов, который поддерживает это расширение, четко и явно документирует его как специфичное для GCC расширение.
AnT 13.10.2009 04:13:53
Нет необходимости говорить, что вызов GCC в режиме -ansi -pedantic приводит к предупреждению о попытке инициализации элемента гибкого массива.
AnT 13.10.2009 04:20:13
Я считаю, что AudreyT верен - в другом месте стандарт прямо говорит: «Тип структуры, содержащий элемент гибкого массива, является неполным типом, который не может быть завершен».
caf 13.10.2009 04:25:03
Вздох, если бы вы просто цитировали стандарт, мы могли бы избежать обсуждения. Оказывается, есть пример гибкой инициализации массива, и стандарт говорит, что он недействителен. 6.7.2.1 (20)
DigitalRoss 13.10.2009 04:26:09
Ну, если честно, я пропустил этот пример. Более того, примеры, как правило, ненормативны, поэтому я их пропустил. Я все еще не могу найти какую-либо конкретную формулировку в нормативном тексте, которая бы запрещала это.
AnT 13.10.2009 04:38:09

Вот моя версия:

#include <stdio.h> 

struct matrix {
  int rows;
  int cols;
  int **val;
} a = {        .rows=3,  .cols=1,
        .val = (int*[3]){ (int[1]){1},
                          (int[1]){2},
                          (int[1]){3} } },

  b = {        .rows=3,  .cols=4,
        .val = (int*[3]){ (int[4]){1, 2, 3, 4},
                          (int[4]){5, 6, 7, 8},
                          (int[4]){9,10,11,12} } };

void print_matrix( char *name, struct matrix *m ){
  for( int row=0;row<m->rows;row++ )
    for( int col=0;col<m->cols;col++ )
      printf( "%s[%i][%i]: %i\n", name, row, col, m->val[row][col] );
  puts("");
}

int main(){
  print_matrix( "a", &a );
  print_matrix( "b", &b );
}
27
13.10.2009 04:18:43
Я знаю, что это старая тема, но она просто решила большую проблему, с которой я столкнулся. Полезным моментом является то, что массивы могут быть разных размеров. Я удивлен, что не смог найти ответ где-либо еще. Спасибо.
kainosnous 12.11.2010 10:27:47
Также довольно полезно для меня! Другие ответы, по-видимому, не отвечают основному требованию вопроса. Большое спасибо!
basicthinker 19.07.2013 08:24:33
Спасибо. Я наконец нашел это специальное решение, которое искал время от времени в течение нескольких месяцев.
mesmerizingr 17.06.2014 14:15:33
Наиболее полезным аспектом этого ответа является идея создания указателя в структуре, но инициализация его составным литералом массива . Без (int*[])приведения компилятор будет жаловаться.
nitrogen 6.07.2014 19:32:04
Это не похоже на переносимость. Gcc с радостью принимает следующее: typedef struct node { int **data; } node; node n = { (int*[2]) { (int[1]) {5}, (int[1]) {1} } };Но cl.exe (visual studio 2013) - это другая история. Когда код находится в файле .c, cl.exe прекрасно его компилирует. Но когда он находится в файле .cpp, cl.exe возражает с этим:thing.cpp(15) : error C2059: syntax error : '{' thing.cpp(15) : error C2143: syntax error : missing ';' before '{' thing.cpp(15) : error C2447: '{' : missing function header (old-style formal list?) ...
Ewat 14.07.2016 22:56:04