Как распределить память в структуре, чтобы она была непрерывной - PullRequest
0 голосов
/ 19 октября 2019

Я хочу сделать непрерывную структуру в памяти? У меня есть эта структура:

struct QUESTION
{
    unsigned char *qname;
    unsigned short qtype;
    unsigned short qclass;
};

И я хочу выделить * qname как строку, и я также хочу, чтобы qtype и qclass были в памяти сразу после строки * qname. Мне нужно сохранить его в буфер:

unsigned char buf[65536];
struct QUESTION *qu = NULL;
qu = (struct QUESTION *)&buf;

И мне нужно убедиться, что он будет в буфере в следующем порядке: полностью * qname string, qtype и qclass. Моя проблема в том, что я думаю, что когда я выделяю память (с помощью malloc ()) для * qname (что я должен сделать, потому что мне нужно поместить туда строку), он не будет выделять память в buf, но где-то еще. Так как же мне выделить память так, как я хочу? Спасибо! :)

1 Ответ

1 голос
/ 19 октября 2019

Многие учителя не тратят время, чтобы полностью объяснить, почему для строк нужны «указатели», а не просто наличие строки.

Целое число составляет 4 байта (или 2, или 8, в зависимости откомпьютер). Значение с плавающей запятой составляет 6 байтов (или 4, или 8, также в зависимости). Проблема в том, что строка не имеет известного количества байтов. Будь то "Hi!" или "My name is Inigo Montoya", вы не можете знать, какова длина строки во время компиляции . Таким образом, сохранить строку или передать ее в функцию нельзя. Вместо этого вы помещаете его в другое место и используете указатель на эту строку - это то, чем является *.

A char * - указательдо первого символа строки. По соглашению, каждый символ после также является частью строки - до тех пор, пока вы не получите NUL. Это имеет значение 0, которое отличается от '0'. Вы хотите, чтобы строка могла содержать номер телефона ("The phone number is 1-800-800-8000"), поэтому вы должны иметь возможность хранить '0' (значение которого 48). NUL буквально имеет значение 0 - как char, оно записано как '\0'. К счастью, компилятор ставит NUL в конце каждой строки для вас.

Итак, у вас есть строка;скажем const char question[] = "What do you get when you multiply six by nine?". Видите, как я объявил question как массив? Многие учителя говорят, что вы должны объявить это как const char * (или const char * const) - но это неправильно! Почему? Если вы объявляете const char * const hi = "Hi!", то вы объявляете четыре байта для хранения значений 'H', 'i', '!' и '\0' где-то или другим, а затем четыре больше байта для хранения указателя, указывающего на них, называемого hi! И это не то, что вы хотите.

Теперь question сидит в памяти, в (скажем) 0x1000. Любой указатель на эту строку имеет размер 4 байта (или 2, или 8 ...), но все они содержат значение 0x1000. Ваша структура QUESTION имеет указатель на строку, поэтому QUESTION составляет 4 байта, плюс два лота по 2 байта (short s - 2 байта), в общей сложности 8 байтов.

То, что вы хотите - это большая структура, которая содержит следующее:

"What do you get when you multiply six by nine?\0" qtype qclass

И вот где вам нужновыполнять манипуляции с памятью, а не простые структуры C / C ++.

Один из методов - поместить фиксированную часть структуры сначала , за которой следует переменная строка. В зависимости от вашего компилятора, вы можете сделать это следующим образом:

struct QUESTION
{
    unsigned short qtype;
    unsigned short qclass;
    unsigned char  qname[];
};

Видите, что я сделал? Первые две переменные имеют известный размер, поэтому нужно идти первым. Последняя переменная является массивом - но неизвестного размера. Вы не можете следовать за этим с другими переменными. Обратите внимание, что только последние компиляторы позволяют это! Более ранние компиляторы позволяли вам использовать qname[0] для указания «неизвестного размера» - ваш компилятор может жаловаться на любой из этих ...

Предполагая, что вы скомпилировали вышеприведенное, у вас теперь есть еще две проблемы:

  1. Что такое sizeof(QUESTION)?
  2. Как мне получить актуальный вопрос в qname[]?

Ответ на (1) очевиден- но не то, что вы хотите. Это просто 4 - размер двух short с. Это имеет смысл, потому что вы не указали размер массива. Таким образом, если вы используете malloc(), вам нужно указать размер QUESTION плюс размер question:

QUESTION *q = (QUESTION *)malloc(sizeof(QUESTION) + strlen(question) + 1);

Посмотрите, что я здесь сделал? Я попросил malloc() выделить достаточно памяти для структуры QUESTION, плюс длина question, плюс еще один для финала NUL.

Затем вы можете делать такие вещи, как:

q->qtype = PHILOSOPHICAL;
q->qclass = FUNNY;

Но для того, чтобы на самом деле получить вопрос в q, вам нужно переместить байтыот 0x1000 до точки q:

strcpy(q->qname, question);

Это будет копировать символы из question в q в нужном месте - включая окончательный NUL

...