C++ Инициализация структуры с массивом в качестве члена

Отредактировано снова, потому что изначально было неясно, что я пытаюсь инициализировать массивы во время компиляции, а не во время выполнения...


У меня есть следующий сокращенный тестовый пример:

typedef struct TestStruct
{
    int length;
    int values[];
};

TestStruct t = {3, {0, 1, 2}};
TestStruct t2 = {4, {0, 1, 2, 3}};

int main()
{
    return(0);
}

Это работает с Visual C++, но не компилируется с g++ под Linux. Может ли кто-нибудь помочь мне сделать этот конкретный вид инициализатора переносимым?

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

РЕДАКТИРОВАТЬ: я думаю (но не уверен), что это не проблема VLA. Чтобы уточнить, я пытаюсь заставить компилятор выполнять работу за меня во время компиляции. Длина массива во время выполнения постоянна. Извиняюсь, если я ошибаюсь; Я в первую очередь программист на С#/Perl/Ruby, который застрял в поддержке этого устаревшего приложения...

Любая помощь высоко ценится. Спасибо!


person Drew Shafer    schedule 16.04.2010    source источник
comment
Это недопустимый код C++. C++ не поддерживает массивы переменной длины или гибкие члены массива.   -  person rlbond    schedule 16.04.2010
comment
На самом деле, это работает в gcc — у вас есть нерелевантная ошибка в вашем коде, вы забыли имя структуры после того, как получили ее typedefed. Хотя, учитывая, что вы пометили вопрос как C++, я не понимаю, зачем вам вообще это делать. Структуры определения типов использовались для преодоления ограничения C (в отличие от C++), где нельзя было объявить структурную переменную без ключевого слова ненужногоstruct.   -  person Hi-Angel    schedule 30.11.2017


Ответы (4)


c++ не имеет такого же гибкого члена массива, как последний элемент, как c99. Вы должны использовать std::vector, если вы не знаете, сколько элементов, или вы должны указать, сколько, если знаете.

EDIT: в своем редактировании вы сказали, что массив является константой времени выполнения, поэтому укажите размер, и он должен работать нормально. g++ не имеет проблем со следующим кодом:

struct TestStruct { // note typedef is not needed */
    int length;
    int values[3]; // specified the size
};

TestStruct t = {3, {0, 1, 2}};

int main() {
    // main implicitly returns 0 if none specified
}

EDIT: чтобы ответить на ваш комментарий, вы можете использовать такие шаблоны:

template <int N>
struct TestStruct {
    int length;
    int values[N];
};

TestStruct<3> t3 = {3, {0, 1, 2}};
TestStruct<2> t2 = {2, {0, 1}};

int main() {}

