русс | укр

Языки программирования

ПаскальСиАссемблерJavaMatlabPhpHtmlJavaScriptCSSC#DelphiТурбо Пролог

Компьютерные сетиСистемное программное обеспечениеИнформационные технологииПрограммирование

Все о программировании


Linux Unix Алгоритмические языки Аналоговые и гибридные вычислительные устройства Архитектура микроконтроллеров Введение в разработку распределенных информационных систем Введение в численные методы Дискретная математика Информационное обслуживание пользователей Информация и моделирование в управлении производством Компьютерная графика Математическое и компьютерное моделирование Моделирование Нейрокомпьютеры Проектирование программ диагностики компьютерных систем и сетей Проектирование системных программ Системы счисления Теория статистики Теория оптимизации Уроки AutoCAD 3D Уроки базы данных Access Уроки Orcad Цифровые автоматы Шпаргалки по компьютеру Шпаргалки по программированию Экспертные системы Элементы теории информации

Указатели


Дата добавления: 2013-12-23; просмотров: 1619; Нарушение авторских прав


Понятие указателя знакомо читателю из разд. 3.21, в котором описывается ссылочный тип данных в Паскале. Смысл этого понятия в Си/Си++ остается тем же: указатель — это адрес поля памяти, занимаемого программным объектом.

Пусть в программе определены три переменные разных типов:

int a=5;

char с='G';

float г=1.2Е8;

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

 

Операция & — адрес. Применение этой операции к имени переменной дает в результате ее адрес в памяти. Для переменных из данного выше примера: &а равно FFCO, &с - FFC2, &r - FFC3.

Описание указателей. Для хранения адресов используются переменные типа «указатель». Формат описания таких переменных следующий:

тип *имя_переменной

Примеры описания указателей:

int *pti; char *ptc; float *ptf;

После такого описания переменная pti может принимать значение указателя на величину целого типа; переменная ptc предназначена для хранения указателя на величину типа char; переменная ptf — на величину типа float.

Указателям могут присваиваться значения адресов объектов только того типа, с которым они описаны. В нашем примере допустимы операторы

pti=&a; ptc=&c; ptf=&r;

В результате указатели примут следующие значения:

pti - FFCO, ptc - FFC2, ptf - FFC3.

Как и для других типов данных, значения указателей могут инициализироваться при описании. Например:

int a=5; int *pti=&a;

char c='G'; char *ptc=&c;

float r=1.2E8; float *ptf=&r;

В заголовочном файле stdio.h определена константа — нулевой указатель с именем NULL. Ее значение можно присваивать указателю. Например:

ptf=NULL;

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



Использованный в описаниях указателей символ * (звездочка) в данном контексте является знаком операции разадресации. С ее помощью можно сослаться через указатель на соответствующую переменную.

После приведенных выше описаний в записи выражений этой программы взаимозаменяемыми становятся а и *pti, с и *ptc, r и *ptf. Например, два оператора

х=а+2; и x=*pti+2;

тождественны друг другу. В результате выполнения оператора

cout<<*pti<<a;

на экран выведется 55.

Операции над указателями. Записывая выражения и операторы, изменяющие значения указателей, необходимо помнить главное правило: единицей изменения значения указателя является размер соответствующего ему типа.

Продемонстрируем это правило на определенных выше указателях. Выполнение операторов

pti=pti+l; или pti++;

изменит значение указателя pti на 2, в результате чего он примет значение FFC2. В результате выполнения оператора pti--; значение указателя уменьшится на 2 и станет равным FFBE.

Аналогично для указателей других типов:

ptc++; увеличит значение указателя на 1;

ptf++; увеличит значение указателя на 4.

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

В следующем примере функция swap() производит обмен значениями двух переменных величин, заданных своими указателями в аргументах.

void swap(int *a,int *b)

{ int с;

c=*a; *a=*b; *b=c;

}

Если в основной программе имеется следующий фрагмент:

int х=1,у=2;

swap(&x,&у) ;

printf("x=%d y=%d",x,y);

то на экран будет выведено:

х=2 у=1

т. е. переменные х и у поменялись значениями.

Все выглядит очень похоже на то, как если бы в Паскале использовали процедуру обмена с var-параметрами. И тем не менее передача параметров здесь тоже происходит по значению, только этими значениями являются указатели. После обращения к функции указатель а получил адрес переменной х, указатель b — адрес переменной у. После этого переменная х в основной программе и разадресованный указатель *а в функции оказываются связанными с одной ячейкой памяти; так же — у и *b.

Таким образом, можно сделать вывод о том, что использование указателей в параметрах функции позволяет моделировать работу процедур.

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

Имя массива трактуется как указатель-константа на массив.

Пусть, например, в программе объявлен массив:

int Х[10];

В таком случае Х является указателем на нулевой элемент массива в памяти компьютера. В связи с этим истинным является отношение

Х==&Х[0]

Отсюда следует, что для доступа к элементам массива кроме индексированных имен можно использовать разадресованные указатели по принципу:

имя [индекс] тождественно * (имя + индекс)

Например, для описанного выше массива х взаимозаменяемы следующие обозначения элементов:

Х[5], или *(Х+5), или *(5+Х).

Напоминаем, что для указателей работают свои правила сложения. Поскольку Х — указатель на величину целого типа, то Х+5 увеличивает значение адреса на 10.

