«Правильный» способ хранения двоичных данных с помощью C ++ / STL - PullRequest
45 голосов
/ 14 января 2009

В целом, каков наилучший способ хранения двоичных данных в C ++? Опции, насколько я могу судить, в значительной степени сводятся к использованию строк или векторов s. (Я опущу возможности char * s и malloc () s, поскольку я имею в виду конкретно C ++).

Обычно я просто использую строку, однако я не уверен, есть ли какие-то накладные расходы, которые я пропускаю, или преобразования, которые выполняет STL внутри, которые могут испортить разумность двоичных данных. У кого-нибудь есть указатели (хар) на это? Предложения или предпочтения так или иначе?

Ответы [ 4 ]

41 голосов
/ 14 января 2009

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

  std::vector<char> vect;
  ...
  send(sock, &vect[0], vect.size());

и будет нормально работать.

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

Недостаток - изменение размера не очень эффективно (изменение размера или предварительное распределение разумно), и удаление из передней части массива также будет очень неэффективным. Если вам, скажем, нужно очень часто вставлять только один или два символа за пределы структуры данных, возможно, стоит скопировать в deque перед этой обработкой. Это стоит вам копии, и память deque не является смежной, поэтому вы не можете просто передать указатель на C API.

Итог, узнайте о структурах данных и их компромиссах перед погружением, однако, как правило, вектор char используется в общих чертах.

8 голосов
/ 14 января 2009

Самая большая проблема с std :: string заключается в том, что текущий стандарт не гарантирует непрерывность основного хранилища. Однако нет известных реализаций STL, в которых строка не является смежной, поэтому на практике это, вероятно, не даст сбоя. Фактически, новый стандарт C ++ 0x решит эту проблему, указав, что std :: string использует непрерывный буфер, такой как std :: vector.

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

Тем не менее, я рекомендую и вектор.

6 голосов
/ 14 января 2009

Я использую std::string для этого тоже, и никогда не было проблем с ним.

Один «указатель», о котором я только что получил резкое напоминание в куске кода вчера: при создании строки из блока двоичных данных используйте форму конструктора std::string(startIter, endIter), а не форму std::string(ptr, offset, length) - последний предполагает, что указатель указывает на строку в стиле C и игнорирует все, что находится после первого нулевого символа (он копирует «до» указанного length, а не length символов).

3 голосов
/ 14 января 2009

Вы, безусловно, должны использовать какой-то контейнер char, но тот контейнер, который вы хотите использовать, зависит от вашего приложения.

У символов есть несколько свойств, которые делают их полезными для хранения двоичных данных: стандарт запрещает любые "дополнения" для типа данных char, что важно, поскольку это означает, что вы не получите мусора в своей двоичной структуре. Каждый символ также гарантированно равен одному байту, что делает его единственным простым старым типом данных (POD) с заданной шириной (все остальные указаны в терминах верхних и / или нижних границ).

Обсуждение вопроса о подходящем контейнере stl для хранения символов хорошо выполняется Дагом выше. Какой из них вам нужен, полностью зависит от вашего варианта использования. Если вы просто держите блок данных, через который вы проходите итерацию, без какого-либо специального поиска, добавления / удаления или соединения, я бы предпочел вектор, который делает ваши намерения более ясными, чем std :: string, который будут принимать многие библиотеки и функции содержит строку в стиле c с нулевым символом в конце.

...