Добавил:
СПбГУТ * ИКСС * Программная инженерия Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Язык Си. Лабораторные работы / Справочник. Часть 2 (СПбГУТ).doc
Скачиваний:
47
Добавлен:
10.09.2019
Размер:
801.79 Кб
Скачать

1.37.4. Указатели и квалификатор restrict

По словам Герберта Шилдта, автора книги [6]: “ Одной из самых важных новинок стандарта С99 является квалификатор restrict (ограниченный)”. Он применяется только к указателям. Предполагается, что такой указатель является единственным указателем на объект, на который он установлен. Этот квалификатор в стандарте С99 был добавлен в прототипы многих библиотечных функций, определённых еще в стандарте С89.

1.37.5. Связь между указателями и массивами

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

int a[5], b[5];

Имена массивов a и b, рассматриваемые как константные указатели, будут иметь тип int* const. Таким образом, имя одномерного массива является константным указателем на первый элемент массива. В связи с этим оператор a = b не должен успешно компилироваться. Это объясняет невозможность использования в языке Си оператора присваивания для копирования массивов.

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

double m[3][4];

Компилятор языка Си рассматривает такой массив, как одномерный, рассчитанный на хранение трех элементов, которыми являются одномерные массивы. Каждый из этих массивов имеет тип double[4]. С учетом этого имя массива является константным указателем типа double (* const p)[4]. Скобки здесь необходимы, иначе это будет объявлением массива указателей.

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

int a[5]; int*p; p = a; a[2] = 10; p[2] = 20; // Допустимо. теперь a[2] == 20

Следует учитывать, что указатели можно использовать для выполнения любых операций, в которых применяется оператор индексирования. Пусть имеются одномерный массив int a[5] и двумерный массив int m[2][3]. Для рассматриваемого случая выражение a[1] можно заменить выражением *(a + 1), а выражение m[1][2] – заменить выражением *(*(m + 1) + 2).

1.37.6. Указатели – параметры функций

Применение параметров-указателей в языке Си позволяет программисту вернуть в точку вызова функции результаты вычислений, которые в ней получены. Эта возможность обусловлена тем, что указатели позволяют организовать связь между функцией и памятью, выделенной в вызывающей функции. Хорошим примером такого использования является функция swap().

Пример. Обмен значений двух переменных типа double.

Постановка задачи. Даны две переменные x и y типа double. Написать функцию, позволяющую выполнить обмен значений этих переменных.

Решение. Разработка интерфейса. Предположим, что в точке вызова функции объявлены две переменные типа double c именами x и y. Для получения доступа к этим переменным разрабатываемая функция должна иметь два параметра – указателя типа double*. Учитывая, что возврат результатов берут на себя параметры, сама функция может не иметь возвращаемого значения. С учетом этого заголовок функции будет иметь вид:

void swap(double* p1, double* p2);

Разработка алгоритма. Для организации обмена в теле функции swap необходимо выполнить три действия:

1. Объявить локальную переменную double temp и переписать в нее значение переменной x, разыменовав указатель p1.

2. Скопировать содержимое переменной у в переменную x. Учесть, что синонимом x является *p1, а синонимом y – *p2.

3. Скопировать в переменную y значение x, сохраненное в temp.

Программный код разработанной функции и клиентский код.

void swap(double *p1, double *p2) { double temp = *p1; //В temp сохраняем значение «x» *p1 = *p2; //Копируем в «x» значение «y» *p2 = temp;//Копируем в «y» значение «x», //сохраненное в temp } // Клиентский код int main(void) { double x = 2, y = 3; swap(&x, &y); //На время выполнения вызова swap() //адрес переменной x копируется

//в параметр p1,а адрес переменной y - в

//параметр p2 }