Поскольку мои комментарии становились все длиннее и длиннее, вот полный ответ:
Ваш буфер char *
должен хранить длину строки в первых X байтах (например, как это делает Паскаль). После этой длины идут строковые данные, которые могут содержать любые символы, которые вам нравятся. После этого следующие X байтов сообщают вам длину строки next . И так далее, до конца, который ограничен пустой строкой (т. Е. Последние байты X утверждают, что следующая строка имеет нулевую длину, и ваше приложение воспринимает это как сигнал о прекращении поиска дополнительных строк).
Одним из преимуществ является то, что вам не нужно сканировать строковые данные - поиск следующей строки в начале первой строки занимает O (1) времени, а поиск количества строк в вашем списке занимает O (n). ) время, но все равно будет невероятно быстрым (если O (n) неприемлемо, вы можете обойти это, но я не думаю, что стоит начинать прямо сейчас).
Еще одним преимуществом является то, что строковые данные могут содержать любой символ, который вам нравится. Это может быть мошенничеством - если ваша строка может содержать символ NUL, вы можете безопасно извлечь ее, но вы должны быть осторожны, чтобы не передавать ее в строковую функцию C (например, strlen()
или strcat()
), которая увидит NUL-символ как конец ваших данных (которые могут быть или не быть). Вам придется полагаться на memcpy()
и арифметику указателей.
Проблема заключается в значении X (количество байтов, которое вы используете для хранения длины строки). Самым простым будет 1, который обойдет все проблемы с порядком байтов и выравниванием, но ограничит ваши строки 255 символами. Если это ограничение, с которым вы можете жить, отлично, но 255 мне кажется немного низким.
X может быть 2 или 4 байта, но вам необходимо убедиться, что у вас есть (беззнаковый) тип данных, который по крайней мере столько же байтов (stdint.h
uint16_t
или uint32_t
, либо, возможно, uint_least16_t
или uint_least32_t
). Лучшим решением было бы сделать X = sizeof(size_t)
, поскольку тип size_t
гарантированно сможет хранить длину любой строки, которую вы захотите сохранить.
Наличие X > 1
вводит выравнивание и, если переносимость сети является проблемой, порядковый номер. Самый простой способ прочитать первые X байтов как переменную size_t
- это преобразовать ваши данные char *
в size_t *
и просто разыменовать. Однако, если вы не можете гарантировать, что ваши char *
данные выровнены должным образом, в некоторых системах это будет нарушено. Даже если вы гарантируете выравнивание данных char *
, вам придется тратить несколько байтов в конце большинства строк, чтобы убедиться, что значение длины следующей строки выровнено.
Самый простой способ преодоления выравнивания - вручную преобразовать первые sizeof(size_t)
байтов в значение size_t
. Вам нужно будет решить, хотите ли вы, чтобы данные хранились в порядке байтов. Большинство компьютеров будут иметь непосредственный порядок байтов, но для ручного преобразования это не имеет значения - просто выберите один. Число 65537 (2 ^ 16 + 2), хранящееся в 4 байтах, с прямым порядком байтов, выглядит как { 0, 1, 0, 2 }
; little-endian, { 2, 0, 1, 0 }
.
Как только вы решили, что (неважно, выберите тот, который вам нравится), вы просто приводите первые X точек данных к unsigned char
с, затем к size_t
, затем делаете сдвиг битов по соответствующему показателю, чтобы поместить их в нужное место, а затем сложить их все вместе. В приведенных выше примерах 0 будет умножено на 2 ^ 32, 1 на 2 ^ 16, 0 на 2 ^ 8 и 2 на 2 ^ 0 (или 1), что приведет к 0 + 65536 + 0 + 2 или 65537. Там, вероятно, будет нулевая разница в эффективности между старшим и младшим порядком байтов, если вы будете выполнять ручное преобразование - я хочу отметить (снова), насколько я могу судить, выбор совершенно произвольный.
Ручное преобразование позволяет избежать проблем с выравниванием, и полностью обходит проблемы, связанные с порядком байтов между системами, поэтому данные, передаваемые с компьютера с прямым порядком байтов на компьютер с прямым порядком байтов, будут считываться одинаково. Все еще существует потенциальная проблема с передачей данных из системы, где sizeof(size_t) == 4
, в систему, где sizeof(size_t) == 8
. Если это проблема, вы можете либо а) отказаться от size_t
и выбрать инвариантный размер, либо б) кодировать (единственный байт - все, что вам нужно) значение sizeof(size_t)
для отправителя в качестве первого байта данных, и пусть получатель внесет необходимые изменения. Вариант а) может быть проще, но может вызвать проблемы (что, если вы выберете размер, слишком малый для учета устаревших компьютеров в вашей сети, и по мере их отказа у вас не хватит места для хранения данных?), Поэтому Я бы предпочел вариант б), так как он масштабируется с любой системой, на которой вы работаете (16-битная, 32-битная, 64-битная, возможно, даже в будущем 128-битная), но вам не понадобятся такие усилия .
</vomit>
Я даю читателю разобраться во всем этом беспорядке, который я только что написал.