Лекция 4. Указатели

Знакомство с указателями

Понятие указателя

Адресация

  • прямая
  • косвенная

Прямой доступ

int count=154;
count++;
printf("Address: %x\n",&count);

Понятие указателя

Косвенный доступ

int count=154;
int*p=&count;
(*p)++;
printf("Address: %x\n",p);

p - переменная-указатель, используемая для хранения адреса переменной count.

Указатели на указатели

Косвенный доступ второго порядка

int count=154;
int*p=&count;
int**pp=&p;
  • p - указатель на тип int.
  • pp - указатель на указатель на тип int.
  • *pp- доступ к значению р
  • **pp – доступ к значению count

Применение указателей

  • передача параметров в функцию;
  • манипуляции адресами;
  • повышение эффективности кода;
  • организация динамических массивов;
  • формирование связей между объектами;

Особенности работы с указателями

Особенности:

  • указатель всегда объявляется на определенный тип;
  • размер указателя определяется разрядностью ОС;
  • при работе с разнотипными указателями необходимо использовать явное преобразование типа;

Можно объявить указатель на:

  • обычную скалярную переменную;
  • массив;
  • функцию;
  • указатель.

Объявление и инициализация указателей

Объявление и присваивание

int val1=0;
char ch='#';
double pi=3.14159265358;

int*p1;
char* p2;
double*p3;

p1=&val1;
p2=&ch;
p3=π

Объявление и инициализация

int val1=0;
char ch='#';
double pi=3.14159265358;

int *p1 =&val1;
char* p2 =&ch;
double *p3 =π

Второй вариант более безопасен, поскольку указатели не остаются без значений.

Пример работы с указателями (1)

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

int a=10,b=20;
int * pa=&a;
int * pb=&b;
int *temp;

printf("A=%d, B=%d\n",*pa,*pb);
printf("A=%d, B=%d\n",a,b);
temp=pa;
pa=pb;
pb=temp;
printf("A=%d, B=%d\n",*pa,*pb);
printf("A=%d, B=%d\n",a,b);

Пример работы с указателями (2)

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

int a=10,b=20,temp;
int * pa=&a;
int * pb=&b;

printf("A=%d, B=%d\n",*pa,*pb);
printf("A=%d, B=%d\n",a,b);
temp=*pa;
*pa=*pb;
*pb=temp;
printf("A=%d, B=%d\n",*pa,*pb);
printf("A=%d, B=%d\n",a,b);

Адресная арифметика

Операции над значениями указателей

Инкремент значения указателя

int a=0;
int * pa=&a;
pa++;
_images/ris4.png

Адрес увеличивается на размер типа данных в байтах

A2=A1+sizeof(int)

В результате указатель содержит адрес ячейки, вплотную прилегающей к исходной (обращение к ней опасно!)

Наиболее естественно использовать указатели с массивами:

_images/ris5.png

Инкремент значения указателя

int arr[]={1,2,3,4,5,6,7,8,9,0};
int *p1=&arr[0];
int *p2=&arr[9];
int size=p2-p1;

Связывание указателя с началом массива:

int *p=arr;
int *p1=&arr[0];
int *p2=p1+2;
int size=p2-p1;

Указатели и массивы

Связывание указателей с массивами

Указатели и массивы

Суммирование элементов массива

int arr[N]={1,2,3,4,5,
           6,7,8,9,0};
int *p=arr,sum=0;
while(p<arr+N)
   sum+=*p++;

Обработка строки

char str[N]="a very simple string";
char *p=str;
while(*p)
   putchar(*p++);

Массивы указателей

Массив указателей на char можно использовать для создания массива строковых констант

int *pa[10];
char *str[5];

char *mes[]={"Divide by 0" ,
                       "File not found" ,
                       "Not enough memory"
                       };
...
printf("%s", mes[errcod]);
...

Организация массива указателей

Что означают следующие выражения?

  • mes
  • mes[0]
  • *mes
  • mes+1
  • *mes+1
  • *(mes+1)
  • mes[1][1]
  • *mes[1][1]

Взаимозаменяемость

Имена массивов и указателей могут быть эквиваленты

  • arr[i] = *(arr+i)
  • arr[i][j]=*(arr[i]+j)
  • arr[i][j]=*(*(arr+i)+j)

Следующие выражения эквивалентны:

int arr[10];
arr[5] = 5 [arr] = *( arr+ 5)

int *p=arr;
p[5] = *(p+5)
int arr[5];
int *parr=arr;
*(parr+2)=0;
parr[2]++;

Имя массива может выступать в операции как имя указателя-константы, а имя указателя может действовать как имя массива с использованием []

Строки и указатели

Строка может быть задана как локальный массив или как указатель на ‘’далёкую’’ текстовую константу

char str[]="A simple string";
char *s="A simple string";
_images/ris6.png

Фиксация указателей

Для предотвращения изменения данных, связанных с указателем используют указатели на константу:

const char * p="don't change me!";

При попытке изменить данные по адресу в p появится ошибка компиляции.

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

char * const p="don't lost me!";

При попытке изменить адрес в указателе появится ошибка компиляции.

Константный указатель на константу:

const char * const p="don't touch me!";

Указатели на массив

Указатели на массивы

int arr[5][2];
int *pa[10]; // объявление массива из 10 указателей на int
int (*parr)[2]; // объявление указателя на массив из 2 int

parr=arr;   // связывание указателя с массивом

Указатели на функцию

Указатель на функцию хранит адрес точки входа в тело функции.

Указатель объявляется на функцию с определенным возвращаемым значением и набором параметров.

// математические функции с похожими прототипами
double  sin(double x);
double  cos(double x);
double  exp(double x);
// объявление указателя на функцию
double (*pfun)(double);
// использование указателя
pfun=sin; // pfun=&sin;
// теперь можно вызывать функцию с помощью указателя
y=pfun(x); // y=(*pfun)(x);

Указатель можно связать с другой функцией и при обращении к его значению будет вызвана другая функция

Вопросы для самоконтроля

  • Какие бывают способы адресации?
  • В чем состоит прямой доступ к переменной?
  • Что такое косвенная адресация?
  • Как объявить и использовать указатель на указатель?
  • Для чего используются указатели?
  • В чем состоят особенности работы с указателями?
  • На что объявить указатель?
  • Как объявить и инициализировать указатель?
  • В чем опасность непроинициализированных указателей?
  • Как работает инкремент значения указателя?
  • Как связать указатель и массив?
  • Что представляет из себя массив указателей?
  • В каком смысле говорят о взаимозаменяемости массивов и указателей?
  • Чем отличается строка, заданная с помощью указателя от строки, заданной с помощью массива?
  • Что такое указатель на константу? Как его объявить?
  • Что такое константный указатель? Как его записать?
  • Что такое константный указатель на константу? Как его использовать?
  • Как объявить указатель на массив?
  • Чем отличается указатель на массив от массива указателей?
  • Где используются указатели на массивы?
  • Как объявить и использовать указатель на функцию?