 |
iptr, но вместо присваивания числа 421 переменной ivar, это зна-
чение присваивается по указателю *iptr. Каков результат ? Точно
такой же, как и в предыдущей программе. Почему ? Потому что выра-
жения *iptr и ivar суть одна и та же ячейка памяти - поэтому в
этом случае оба оператора заносят значение 421 в одну и ту же
ячейку памяти.
- 485,486 -
Динамическое распределение
-----------------------------------------------------------------
Изменим еще раз нашу программу:
#include
main()
{
int *iptr;
iptr = (int *) malloc(sizeof(int));
*iptr = 421;
printf("Содержимое iptr: %p\n", iptr);
printf("Адресуемое значение: %d\n",*iptr);
}
Эта версия позволяет вполне обойтись без описания переменной
ivar, которое непременно фигурировало в предыдущих примерах.
Вместо адреса этой переменной iptr присваивается значение (тоже
адрес некоторой ячейки памяти), возвращаемое некоторой функцией,
которая называется malloc, и описана в библиотеке alloc.h (отсюда
появление директивы #include в начале программы). Затем по этому
адресу присваивается значение 421 и переменная *iptr вновь, как и
в предыдущем примере, принимает значение 421.
Обратите внимание, что если вы теперь выполните программу,
то получите иное значение iptr, чем раньше, но значение *iptr ос-
танется равным 421.
Разберем теперь, что же делает оператор
iptr = (int *) malloc(sizeof(int));
Разобьем его на части:
- выражение sizeof(int) возвращает количество байтов,
требуемое для хранения переменной типа int; для компи-
лятора Турбо Си, работающего на IBM PC, это возвращае-
мое значение равно 2.
- функция malloc(num) резервирует num последовательных
байтов доступной (свободной) памяти в компьютере, а за-
тем возвращает начальный адрес размещения в памяти этой
последовательности байтов.
- 487,488 -
- выражение (int *) указывает, что этот начальный адрес
суть указатель на данные типа int. Выражение такого ви-
да известно как выражение приведения типа (type
casting). В данном случае Турбо Си не требует обяза-
тельного его применения. Но в связи с тем, что для дру-
гих компиляторов Си это выражение является обязатель-
ным, при его отсутствии вы получите сообщение об
ошибке:
" Non-portable pointer assignment."
(Непереносимое в другие системы присваивание указателя)
Из соображений переносимости программного обеспечения,
лучше всегда предусматривайте явное приведение типов в
своих программах.
- наконец, адрес, полученный с помощью функции malloc,
запоминается в iptr. Таким образом, вами получена дина-
мически созданная целая переменная к которой вы можете
обращаться при помощи идентификатора *iptr.
Весь этот блок можно описать следующим образом: "выделить в
памяти компьютера некоторый участок для переменной типa int, за-
тем присвоить начальный адрес этого участка переменной iptr, яв-
ляющейся указателем на переменную типа int".
Необходимо ли все это? Да. Почему? Потому что без этого у
вас нет гарантии, что iptr указывает на свободный участок памяти.
iptr будет содержать некоторое значение, которое вы будете ис-
пользовать в качестве адреса, но вам не будет известно, не ис-
пользуется ли уже этот раздел памяти для других целей. Правило
использования указателей простое: указатель всегда должен иметь
адрес до своего использования в программе.
Иными словами, не присваивайте целое значение *iptr без
предварительного присвоения адреса в iptr.
- 489,490 -
Указатели и функции
-----------------------------------------------------------------
В прошлой главе мы объяснили, как объявлять параметры функ-
ций. Возможно теперь вам более понятна причина использования ука-
зателей в качестве формальных параметров функции, значения кото-
рых вы можете изменять.
Рассмотрим, например, следующую функцию:
void swap(int *a, int *b)
{
int temp;
temp = *a; *a = *b; *b =temp;
}
Эта функция swap (обмен) объявляет два формальных параметра
a и b, как указатели на некие данные типа int. Это означает, что
функция swap работает с адресами целых переменных (а не с их зна-
чениями). Поэтому будут обработаны данные, адреса которых переда-
ны функции во время обращения к ней.
Далее представлена программа, вызывающая swap:
main()
{
int i,j;
i = 421;
j = 53;
printf("До обращения: i=%4d, j=%4d\n",i,j);
swap(&i,&j);
printf("После обращения: i =%4d, j=%4d\n",i,j);
}
Вы видите, что эта программа действительно заменяет значение
i на значение j (переставляет их местами). Заменим эту программу
на аналогичную ей, раскрыв процедуру swap в теле программы:
main()
{
int i,j;
- 491,492 -
int *a,*b,temp;
i = 421;
j = 53;
printf("До обработки: i = %4d j = %4d\n",i,j);
a = &i;
b = &j;
temp = *a; *a = *b; *b =temp;
printf("После обработки: i = %4d j = %4d\n",i,j);
}
Эта программа, конечно, приводит к тому же результату, что и
предыдущая, поскольку не отличается от нее. При вызове функции,
swap(&i,&j) значения двух фактических параметров (&i,&j) присваи-
ваются двум формальным параметрам (a и b), обрабатываемым непос-
редственно операторами функции swap.
Адресная арифметика
-----------------------------------------------------------------
Каким образом вам необходимо поступить, если вы хотите так
модифицировать программу, чтобы iptr указывала на три переменных
типа int вместо одной? Далее представлено одно из возможных реше-
ний:
#include
main()
{
#define NUMINTS 3
int *list,i;
list = (int *) calloc(NUMINTS,sizeof(int));
*list = 421;
*(list+1) = 53;
 |
|