Многие учителя не тратят время, чтобы полностью объяснить, почему для строк нужны «указатели», а не просто наличие строки.
Целое число составляет 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]
для указания «неизвестного размера» - ваш компилятор может жаловаться на любой из этих ...
Предполагая, что вы скомпилировали вышеприведенное, у вас теперь есть еще две проблемы:
- Что такое
sizeof(QUESTION)
? - Как мне получить актуальный вопрос в
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