Разыменование массивов переменного размера в структурах

StackOverflow https://stackoverflow.com/questions/178838

  •  05-07-2019
  •  | 
  •  

Вопрос

Структуры кажутся полезным способом анализа двоичного большого двоичного объекта данных (то есть файла или сетевого пакета).Это прекрасно до тех пор, пока у вас не появятся массивы переменного размера в большом двоичном объекте.Например:

struct nodeheader{
        int flags;
        int data_size;
        char data[];
};

Это позволяет мне найти последний символ данных:

nodeheader b;
cout << b.data[b.data_size-1];

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

struct nodeheader{
    int friend_size;
    int data_size;
    char data[];
    char friend[];
};

Я не распределяю эти структуры вручную.У меня есть файл, подобный этому:

char file_data[1024];
nodeheader* node = &(file_data[10]);

Поскольку я пытаюсь разобрать двоичный файл (точнее, файл класса).Я написал реализацию на Java (которая была моим классным заданием), нет, я делаю личную версию на C ++ и надеялся обойтись без необходимости писать 100 строк кода.Есть какие-нибудь идеи?

Спасибо, Стефан

Это было полезно?

Решение

У вас не может быть нескольких массивов переменного размера.Как компилятор должен во время компиляции знать, где находится friend[]?Местоположение friend зависит от размера данных[], а размер данных неизвестен во время компиляции.

Другие советы

Это очень опасная конструкция, и я бы не советовал этого делать.Вы можете включать массив переменной длины в структуру только тогда, когда это ПОСЛЕДНИЙ элемент, и когда вы делаете это, вы должны убедиться, что вы выделяете достаточно памяти, например:

nodeheader *nh = (nodeheader *)malloc(sizeof(nodeheader) + max_data_size);

То, что вы хотите сделать, это просто использовать обычные динамически выделяемые массивы:

struct nodeheader
{
  char *data;
  size_t data_size;
  char *friend;
  size_t friend_size;
};

nodeheader AllocNodeHeader(size_t data_size, size_t friend_size)
{
  nodeheader nh;
  nh.data = (char *)malloc(data_size);  // check for NULL return
  nh.data_size = data_size;
  nh.friend = (char *)malloc(friend_size);  // check for NULL return
  nh.friend_size = friend_size;

  return nh;
}

void FreeNodeHeader(nodeheader *nh)
{
  free(nh->data);
  nh->data = NULL;
  free(nh->friend);
  nh->friend = NULL;
}

Вы не можете - по крайней мере, не тем простым способом, который вы пытаетесь.Массив без размера в конце структуры - это, по сути, смещение к концу структуры, без встроенного способа нахождения конца.

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

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

struct nodeheader
{
    int friend_size;
    int data_size;
};

struct nodefile
{
    nodeheader *header;
    char *data;
    char *friend;
};

char file_data[1024];

// .. file in file_data ..

nodefile file;
file.header = (nodeheader *)&file_data[0];
file.data = (char *)&file.header[1];
file.friend = &file.data[file->header.data_size];

Для того, что вы делаете, вам нужен кодер / декодер для данного формата.Декодер берет необработанные данные и заполняет вашу структуру (в вашем случае выделяя место для копии каждого раздела данных), и декодер записывает необработанный двоичный файл.

(Было 'Использовать std::vector')

Редактировать:

Прочитав отзывы, я полагаю, мне следует расширить свой ответ.Вы можете эффективно разместить два массива переменной длины в своей структуре следующим образом, и хранилище будет освобождено для вас автоматически, когда file_data выйдет за пределы области видимости:

struct nodeheader {
    std::vector<unsigned char> data;
    std::vector<unsigned char> friend_buf; // 'friend' is a keyword!
    // etc...
};

nodeheader file_data;

Теперь file_data.data.size() и т.д. дает вам длину, а &file_data.data[0] дает вам необработанный указатель на данные, если вам это нужно.

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

Кстати, техника OP неверна даже для его "прекрасных и изящных" случаев, напримертолько с одним VLA в конце.

char file_data[1024];
nodeheader* node = &(file_data[10]);

Нет никакой гарантии, что file_data правильно выровнены для типа nodeheader.Предпочитаю получать file_data с помощью malloc(), что гарантирует возврат указателя, выровненного для любого типа, или же (лучше) в первую очередь объявить буфер правильного типа:

struct biggestnodeheader {
    int flags;
    int data_size;
    char data[ENOUGH_SPACE_FOR_LARGEST_HEADER_I_EVER_NEED];
};

biggestnodeheader file_data;
// etc...
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top