Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
9
Добавлен:
17.04.2015
Размер:
20.42 Кб
Скачать

Лабораторная работа по теме «Указатели".

Теоретические сведения.

Указатель – это переменная, содержащая адрес другой переменной.

Получение адреса

px = &x;

Теперь px «указывает» на x.

Операция получения адреса & применима только к переменным и элементам массива.

Адрес временных значений, таких как &(x + 1), получить нельзя.

Разыменование

Унарная операция * позволяет обратиться к значению по его адресу.

Выражение *px означает то же, что любая целая переменная, и с ним можно поступать, как с целой переменной, например,

*px = 5;

(*px)++;

printf("%d", *px);

Типы указателей

Типов указателей столько, сколько типов переменных, и указатель может указывать только на переменную своего типа. Исключение составляет указатель void*, который может указывать на переменную любого типа, но к нему нельзя применять операцию разыменования.

Описание указателей имеет мнемонический характер.

int i; // целая переменная

int *p; // указатель на целое

int i, *p; // совместное объявление

Любое описание (не только указателей) состоит из декларации и одного или более деклараторов.

Мнемоника в том, что выражения для деклараторов в контексте программного кода должны иметь тип декларации.

double d, *pd, m[10], F();

Указатели и аргументы функций

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

void Swap(int *px, int *py)

{

int t = *px;

*px = *py;

*py = t;

}

void main()

{

int a = 5, b = 6;

Swap(&a, &b);

}

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

Любые действия, которые можно выполнить с помощью индексов массива, можно сделать и с помощью указателей.

int m[10], *pm;

pm = &m[0];

int x = *pm; // x = m[0]

int y = *(pm + 1); // y = m[1]

При разборе выражения a[i] компилятор Си преобразует его к виду *(a+i).

Различие между именем массива и указателем в том, что указатель является переменной, а имя массива – константой. Т.е.

pm = &x; // можно

m = &x; // нельзя!

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

Когда массив передается в функцию, то на самом деле передается указатель.

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

void F(int m[], int n);

void F(int *m, int n);

Арифметика указателей

Пример. Сложить элементы массива, используя арифметику указателей.

int Sum(int *m, int n) {

int s = 0, *end = m + n;

while (m < end)

s += *(m++);

return s;

}

void main()

{

int a[] = {1,2,3};

printf("%d\n", Sum(a, 3));

}

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

#define NULL 0;

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

Допустимые операции

p + i – смещение вперед

p – i – смещение назад

p1 – p2 – расстояние

p1 == p2 – равенство

p1 != p2 – неравенство

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

Многомерные массивы и массивы указателей

Многомерные – аналог прямоугольных в С#.

char m[2][3] = { {'1','2','3'}, {'4','5','6'} };

Массивы указателей – аналог невыровненных в С#.

char *a[2];

a[0] = (char*)malloc(sizeof(int) * 3);

a[1] = (char*)malloc(sizeof(int) * 33);

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

Пример. Функция foreach.

int m[] = {1,2,3,4,5,6};

void foreach(int m[], int n, int (*f)(int)) {

for (int i = 0; i < n; i++)

m[i] = f(m[i]);

}

int F(int x) {

return x * x;

}

void main()

{

foreach(m, 6, &F);

}

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

int m[] = {1,2,3,4,5,6};

void foreach(int m[], int n, int f(int)) {

for (int i = 0; i < n; i++)

m[i] = f(m[i]);

}

int F(int x) {

return x * x;

}

void main()

{

foreach(m, 6, F);

}