Каждая переменная существует в памяти вашего компьютера.Память организована в байтах.
Когда вы пишете код на C ++, вы можете непосредственно читать эти байты.Для структуры память для всех ее членов находится в одном непрерывном фрагменте (хотя между каждым членом могут быть пробелы).
Итак, если я объявлю:
struct foo {
char x;
char y;
short z;
int q;
};
Тогда когдаЯ создаю struct foo
, я получаю следующий макет в памяти (всего 8 байт в большинстве систем):
xyzzqqqq
Первый байт x
, второй y
, третий и четвертыйвместе z
, а последние четыре - q
.
Итак, объект уже"сериализован" - у вас есть набор байтов, которые его представляют.Это все, что вам нужно отправить по сети: информация, которая представляет структуру данных.
Причина, по которой вы пишете свой собственный сериализатор, заключается в том, что вы можете изменить способом, которымобъект читается или записывается (например, что, если я добавил поле к struct foo
?), потому что вам нужно обмениваться данными между машинами, где структура памяти отличается (какой байт z
представляет «самую значительную» частьчисло?), или потому что вы хотите сериализовать только часть структуры (что, если бы у нас было некоторое свободное пространство между членами?).
Но, по сути, причина, по которой вы отправляете «данные типа char»потому что все на вашем компьютере может быть представлено таким образом.Я не буду вдаваться в доказательства Тьюринга о кодировке символов, но это математическая уверенность в том, что любая часть знаний может быть закодирована как серия единиц и нулей.
В более конкретном смысле, способ, которым вы помещаете данные вполе «char data» пакета находится на memcpy
от того места, где данные в данный момент находятся в буфере.Поэтому, если бы у меня было char* target
, я мог бы написать в него struct foo x
таким образом:
memcpy(target, &x, sizeof(struct foo));
Или я мог бы сделать это более осторожно, написав каждое поле:
memcpy(target, &x.x, 1);
memcpy(target+1, &x.y, 1);
memcpy(target+2, &x.z, sizeof(short));
memcpy(target+4, &x.q, sizeof(int));
&
- это адрес оператора, если вы еще не знали.Поэтому я пишу с адреса каждого члена в какое-то смещение в пределах target
и записываю число байтов, равное длине представления переменной члена.
Принятый ответ на ваш последний вопрос указализ-за того, что это упрощение: когда вы отправляете многобайтовое целое число по сети, вам нужно беспокоиться о endianness (порядок байтов).Итак, что вы на самом деле делаете, это:
memcpy(target, &x.x, 1);
memcpy(target+1, &x.y, 1);
*((short*)(target+2)) = htons(x.z);
*((int*)(target+4)) = htonl(x.q);
Это будет обрабатывать обращение байтов соответствующим образом для преобразования из порядка байтов хоста в порядка сетевых байтов .Очевидно, что значения длиной в один байт являются невосприимчивыми.