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

Являются ли гибкие члены массива действительными в С++?

В C99 вы можете объявить гибкий элемент массива структуры как таковой:

struct blah
{
    int foo[];
};

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

struct blah
{
    int foo[0];
};

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

4b9b3361

Ответ 1

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

Ответ 2

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

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

struct blah
{
    int count;
    int foo[0]; // also not valid 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 и С++ и работает так же, как и члены гибкого массива C99.

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

Примечание. В статье Raymond Chen есть опечатка/ошибка в примере, инициализирующем "гибкий" массив. Он должен гласить:

for (DWORD Index = 0; Index < NumberOfGroups; Index++) { // note: used '<' , not '='
  TokenGroups->Groups[Index] = ...;
}

Ответ 3

Второй не будет содержать элементов, а будет указывать сразу после 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 int, но данные в массиве будут после структуры something.

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

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

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

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

Ответ 4

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

template <typename BASE, typename T, unsigned SZ>
struct Flex : public BASE {
    T flex_[SZ];
};

Ответ 5

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

struct blah { int foo[]; };

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

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

struct blah { char a,b; /*int foo[]; //not valid in 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;
}

Ответ 6

Предложение находится в стадии разработки и может быть внесено в будущую версию C++. Подробности смотрите в https://thephd.github.io/vendor/future_cxx/papers/d1039.html (предложение довольно новое, поэтому оно может быть изменено)

Ответ 7

Я столкнулся с той же проблемой при объявлении члена гибкого массива, который можно использовать из кода C++. Просматривая заголовки glibc, я обнаружил, что есть некоторые варианты использования гибких элементов массива, например, в struct inotify, который объявлен следующим образом (комментарии и некоторые несвязанные элементы пропущены):

struct inotify_event
{
  //Some members
  char name __flexarr;
};

Макрос __flexarr, в свою очередь, определяется как

/* Support for flexible arrays.
   Headers that should use flexible arrays only if they're "real"
   (e.g. only if they won't affect sizeof()) should test
   #if __glibc_c99_flexarr_available.  */
#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
# define __flexarr  []
# define __glibc_c99_flexarr_available 1
#elif __GNUC_PREREQ (2,97)
/* GCC 2.97 supports C99 flexible array members as an extension,
   even when in C89 mode or compiling C++ (any version).  */
# define __flexarr  []
# define __glibc_c99_flexarr_available 1
#elif defined __GNUC__
/* Pre-2.97 GCC did not support C99 flexible arrays but did have
   an equivalent extension with slightly different notation.  */
# define __flexarr  [0]
# define __glibc_c99_flexarr_available 1
#else
/* Some other non-C99 compiler.  Approximate with [1].  */
# define __flexarr  [1]
# define __glibc_c99_flexarr_available 0
#endif

Я не знаком с компилятором MSVC, но, вероятно, вам придется добавить еще один условный макрос в зависимости от версии MSVC.

Ответ 8

Оба int foo[]; и int foo[0]; неверны в С++ (по крайней мере, в таком контексте). Используйте int *foo;.

Когда вы объявляете такой массив в С++, его размер должен быть определен во время компиляции. То есть вы должны явно указать размер массива явно, например int foo[5], или использовать список инициализации, например int foo[] = {1, 2, 3}. К сожалению, вы не можете использовать список инициализации для инициализации члена класса.

ИЗМЕНИТЬ

Для целей сериализации используйте std::vector<int> foo. После заполнения вы можете легко получить указатель на массив целых чисел и его размер:

int* begin = &foo[0];
std::size_t size = foo.size();

Ответ 9

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

struct blah
{
    int* foo;
};

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

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