Страница 67 из 69
Использование указателей может быть наиболее трудно для по-
нимания начинающему программисту на Си. Когда надо использовать
указатели, а когда нет? Когда использовать косвенный оператор
(*)? Когда использовать адресный оператор (&)? И как избежать
ошибок операционной системы во время выполнения программы?
На все эти и многие другие вопросы ответ будет дан в этом
разделе.
Использование неинициализированных указателей
-----------------------------------------------------------------
Одна серьезная опасность таится в присвоении значения по ад-
ресу, содержащемуся в указателе, без первоначального присвоения
адреса этому указателю.
Например:
main()
{
int *iptr;
*iptr = 421;
printf("*iptr = %d\n",*iptr);
}
Эта ловушка опасна тем, что программа, содержащая ее, может
быть "верна" и компилятор может не выдать никаких сообщений во
время компиляции такой программы. В примере, указанном выше, ука-
затель iptr имеет некоторый произвольный адрес, по которому запо-
минается значение 421. Эта программа настолько мала, что шанс что
-нибудь затереть в памяти с ее помощью ничтожно мал, однако в
больших программах возрастает вероятность разрушения других
данных, поскольку вполне возможно, что по адресу iptr уже
хранится другая информация. Если вы используете модель самой
маленькой памяти (tiny), в которой сегменты программы и данных
занимают одну и ту же область памяти, то вы подвергаете себя
риску испортить свой же загрузочный модуль. Поэтому старайтесь не
рубить сук на котором вы сами же сидите и внимательно пишите
программы, использующие указатели.
- 553,554 -
Строки
-----------------------------------------------------------------
Как мы уже говорили, строки можно объявить как указатели на
тип данных char или как массивы данных типа char. Речь шла о том,
что между ними существует лишь одно важное различие: если вы ис-
пользуете указатель на данные типа char, память для строки не ре-
зервируется; если вы используете массив данных, то память резер-
вируется автоматически и переменная - имя массива - содержит
адрес начала зарезервированной области памяти.
Недостаточное понимание этой разницы может привести к двум
типам ошибок. Рассмотрим следующую программу:
main()
{
char *name;
char msg[10];
printf("Назовите свое имя.");
scanf("%s",name);
msg = "Здраствуйте, ";
printf("%s %s:",msg,name);
}
На первый взгляд все законно, немного неуклюже, но вполне
допустимо. Однако здесь допущены две ошибки.
Первая ошибка содержится в выражении:
scanf("%s",name).
Выражение само по себе законно и корректно. Поскольку name
является указателем на char, вам не нужно ставить перед ним ад-
ресный оператор (&). Однако память для name не зарезервирована;
строка, которую вы введете, будет записана по какому-то случайно-
му адресу, который окажется в name. Компилятор обнаружит это, но
поскольку эта ситуация не приведет к сбою выполнения программы
(т.к. строка все же будет сохранена), то компилятор выдаст лишь
предупреждающее сообщение, но не ошибку.
"Possible use of 'name' before definition"
("Возможно использование 'name' до ее определения")
- 555,556 -
Вторая ошибка содержится в операторе msg = "Здравствуйте,".
main()
{
char *name;
char msg[10];
name = (char *) malloc (10);
printf("Назовите свое имя");
scanf("%s",name);
strcpy(msg,"Здраствуйте,");
printf("%s%s",msg,name);
}
Вызов функции malloc выделяет отдельно 10 байтов памяти и
- 557,558 -
присваивает адрес этого участка памяти name, решив нашу первую
проблему. Функция strcpy производит посимвольное копирование из
строковой константы string "Здравствуйте," в массив msg.
Разница между присваиванием (=) и равенством (==)
----------------------------------------------------------------
В языках Паскаль и Бейсик проверка на равенство производится
выражением
if (a = b).
Для Си эта конструкция допустима, но имеет несколько иное
значение. Посмотрите на этот фрагмент программы:
if (a = b) puts("Равно");
else puts("Не равно");
Если это программа на Паскале или Бейсике, то вы можете
предполагать, что будет напечатано "Равно", если a и b имеют оди-
наковое значение и "Не равно" в противном случае.
Иначе происходит с программой на Си, где выражение a = b оз-
начает "Присвоить значение b переменной a", и все выражение при-
нимает значение b. Поэтому во фрагменте, приведенном выше, прис-
воится значение b переменной a, а затем напечатается "Равно",
- 559,560 -
если b имеет нулевое значение, в противном случае - "Не равно".
Правильное решение следующее:
if (a == b) puts("Равно");
else puts("Не равно");