Допустимы ли члены гибкого массива в C ++? - PullRequest
34 голосов
/ 10 декабря 2010

В C99 вы можете объявить член гибкого массива структуры следующим образом:

struct blah
{
    int foo[];
};

Однако, когда кто-то здесь на работе пытался скомпилировать некоторый код с использованием clang в C ++, этот синтаксис не работал,(Он работал с MSVC.) Нам пришлось преобразовать его в:

struct blah
{
    int foo[0];
};

Просматривая стандарт C ++, я вообще не нашел ссылок на гибкие массивы элементов;Я всегда думал, что [0] было недопустимым объявлением, но, по-видимому, для гибкого массива членов это допустимо.Действительно ли гибкие массивы-члены действительны в C ++?Если это так, является ли правильное объявление [] или [0]?

Ответы [ 6 ]

21 голосов
/ 10 декабря 2010

C ++ был впервые стандартизирован в 1998 году, поэтому он предшествовал добавлению гибких элементов массива в C (который был новым в C99).В 2003 году было исправление для C ++, но это не добавило каких-либо соответствующих новых функций.Следующая версия C ++ (C ++ 0x) все еще находится в стадии разработки, и кажется, что гибкие члены массива к ней не добавляются.

17 голосов
/ 10 декабря 2010

C ++ не поддерживает элементы гибкого массива C99 в конце структур, используя либо пустую индексную нотацию, либо индексную нотацию 0 (за исключением расширений, специфичных для поставщика):

struct blah
{
    int count;
    int foo[];  // not valid C++
};

struct blah
{
    int count;
    int foo[0]; // also not valid C++
};

Насколько я знаю, C ++ 0x также не добавит это.

Однако, если вы увеличите размер массива до 1 элемента:

struct blah
{
    int count;
    int foo[1];
};

вещи действительны и работают довольно хорошо. Вы можете выделить соответствующую память с помощью выражения, которое вряд ли будет содержать ошибки «один на один»:

struct blah* p = (struct blah*) malloc( offsetof(struct blah, foo[desired_number_of_elements]);
if (p) {
    p->count = desired_number_of_elements;

    // initialize your p->foo[] array however appropriate - it has `count`
    // elements (indexable from 0 to count-1)
}

Так что он переносим между C90, C99 и C ++ и работает так же, как и члены гибкого массива C99.

Раймонд Чен сделал хорошую запись по этому поводу: Почему некоторые структуры заканчиваются массивом размера 1?

Примечание. В статье Рэймонда Чена приведена опечатка / ошибка в примере, инициализирующем «гибкий» массив. Следует читать:

for (DWORD Index = 0; Index < NumberOfGroups; Index++) { // note: used '<' , not '='
  TokenGroups->Groups[Index] = ...;
}
3 голосов
/ 10 декабря 2010

Второй не будет содержать элементов, а будет указывать сразу после blah.Поэтому, если у вас есть такая структура:

struct something
{
  int a, b;
  int c[0];
};

, вы можете делать такие вещи:

struct something *val = (struct something *)malloc(sizeof(struct something) + 5 * sizeof(int));
val->a = 1;
val->b = 2;
val->c[0] = 3;

В этом случае c будет вести себя как массив с 5 ints, но данные в массиве будут иметь структуру something.

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

struct String
{
  unsigned int allocated;
  unsigned int size;
  char data[0];
};

Из-за поддерживаемых архитектурэто будет занимать 8 байт плюс allocated.

Конечно, все это C, но, например, g ++ принимает его без помех.

1 голос
/ 05 ноября 2018

Если вы можете ограничить свое приложение только несколькими известными размерами, тогда вы можете эффективно создать гибкий массив с шаблоном.

template <typename BASE, typename T, unsigned SZ>
struct Flex : public BASE {
    T flex_[SZ];
};
0 голосов
/ 30 ноября 2017

Если вы хотите только

struct blah { int foo[]; };

, тогда вам не нужна структура вообще, и вы можете просто иметь дело с массивом malloc'ed / new'ed int.

Еслиу вас есть несколько членов в начале:

struct blah { char a,b; /*int foo[]; //not valid in C++*/ };

затем в C ++, я полагаю, вы могли бы заменить foo на foo функцию-члена:

struct blah { alignas(int) char a,b; 
    int *foo(void) { return reinterpret_cast<int*>(&this[1]); } };

Пример использования:

#include <stdlib.h>
struct blah { 
    alignas(int) char a,b; 
    int *foo(void) { return reinterpret_cast<int*>(&this[1]); }
};
int main()
{
    blah *b = (blah*)malloc(sizeof(blah)+10*sizeof(int));
    if(!b) return 1;
    b->foo()[1]=1;
}
0 голосов
/ 10 декабря 2010

Лучшее решение - объявить его как указатель:

struct blah
{
    int* foo;
};

Или еще лучше объявить его как std::vector:

struct blah
{
    std::vector<int> foo;
};
...