Подтвердить что ты не робот

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

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


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

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 С++, но не компилируется с g++ под linux. Может ли кто-нибудь помочь мне сделать этот вид инициализатора портативным?

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

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

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

4b9b3361

Ответ 1

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

РЕДАКТИРОВАТЬ: Вы сказали в своем правлении, что массив является константой времени выполнения, поэтому укажите размер, и он должен работать нормально. 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 (например, list/vector/stack/queue/etc, потому что они имеют разные размеры. Если вы этого хотите, вы должны использовать 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 в "коллекции".

EDIT: В общем, это похоже на вас (если вы не храните больше данных, а не только некоторые цифры и размер), вообще не нужна структура и не может просто использовать простой старый вектор.

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

Ответ 2

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

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

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

Ответ 3

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++, массив значений - это только сырое хранилище данных, поэтому оно является static const и будет храниться во flash. Я использую этот способ для хранения шаблонов шрифтов для отображения lcd.

Использование шаблона - это сохранение, но не сохранение размера.

Ответ 4

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