Разыменование массивов переменного размера в структурах - PullRequest
2 голосов
/ 07 октября 2008

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

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 строк кода. Есть идеи?

Спасибо, Stefan

Ответы [ 6 ]

3 голосов
/ 07 октября 2008

Это очень опасная конструкция, и я бы посоветовал против нее. Вы можете включать массив переменной длины в структуру только тогда, когда это элемент LAST, и когда вы делаете это, вы должны убедиться, что вы выделяете достаточно памяти, например ::

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;
}
3 голосов
/ 07 октября 2008

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

1 голос
/ 09 октября 2008

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

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];
1 голос
/ 07 октября 2008

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

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

0 голосов
/ 07 октября 2008

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

0 голосов
/ 07 октября 2008

(Был 'Use 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 () для вектора назначения, затем прочитайте данные. (Есть способы сделать это немного более эффективно. В контексте операций ввода-вывода на диске, я полагаю, это не имеет значения).

Кстати, методика О.П. неверна даже для его «хороших и модных» дел, например. только один VLA в конце.

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

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

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

biggestnodeheader file_data;
// etc...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...