В языке Си символ [ играет роль знака операции сложения адреса массива с индексом элемента массива.

Из сказанного должно быть понятно, почему индекс первого элемента массива всегда нуль. Его адрес должен совпадать с адресом массива:

Х[0]==*(Х+0)

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

int X[10],Y[10];

то оператор присваивания X=Y будет ошибочным. Такое возможно в Паскале, но недопустимо в Си. Пересылать значения одного массива в другой можно только поэлементно.

Теперь рассмотрим двумерные массивы. Пусть в программе присутствует описание:

int Р[5][10];

Это матрица из пяти строк и десяти чисел в каждой строке. Двумерный массив расположен в памяти в последовательности по строкам. По-прежнему Р является указателем-константой на массив, т. е. на элемент Р[0][0]. Индексированное имя Р[i] обозначает i-ю строку. Ему тождественно следующее обозначение в форме разадресованного указателя:

*(P+i*10)

Обращение к элементу массива Р[2][4] можно заменить на *(Р+2*10+4). В общем случае эквивалентны обозначения:

P[i] [j] и *(P+i*10+j)

Здесь дважды работает операция «квадратная скобка». Последнее выражение можно записать иначе, без явного указания на длину строки матрицы Р:

*(*(P+i)+j).

Очевидно, что по индукции для ссылки на элемент трехмерного массива A[i][j][k] справедливо выражение

* (* (* (A+i)+j)+k) и т.д.

Массив как параметр функции. Обсудим эту тему на примерах.

Пример 1. Составим программу решения следующей задачи. Дана вещественная матрица А[М][N]. Требуется вычислить и вывести евклидовы нормы строк этой матрицы.

Евклидовой нормой вектора называют корень квадратный из суммы квадратов его элементов:

 

Если строку матрицы рассматривать как вектор, то данную формулу надо применить к каждой строке. В результате получим M чисел.

Определение функции вычисления нормы произвольного вектора:

double Norma(int n, double X[])

{ int i;

double S=0;

for(i=0; i<n; i++) S+=X[i]*X[i];

return sqrt(S);

}

Заголовок этой функции можно было записать и в такой форме:

double Norma(int n, double *X)

В обоих случаях в качестве второго параметра функции используется указатель на начало массива. Во втором варианте это более очевидно, однако оба варианта тождественны.

При вызове функции Norma() в качестве второго фактического параметра должен передаваться адрес начала массива (вектора).

Рассмотрим фрагмент основной программы, использующей данную функцию для обработки матрицы размером 5 х 10.

 

В обращении к функции второй фактический параметр A[i] является указателем на начало i-й строки матрицы А.

Пример 2. Заполнить двумерную матрицу случайными целыми числами в диапазоне от 0 до 99. Отсортировать строки полученной матрицы по возрастанию значений. Отсортированную матрицу вывести на экран.

#include <iostream.h>

#include <iomanip.h>

 

Здесь все выглядит совсем как при использовании процедур на Паскале. Обратите внимание на прототип и заголовок функции Matr() . В них явно указывается вторая размерность параметра-матрицы. Первую тоже можно указать, но это необязательно. Как уже говорилось выше, двумерный массив рассматривается как одномерный массив, элементами которого являются массивы (в данном случае — строки матрицы). Компилятору необходимо «знать» размер этих элементов. Для массивов большей размерности (3, 4 и т.д.) в заголовках функций необходимо указывать все размеры, начиная со второго.

При обращении к функции Matr() фактическим параметром является указатель на начало двумерного массива А, а при обращении к функции Sort () — указатели на начало строк.

В итоге тестирования программы получен следующий результат.

Матрица до сортировки:

 

Матрица после сортировки:

 

Упражнения

1. В оперативной памяти вектор int Х[10] расположен, начиная с адреса B7F0. Какие значения примут выражения:

а) х+1; б) х+5; в) х-4?

2. В программе объявлен массив:

int Р[]={0,2,4,5,6,7,9,12);

Какие значения примут выражения:

а) Р[3]; б) *Р; в) *(Р+4); г) *(Р+P[2])?

3. Составить функцию сортировки значений трех переменных а, b, с в порядке возрастания.

4. Составить функцию заполнения целочисленного одномерного массива случайными значениями в диапазоне от 0 до N.

5. Составить функцию вычисления среднего значения элементов вещественного одномерного массива. Использовать эту функцию в основной программе, определяющей в матрице номер строки с наибольшим средним значением.



<== предыдущая лекция | следующая лекция ==>
Примеры обработки двумерных массивов | Лекция №4. Формализация задачи проектирования.


Карта сайта Карта сайта укр


Уроки php mysql Программирование

Онлайн система счисления Калькулятор онлайн обычный Инженерный калькулятор онлайн Замена русских букв на английские для вебмастеров Замена русских букв на английские

Аппаратное и программное обеспечение Графика и компьютерная сфера Интегрированная геоинформационная система Интернет Компьютер Комплектующие компьютера Лекции Методы и средства измерений неэлектрических величин Обслуживание компьютерных и периферийных устройств Операционные системы Параллельное программирование Проектирование электронных средств Периферийные устройства Полезные ресурсы для программистов Программы для программистов Статьи для программистов Cтруктура и организация данных


 


Не нашли то, что искали? Google вам в помощь!

 
 

© life-prog.ru При использовании материалов прямая ссылка на сайт обязательна.

Генерация страницы за: 0.005 сек.