Ключ в том, что вы должны понимать, что все в компьютере - это просто массив байтов (или слов, или двойных слов).
ZEN MASTER MUSTARD сидит за столом и смотрит на монитор, уставившись на сложную комбинацию, казалось бы, случайных символов. СТУДЕНТ приближается.
Студент: магистр? Могу ли я прервать?
Дзен Мастер Горчица: Вы ответили на свой вопрос, сын мой.
S: Что?
ЗММ: Задавая вопрос о том, чтобы прервать меня, вы прервали меня.
S: Ой, прости. У меня вопрос о перемещении конструкций разного размера с места на место.
ЗММ: Если это правда, тогда вам следует проконсультироваться с мастером, который преуспевает в таких вещах. Я предлагаю вам посетить Мастера DotPuft, который имеет большие знания в перемещении больших металлических конструкций, таких как следящие радары, с места на место. Master DotPuft также может заставить малейшие элементы тензодатчика с легким весом двигаться с силой дыхания голубя. Поверните направо, затем поверните налево, когда доберетесь до двери хай-бей. Там обитает Мастер DotPuft.
С: Нет, я имею в виду перемещение больших структур разного размера с места на место в памяти компьютера.
З.М.М.: Я могу помочь вам в этом начинании, если хотите. Опишите свою проблему.
S: В частности, у меня есть функция c, для которой я хочу принимать несколько различных типов структур (они будут представлять различные типы пакетов). Так что мои структурные пакеты будут переданы моей функции как void *. Но, не зная типа, я не могу их разыграть или вообще ничего не делаю. Я знаю, что это решаемая проблема, потому что sento () из socket.h делает именно это:
ssize_t sendto(int socket, const void *message, size_t length, int flags, const struct sockaddr *dest_addr,socklen_t dest_len);
где sendto будет называться как:
sendto(socketAddress, &myPacket, sizeof(myPacket), Other args....);
ZMM: Вы рассказали о своей проблеме Мастеру Zen MANTAR!
S: Да, он сказал: «Это просто указатель. Все в C это указатель». Когда я попросил его объяснить, он сказал: «Бок, бок, убирайся из моего офиса».
ЗММ: Действительно, вы говорили с мастером. Вам это не помогло?
S: Гм, нет. Затем я спросил мастера дзен Макс.
ЗММ: Мудрый он. Какой его совет был вам полезен?
S: Нет. Когда я спросил его о sendto (), он просто закрутил кулаки в воздухе. Это просто массив байтов. "
ZMM: Действительно, у Дзен Мастера Макса есть тау.
S: Да, у него есть тау, но как мне обращаться с аргументами функции типа void *?
ЗММ: Чтобы учиться, вы должны сначала отучиться. Ключ в том, что вы должны понимать, что все в компьютере - это просто массив байтов (или слов, или двойных слов). Если у вас есть указатель на начало буфера и его длину, вы можете отправить его куда угодно без необходимости знать тип данных, помещаемых в буфер.
S: ОК.
ZMM: рассмотрим строку читаемого человеком текста. «Вы планируете башню, которая пробьет облака? Сначала положите фундамент смирения». Это 82 байта в длину. Или, может быть, 164, если используется злой Unicode. Защитите себя от лжи Unicode! Я могу отправить этот текст в sendto (), указав указатель на начало буфера, который содержит строку, и длину буфера, например:
char characterBuffer[300]; // 300 bytes
strcpy(characterBuffer, "You plan a tower that will pierce the clouds? Lay first the foundation of humility.");
// note that sizeof(characterBuffer) evaluates to 300 bytes.
sendto(socketAddress, &characterBuffer, sizeof(characterBuffer));
ZMM: Обратите внимание, что число байтов символьного буфера автоматически рассчитывается компилятором. Число байтов, занимаемых любым типом переменной, относится к типу "size_t
". Вероятно, он эквивалентен типу "long
" или "unsinged int
", но зависит от компилятора.
S: Ну, а если я хочу отправить структуру?
ZMM: Тогда давайте отправим структуру.
struct
{
int integerField; // 4 bytes
char characterField[300]; // 300 bytes
float floatField; // 4 bytes
} myStruct;
myStruct.integerField = 8765309;
strcpy(myStruct.characterField, "Jenny, I got your number.");
myStruct.floatField = 876.5309;
// sizeof(myStruct) evaluates to 4 + 300 + 4 = 308 bytes
sendto(socketAddress, &myStruct, sizeof(myStruct);
S: Да, это здорово при передаче данных через сокеты TCP / IP. Но как насчет плохой функции приема? Как он может определить, отправляю ли я массив символов или структуру?
ZMM: Один из способов - перечислить различные типы данных, которые могут быть отправлены, а затем отправить тип данных вместе с данными. Мастера дзен называют это «метаданными», то есть «данными о данных». Ваша принимающая функция должна проверить метаданные, чтобы определить, какой тип данных (структура, число с плавающей запятой, массив символов) отправляется, а затем использовать эту информацию для приведения данных обратно в исходный тип. Сначала рассмотрим функцию передачи:
enum
{
INTEGER_IN_THE_PACKET =0 ,
STRING_IN_THE_PACKET =1,
STRUCT_IN_THE_PACKET=2
} typeBeingSent;
struct
{
typeBeingSent dataType;
char data[4096];
} Packet_struct;
Packet_struct myPacket;
myPacket.dataType = STRING_IN_THE_PACKET;
strcpy(myPacket.data, "Nothing great is ever achieved without much enduring.");
sendto(socketAddress, myPacket, sizeof(Packet_struct);
myPacket.dataType = STRUCT_IN_THE_PACKET;
memcpy(myPacket.data, (void*)&myStruct, sizeof(myStruct);
sendto(socketAddress, myPacket, sizeof(Packet_struct);
S: Хорошо.
ZMM: Теперь только мы пройдемся вместе с функцией приема. Он должен запросить тип отправленных данных и скопировать данные в переменную, объявленную этого типа. Простите, но я забыл точное значение функции recvfrom()
.
char[300] receivedString;
struct myStruct receivedStruct;
recvfrom(socketDescriptor, myPacket, sizeof(myPacket);
switch(myPacket.dataType)
{
case STRING_IN_THE_PACKET:
// note the cast of the void* data into type "character pointer"
&receivedString[0] = (char*)&myPacket.data;
printf("The string in the packet was \"%s\".\n", receivedString);
break;
case STRUCT_IN_THE_PACKET:
// note the case of the void* into type "pointer to myStruct"
memcpy(receivedStruct, (struct myStruct *)&myPacket.data, sizeof(receivedStruct));
break;
}
ЗММ: Достигли ли вы просветления? Во-первых, кто-то спрашивает у компилятора размер данных (например, количество байтов), которые должны быть переданы в sendto()
. Вы отправляете тип исходных данных, которые также отправляются вместе. Затем получатель запрашивает тип исходных данных и использует его для вызова правильного преобразования типа «указатель на пустоту» (универсальный указатель) к типу исходных данных (int, char [], структура, и др.)
S: Хорошо, я попробую.
ЗММ: иди с миром.