Читать интересную книгу Язык Си - руководство для начинающих - M. УЭИТ

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 68 69 70 71 72 73 74 75 76 ... 98

"Беги, Cпот, беги! - "сказал Дик.

     Cтроковые константы размещаются в статической памяти. Вся фраза в кавычках является указателем на место в памяти, где записана строка. Это аналогично использованию имени массива, служащего указателем на расположение массива. Если это действительно так, то как выглядит оператор, который выводит строку?

/* строки в качестве указателей */

main( )

{

printf("%s, %u, %c n", "We", "love", *"figs");

     Итак, формат %s выводит строку We. Формат %u выводит целое без знака. Если слово "love" является указателем, то выдается его значение, являющееся адресом первого символа строки. Наконец, *"figs" должно выдать значение, на которое ссылается адрес, т. е. первый символ строки "figs". Произойдет ли это на самом деле? Да, мы получим следующий текст:

We, 34, f

Ну, вот! Давайте теперь вернемся к строкам, находящимся в символьных массивах.

Массивы символьных строк и их инициализация

     При определении массива символьных строк необходимо сообщить компилятору требуемый размер памяти. Один из способов сделать это - инициализировать массив при помощи строковой константы. Так как автоматические массивы нельзя инициализировать, необходимо для этого использовать статические или внешние массивы. Например, оператор

char m1[ ] = "Только ограничьтесь одной строкой.";

инициализировал внешний (по умолчанию) массив m1 для указанной строки. Этот вид инициализации является краткой формой стандартной инициализации массива

char m1[ ] = {'Т', 'о', 'л', 'ь', 'к', 'о', ' ',

                   'о', 'г', 'р', 'а', 'н', 'и', 'ч',

                   'ь', 'т', 'e', 'с', 'ь', ' ', 'о',

                   'д', 'н', 'о', 'й', ' ', 'с', 'т',

                   'р' 'о', 'к', 'о', 'й', ' .', ''};

(Обратите внимание на замыкающий нуль-символ. Без него мы имеем массив символов, а не строку.) Для той и другой формы (а мы рекомендуем первую) компилятор подсчитывает символы и таким образом получает размер массива.

Как и для других массивов, имя m1 является указателем на первый элемент массива:

m1 == &m1[0], *m1 == 'Т', и *(m1 + l) == m1[1] == 'о',

Действительно, мы можем использовать указатель для создания строки. Например:

char *m3 = " n Достаточно обо мне - как вас зовут?";

Это почти то же самое, что и

static char m3[ ] = "n Достаточно обо мне - как вас зовут?" ;

Оба описания говорят об одном: m3 является указателем строки со словами " Как вас зовут?" . В том и другом случае сама строка определяет размер памяти, необходимой для ее размещения. Однако вид их не идентичен.

Массив или указатель

     В чем же тогда разница между этими двумя описаниями? Описание с массивом вызывает создание в статической памяти массива из 38 элементов (по одному на каждый символ плюс один на завершающий символ ''. Каждый элемент инициализируется соответствующим символом. В дальнейшем компилятор будет рассматривать имя m3 как синоним адреса первого элемента массива, т. е. &m3[0]. Следует отметить, что m3 является константой указателя. Вы не можете изменить m3, так как это означало бы изменение положения (адрес) массива в памяти. Можно использовать операции, подобные m3+1, для идентификации следующего элемента массива, однако нe разрешается выражение ++m3. Опeратор увеличения можно использовать с именами переменных, но не констант.

     Форма с указателем также вызывает создание в статической памяти 38 элементов для запоминания строки. Но, кроме того, выделяется еще одна ячейка памяти для переменной m3, являющейся указателем. Сначала эта переменная указывает на начало строки, но ее значение может изменяться. Поэтому мы можем использовать операцию увеличения; ++m3 будет указывать на второй символ строки (Д). Заметим, что мы не объявили *m3 статической неременной, потому что мы инициализировали не массив из 38 элементов, а одну переменную типа указатель. Не существует ограничений на класс памяти при инициализации обычных переменных, не являющихся массивом.

     Существенны ли эти отличия? Чаще всего нет, но все зависит от того, что вы пытаетесь делать. Посмотрите несколько примеров, а мы возвращаемся к вопросу выделения памяти для строк.

Массив и указатель: различия

     В нижеследующем тексте мы обсудим различия в использовании описаний этих двух видов:

static char heart[ ] ="Я люблю Тилли !";

char *head ="Я люблю Милли!";

     Основное отличие состоит в том, что указатель heart является константой, в то время как указатель head - переменной. Посмотрим, что на самом деле даст эта разница.

Вo-пepвых, и в том и в другом случае можно использовать операцию сложения с указателем.

for(i = 0; i < 6; i++ )

putchar(*(heart + i));

putchar('n');

for(i = 0; i < 6; i++ )

putchar(*(head + i));

putchar('n');

в результате получаем

Я люблю Я люблю

Но только в случае с указателем можно использовать операцию увеличения:

while( *(head) != '') /* останов и конце строки */

putchar(*(head++ )); /* печать символа и перемещение указателя */

дают в результате:

Я люблю МИЛЛИ!

Предположим, мы хотим заменить head на heart. Мы можем cказать

head = heart /* теперь head указывает на массив hеart */

но теперь мы можем сказать

heart =  head; /* запрещенная конструкция */

     Ситуация аналогична х = 3 или 3 = х; левая часть оператора присваивания должна быть именем переменной. В данном случае head = heart; не уничтожит строку Милли, а только изменит адрес, записанный в head. Вот каким путем можно изменить обращение к heart и проникнуть в сам массив:

heart[8] = 'М';

или

*(heart + 8) = 'М';

Элементы массива (но не имя) являются переменными

Явное задание размера памяти

     Иной путь выделения памяти заключается в явном ее задании. Во внешнем описании мы могли бы скачать:

char m1[44] = "Только ограничьтесь одной строкой.";

вместо

char m1[ ] = "Только ограничьтесь одной строкой.";

     Можно быть уверенным, что число элементов по крайней мере на один (это снова нуль-символ) больше, чем длина строки. Как и в других статических или внешних массивах, любые неиспользованные элементы автоматически инициализируются нулем (который в символьном виде является нуль-символом, а не символом цифры нуль).

РИС. 13.2. Инициализация массива.

     Отметим, что в нашей программе массиву name задан размер:

char name [81];

     Поскольку массив name должен читаться во время работы программы, у компилятора нет другого способа узнать заранее, сколько памяти нужно выделить для массива. Это нс символьная константа, в которой компилятор может посчитать символы. Поэтому мы предположили, что 80 символов будет достаточно, чтобы поместить в массив фамилию пользователя.

Массивы символьных строк

     Обычно бывает удобно иметь массив символьных строк. В этом случае можно использовать индекс для доступа к нескольким разным строкам. Покажем это на примере:

static char *mytal[LIM] = {"Быстро складываю числа",

                                "Точно умножаю",

                                "Записываю данные",

                                "Правильно выполняю команды",

                                "Понимаю язык Си"};

     Разберемся в этом описании. Вспомним, что LIM имеет значение 5, мы можем сказать, что mytal является массивом, состоящим из пяти указателей на символьные строки. Каждая строка символов, конечно же, представляет собой символьный массив, поэтому у нас есть пять указателей на массивы. Первым указателем является mytal[0], и он ссылается на первую строку. Второй указатель mytal[1] ссылается на вторую строку. Каждый указатель, в частности, ссылается на первый символ своей строки:

*mytal[0] == 'Б', *mytal[1] == 'Т', mytal[2] == 'З'

1 ... 68 69 70 71 72 73 74 75 76 ... 98
На этом сайте Вы можете читать книги онлайн бесплатно русская версия Язык Си - руководство для начинающих - M. УЭИТ.
Книги, аналогичгные Язык Си - руководство для начинающих - M. УЭИТ

Оставить комментарий