Единственная проблема заключается в том, что нет простого способа поместить как t2, так и t3 в контейнер (например, список/вектор/стек/очередь/и т. д., потому что они имеют разные размеры. Если вы хотите этого, вы должны использовать std::vector. Кроме того, если вы делаете это, тогда нет необходимости хранить размер (он связан с типом), поэтому вы можете сделать это вместо этого:

template <int N>
struct TestStruct {
    static const int length = N;
    int values[N];
};

TestStruct<3> t3 = {{0, 1, 2}};
TestStruct<2> t2 = {{0, 1}};

int main() {}

Но опять же, вы не можете легко собрать t2 и t3 в «коллекцию».

РЕДАКТИРОВАТЬ: В общем, похоже, что вам (если вы не храните больше данных, чем просто некоторые числа и размер) вообще не нужна структура, и вы не можете просто использовать простой старый вектор .

typedef std::vector<int> TestStruct;


int t2_init[] = { 0, 1, 2 };
TestStruct t3(t3_init, t3_init + 3);

int t2_init[] = { 0, 1 };
TestStruct t2(t2_init, t2_init + 2);

int main() {}

Что позволит вам иметь как t2, так и t3 в коллекции вместе. К сожалению, std::vector (пока) не имеет синтаксиса инициализатора в стиле массива, поэтому я использовал ярлык. Но достаточно просто написать функцию для красивого заполнения векторов.

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

template <int N>
struct TestStruct {
    static const int length = N;
    int values[N];
};

TestStruct<3> t3 = {{0, 1, 2}};
TestStruct<2> t2 = {{0, 1}};

template <int N>
void func(const TestStruct<N> &ts) { /* you could make it non-const if you need it to modify the ts */
    for(int i = 0; i < N; ++i) { /* we could also use ts.length instead of N here */
        std::cout << ts.values[i] << std::endl;
    }
}

// this will work too...
template <class T>
void func2(const T &ts) { 
    for(int i = 0; i < ts.length; ++i) {
        std::cout << ts.values[i] << std::endl;
    }
}

int main() {
    func(t2);
    func(t3);
    func2(t2);
}
person Evan Teran    schedule 16.04.2010
comment
Можете ли вы указать размер массива в typedef? В любом случае я объявляю несколько экземпляров этой структуры, каждый с массивом разной длины. - person Drew Shafer; 16.04.2010
comment
Великолепно! Спасибо - это решает мою проблему! Спасибо за предупреждение о том, что шаблоны не одного типа - мне не нужен их контейнер, но мне нужно передать их в общую функцию. Пустые указатели и приведение типов должны помочь... - person Drew Shafer; 16.04.2010
comment
Примечание о том, почему я не могу просто использовать векторы — я на самом деле храню некоторую информацию о регистрах ЦП, которые необходимо читать для разных архитектур. В дополнение к массиву адресов регистров, которые мне нужно прочитать, есть еще около 8 других 32-битных целых чисел, необходимых для указания того, как получить доступ к каждой серии регистров. Эти другие целые числа были оптимизированы, когда я создавал свой (довольно глупый) тестовый пример. - person Drew Shafer; 16.04.2010
comment
Если вы хотите передать шаблонную версию функции, сделайте функцию шаблоном! Я отредактирую версию с этим :-). - person Evan Teran; 16.04.2010
comment
В порядке. Теперь я просто хочу купить тебе пива. - person Drew Shafer; 16.04.2010
comment
разве мы не можем иметь следующее? целый массив[3]{4,6,9}; TestStruct t = {3, массив}; - person passionateProgrammer; 26.06.2020

GCC/Clang поддерживает следующее расширение


    typedef struct TestStruct
    {
        int length;
        int* values;
    };

    TestStruct t = {3, (int[]){0, 1, 2}};
    TestStruct t2 = {4, (int[]){0, 1, 2, 3}};

person zufuliu    schedule 12.04.2014
comment
Это будет работать только на статическом/глобальном уровне. Если вы сделаете это внутри метода, компилятор сгенерирует временный массив целых чисел, присвоит его структуре, а затем немедленно уничтожит массив, оставив структуру с неверным указателем; GCC предупреждает, если вы попытаетесь это сделать. - person adelphus; 18.12.2016

struct TestStruct {
    int length;
    const int *values;
};

static const uint8_t TEST_STRUCT_VAL_1[] = {0, 1, 2};
const TestStruct t1 = {3, TEST_STRUCT_VAL_1};

static const uint8_t TEST_STRUCT_VAL_2[] = {0, 1, 2, 3};
const TestStruct t2 = {4, TEST_STRUCT_VAL_2};`

В моем случае, при программировании stm32 с помощью gcc/g++, массив значений представляет собой только хранилище необработанных данных, поэтому он является статическим константой и будет храниться во флэш-памяти. Я использую этот способ для хранения шаблонов шрифтов для ЖК-дисплея.

при использовании шаблона печатается сохранение, но не сохранение размера.

person aGuegu    schedule 19.08.2013

VLA поддерживается только в C99. C++ не поддерживает это. Из http://gcc.gnu.org/c99status.html gcc теперь поддерживает VLA.

person tristan    schedule 16.04.2010