Почему структура переменного размера не компилируется в Arduino IDE?

Этот скетч не компилируется в Arduino IDE

void setup() {
  // put your setup code here, to run once:

}

struct test {
  int i;
  char variable[];
};

typedef struct test test;

test t = {
  0, "hi"
};

void loop() {
  // put your main code here, to run repeatedly:

}

Arduino бросает

sketch_may09a:16: error: initializer-string for array of chars is too long [-fpermissive]

 };

 ^

exit status 1
initializer-string for array of chars is too long [-fpermissive]

Однако компиляция с помощью g++ работает нормально. Как я могу это исправить? Или есть принципиальная причина, по которой гибкие члены массива не поддерживаются?

, 👍2

Обсуждение

Больно видеть этот код, значит, он неправильный. Структура — это определение переменной (которая содержит другие переменные). Как бы вы создали массив этих структур, если бы они имели разную длину?, @Jot

@Jot: сам тип структуры объявлен правильно. Нет ничего плохого в том, что массив [] является последним элементом struct. Однако в стандартном языке C единственный способ заставить такие структуры «иметь разную длину» — это «выделить» их индивидуально. Именно для этого и предназначена функция []. Во всех других контекстах массив [] просто «исчезает» (т.е. это массив размера 0). Таким образом, проблемы просто не существует в ситуациях, когда кто-то пытается создать массив таких структур., @AnT

@AnT Я знаю, что для него установлен нулевой размер, и я прочитал ваш ответ с правильным определением типа и объявлением структур. the_architect пытается поместить символы в саму структуру. Это невозможно., @Jot

@Jot Не уверен, о каком ответе ты говоришь. Размещение символов в самой структуре, как это пытается сделать ОП, **возможно** через расширение, специфичное для GNU. Как я уже сказал, код ОП совершенно корректен с точки зрения GNU., @AnT


2 ответа


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

5

Изменяемый элемент массива – это функция C. В С++ его нет. Кроме того, способ его использования для объявления и инициализации статической структуры гибкого размера нестандартен даже для C.

Однако язык GNU-C поддерживает его как расширение. Кроме того, более новые версии GCC (6 и выше) также позволяют использовать это в коде GNU-C++ в качестве расширения. Но GCC 5.4.0, используемый Arduino IDE, не поддерживает эту нестандартную функцию в коде C++.

Если вы пролистаете разные версии GCC на Godbolt (https://godbolt.org/z/Iul7hD), вы обратите внимание, что поддержка этой функции всегда присутствовала в коде C, но для кода C++ она впервые появилась в GCC 6.

Вот почему вы смогли скомпилировать его с помощью своего автономного компилятора g++ (который, по-видимому, является гораздо более поздней версией), но не смогли скомпилировать его в Arduino IDE.

Это означает, что если вы действительно хотите, чтобы этот нестандартный код скомпилировался в текущей версии Arduino IDE, вы должны поместить его в файл .c. Или просто укажите размер массива явно.

,

Спасибо за ссылку на Godbolt. Я попробовал несколько вещей, и все пошло не так очень быстро. Например, добавление переменной в структуру **после** гибкого массива или с массивом структур. Некоторые вещи работают с gcc версии 6, но не с 8 или 9. Функция sizeof() не включает элемент гибкого массива. И так далее., @Jot

@Jot: Гибкий элемент массива, естественно, должен быть последним полем структуры. Да, sizeof не учитывает это. Гибкие члены массива предназначены для обеспечения поддержки старого доброго «структурного хака» (http://c-faq.com/struct/structhack.html), а тот факт, что sizeof игнорирует этот член, облегчает вычисление правильного размер для malloc., @AnT


2

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

Это потому, что размер структуры должен быть известен во время компиляции, и если вы укажете char variable[];, вы получите массив нулевого размера. Поэтому все, что больше, чем ничего, что вы пытаетесь инициализировать, слишком длинное.

Возможно, вы ищете что-то вроде этого:

struct test {
  int a;  
  const char * ptr;
};

test paramx {1, "some string"};
test paramy {2, "another string"};

test array[] = {{3,"three"}, {4, "four"}, {5,"five"}};
,

charvariable[]; не является массивом нулевого размера. Это *гибкий* массив. В стандарте C его размер зависит от того, сколько памяти было выделено пользователем для всего объекта структуры. А в расширенном GNU-C дополнительная память может быть запрошена так, как ее запрашивает OP: с помощью инициализатора {}. Что касается GNU-C, то с кодом все в порядке., @AnT

И это работает так, как будто это массив нулевого размера, указывающий после конца структуры, поэтому вам нужно выделить больше, чем sizeof(struct), чтобы использовать его без «переполнения буфера». Но, как было отмечено, он не может работать со статически выделенным экземпляром структуры. А в C++ это должна быть структура, содержащая только POD., @KIIV

Ну, это не может работать для объекта нединамической структуры в **педантичном стандарте** C. Но ОП, очевидно, не пытается использовать педантичный стандарт C. И да, он **может и будет** работать в расширенном GNU. -C (и в GNU-C++ после версии 6 компилятора). Исходный код вполне подходит для расширенных GNU версий этих языков. Код **будет** компилироваться и работать как задумано в Arduino IDE, как только он будет помещен в файл .c, как я ясно указал в своем ответе. Как только Arduino IDE перейдет на более позднюю версию AVR-GCC, этот код сразу же скомпилируется и заработает как задумано., @AnT

Однако я не уверен, что он действительно хотел использовать FAM. Возможно, это просто совпадение. Возможно, ему нужна была просто переменная const char *; в данном случае это должно быть совершенно законно (или помощники по строкам / указатели программных ячеек)., @KIIV

Название вопроса, кажется, предполагает, что ОП действительно хотел использовать структуру переменного размера. Имели ли они в виду это *буквально*, как ссылку на эту конкретную особенность языка — я не знаю., @AnT

@AnT Правда. Кстати, похоже, он все равно работает с использованием malloc. Он просто не может использовать эту инициализацию, потому что она воспринимает ее как массив нулевой длины. И strcpy необходимо использовать для копирования строк на место., @KIIV

Итак, еще раз: в GNU C вы *можете* использовать эту инициализацию. Это особенность компилятора GCC. Он выделяет правильный (гибкий) объем статической памяти для объекта структуры и работает так, как если бы инициализирующая строка была правильно помещена в конечный массив. sizeof для структуры будет игнорировать массив [], но элементы будут правильно доступны (и изменяемы)., @AnT