Страница 58 из 69
О НЕКОТОРЫХ ОСОБЕННОСТЯХ ПРОГРАММИРОВАНИЯ НА СИ
-----------------------------------------------------------------
Приятно знать, что вы добрались и до этой главы. В прошлой
главе мы дали вам представление о программировании на Турбо Си,
вполне достаточное для пробуждения вашей любознательности. Теперь
вы готовы углубиться в некоторые тонкости и тайны программирова-
ния на Си, в чем мы и постараемся вам помочь.
В этой главе...
-----------------------------------------------------------------
В этой главе мы предлагаем вам изучить следующее:
- структуры данных, включающие указатели, массивы и структуры;
- оператор switch;
- команды передачи управления, включающие return, break,
- 477,478 -
continue, goto и условный оператор (?:);
- потоки и поток ввода-вывода: как считывать из и записывать
на дисковый файл или встроенное оборудование;
- стиль программирования на Си и обзор новых расширений Си;
- некоторые общие "ловушки" для программистов на Си.
Обзор структур данных
-----------------------------------------------------------------
Основные типы данных мы рассмотрели в прошлой главе. К ним
относятся числа - целые и с плавающей точкой, символы и их разно-
видности. Теперь мы поговорим о том, как использовать эти элемен-
ты для построения СТРУКТУР данных. Но сначала мы исследуем одно
из важнейших понятий Си - указатели.
Указатели
-----------------------------------------------------------------
Большинство переменных, рассмотренных вами, в основном со-
держали данные, т.е. текущую информацию для работы вашей програм-
мы. Но иногда важнее знать место расположения данных, чем собс-
твенно их значение. Именно для этого и используются указатели.
Если вы имеете слабое представление о понятиях "адрес" и
"память", то вам просто необходимо ознакомиться с их кратким опи-
санием, которое мы приводим ниже.
- 479,480 -
Итак, ваш компьютер содержит в своей памяти (часто называе-
мой RAM - Random Access Memory - память произвольного доступа)
вашу программу и совокупность данных. На самом нижнем уровне па-
мять вашего компьютера состоит из бит, мельчайших электронных
схем которые могут "запомнить" (пока компьютер включен) одно из
двух значений, обычно интерпретируемое как "0" и "1".
Восемь битов группируются в один БАЙТ. Большим группам битов
как правило, присваивается имя: обычно два байта составляют СЛО-
ВО, четыре байта составляют ДЛИННОЕ СЛОВО; и для IBM PC шестнад-
цать байт составляют ПАРАГРАФ.
Каждый байт в памяти вашего компьютера имеет собственный
уникальный адрес, так же, как каждый дом на любой улице. Но в от-
личие от большинства домов, последовательные байты имеют последо-
вательные адреса: если данный байт имеет адрес N, то предыдущий
байт имеет адрес N-1, а следующий - N+1.
УКАЗАТЕЛЬ - это переменная, содержащая адрес некоторых дан-
ных, а не их значение. Зачем это нужно?
Во-первых, мы можем использовать указатель места расположе-
ния различных данных и различных структур данных. Изменением ад-
реса, содержащегося в указателе, вы можете манипулировать (созда-
вать, считывать, изменять) информацию в различных ячейках. Это
позволит вам, например, связать несколько зависимых структур дан-
ных с помощью одного указателя.
Во-вторых, использование указателей позволит вам создавать
новые переменные в процессе выполнения программы. Си позволяет
вашей программе запрашивать некоторое количество памяти (в бай-
тах), возвращая адреса, которые можно запомнить в указателе. Этот
прием известен как ДИНАМИЧЕСКОЕ РАСПРЕДЕЛЕНИЕ; используя его, ва-
ша программа может приспосабливаться к любому объему памяти, в
зависимости от того как много (или мало) памяти доступно вашему
компьютеру.
В-третьих, вы можете использовать указатели для доступа к
различным элементам структур данных, таким как массивы, строки
или структуры. Указатель, в сущности, указывает место в памяти
вашего компьютера (а используя смещение относительно начального
адреса можно указать целый сегмент памяти), в котором размещены
те или иные данные. Индексируя указатель, вы получаете доступ к
- 481,482 -
некоторой последовательноси байтов, которая может представлять,
например, массив или структуру.
Теперь вы, несомнено, убеждены в удобстве указателей. А как
же их использовать в Си? Для начала вы должны их объявить.
Рассмотрим следующую программу:
main()
{
int ivar,*iptr;
iptr = &ivar;
ivar = 421;
printf("Размещение ivar: %p\n",&ivar);
printf("Содержимое ivar: %d\n", ivar);
printf("Содержимое iptr: %p\n", iptr);
printf("Адресуемое значение: %d\n",*iptr);
}
В ней объявлены две переменные: ivar и iptr. Первая, ivar -
это целая переменная, т.е. содержащая значение типа int. Вторая,
iptr - это указатель на целую переменную, следовательно она со-
держит АДРЕС значения типа int. Можно также сказать, что перемен-
ная iptr - это указатель, так как перед ее описанием стоит звез-
дочка (*). В языке Си эта звездочка называется косвенным
оператором.
В основном, данная программа делает следующее:
- адрес переменной ivar присваисвается iptr
- целое значение 421 присваивается ivar
Адресный оператор (&), как это было показано в предыдущей
главе, позволяет получить адрес, по которому размещено значение
переменной ivar.
Введя эту программу в свой компьютер и выполнив ее, вы
получите следующий результат:
Размещение ivar: 166E
Содержимое ivar: 421
Содержимое iptr: 166E
- 483,484 -
Адресуемое значение: 421
Первые две строки указывают адрес и содержимое ivar. Третья
представляет адрес, содержащийся в iptr. Как видите, это адрес
переменной ivar, т.е. место в памяти, где ваша программа решила
создать переменную с идентификатором ivar. В последней строке пе-
чатается то, что хранится по этому адресу - те же самые данные,
которые уже присвоены переменной ivar.
Заметим, что в третьем обращении к функции printf используе-
тся выражение iptr, содержимое которого есть адрес ivar. В пос-
леднем обращении к printf используется выражение *iptr, которое
позволяет получить данные, хранящиеся по этому адресу.
Рассмотрим теперь небольшую вариацию предыдущей программы:
main()
{
int ivar,*iptr;
iptr = &ivar;
*iptr = 421;
printf("Размещение ivar: %p\n",&ivar);
printf("Содержимое ivar: %d\n", ivar);
printf("Содержимое iptr: %p\n", iptr);
printf("Адресуемое значение: %d\n",*iptr);
}
В этой программе также адрес переменной ivar присваивается