- •Аннотация
- •Предисловие
- •Г л а в а 1. Введение в указатели
- •§1. Понятие указателя. Операции разыменования и разадресации
- •§2. Инициализация и присваивание указателей
- •§3. Передача параметров функций с помощью указателей.
- •§4. Распределение динамической памяти.
- •4.1. Операция new
- •4.2. Операция delete
- •Упражнения, тесты.
- •Указатели и массивы
- •§1. Связь указателей и массивов
- •1.1. Указатели и одномерные массивы
- •1.2. Указатели и матрицы
- •Int MyFun (int *X, int n)
- •§3. Операции над указателями при работе с массивами.
- •3.1. Арифметические операции
- •3.2. Операции сравнения.
- •§4. Использование операций над указателями при работе с одномерными массивами
- •4.1. Использование индексов
- •4.2. Указатель в качестве параметра цикла
- •4.3. Использование указателя и индекса
- •§5. Строки.
- •5.1. Общая характеристика строк.
- •5.2. Примеры алгоритмов работы со строками.
- •5.3. Анализ строковых функций.
- •§6. Использование операций над указателями при работе со статической матрицей.
- •Упражнения, тесты.
- •Массивы указателей
- •§1. Статический массив указателей
- •§2. Частично динамическая матрица.
- •Int arr_of_size[n];
- •§3. Статический массив строк
- •§4. Динамический массив указателей
- •4.1. Указатель на указатель
- •4.2. Динамические “матрицы”.
- •Int *arr_of_size;
- •4.3. Передача матрицы в функцию
- •Int a[10]; FunArr1(a, 10,…);
- •Упражнения, тесты.
- •Задачи второго среднего уровня.
- •Структуры и другие типы, определяемые пользователем
- •§1. Структуры
- •Объявление структуры
- •1.2. Работа со структурой.
- •1.3. Вложенные структуры и статические массивы в структурах
- •1.4. Статический массив структур
- •§2. Cтруктуры и указатели
- •2.1. Указатели в структуре.
- •2.2. Указатели на структуру
- •2.3. Динамический массив структур
- •2.4. Ссылка на структуру.
- •2.5. Указатели и вложенные структуры
- •§3. Cтруктуры и функции
- •3.1. Передача полей структуры в функцию.
- •Void MyFun1 (int X, float &y, int *u1, float *u2, char *s);
- •3.2. Передача всей структуры в функцию
- •Void Fun1 (tst s,…);
- •Void Fun2 (tst & s,…);
- •Void Fun3 (tst* s,…);
- •§4. Cтруктуры и классы.
- •§5. Объединения.
- •Представление вещественных чисел в памяти компьютера.
- •§6. Поля битов (битовые поля)
- •Ввод в ы в о д
- •Symbol Code16 Code10 Code2
- •§7. Перечисления
- •Какие из строк (//1 – //9) правильные?
§2. Инициализация и присваивание указателей
Кроме приведенного раньше способа инициализации (int* p= &t;) переменную-указатель можно проинициализировать, использую ранее определённую переменную-указатель. Например, корректным будет следующее объявление:
int* q=p;
что равносильно
int *q; q=p; (а не *q=p;)
После этого q и p будут содержать один и тот же адрес, адрес ячейки t. Поэтому *p и *q —это одно и тоже целочисленное значение t. Заметим, что присваивания *q=p; и *p=q; некорректны. В качестве упражнения объяснить, почему.
Нельзя переменную-указатель проинициализировать константой, т.е. недопустимо следующее, например, объявление
int *p=1000; // Ошибка!!!
Запрещены также и такие присваивания:
a) p=1000; т.к. 1000 — константа;
б) int k; cin>>k; p=k; так как k — это целое введённое число, а p — указатель.
Это связано с тем, что система сама должна “найти” свободную ячейку и её адрес присвоить нашей переменной. Наша “помощь” при этом будет отвергнута.
Переменную-указатель нельзя также вводить. Поэтому cin>>p; недопустимо.
Не будет ошибкой, если во время объявления указателя мы его не проинициализируем. Но до его первого использования надо не забыть это сделать, например, с помощью присваивания. Правильным будет, таким образом, следующий фрагмент:
float f=1.1, *p1, *p2; p1=&f; p2=p1; cout<<(*p1)<<” “<<(*p2);
В результате будет выведено дважды одно и то же число 1.1.
Присваивание указателей допустимо только в том случае, если оба указателя содержат адрес ячеек, в которых хранятся данные одного и того же типа. Поэтому следующий фрагмент не приведёт к желаемому результату 12.34:
double x=12.34, y; int *w; w=&x; y=*w; printf (“%f”, y); // Ошибка!!!
Во время компиляции w=&x; появится сообщение об ошибке, связанное с несоответствием типов. Но если int* w; заменить на double * w, то программа выведет 12.34.
§3. Передача параметров функций с помощью указателей.
Из первого семестра мы знаем два способа передачи параметров (не массивов) в функции: передача по значению и по ссылке. В случае, когда полученное или изменённое в функции значение необходимо возвратить в вызывающую функцию, можно использовать третий способ передачи параметра: с помощью указателя.
В следующем примере для повторения и сравнения приведены все три способа передачи параметров.
П р и м е р (+).
/* 1) В первой функции MyMax1 рассматривается передача параметров с помощью указателей. Изменённые значения с их помощью возвращаются из функции в вызвавшую её функцию. Обратим внимание, что в тексте функции для доступа к числам, находящимся в ячейках с адресами x и у, необходимо использовать операцию разадресации. */
void MyMax1 (int* x, int* y)
{ if (*x>*y) { int t; t=*x; *x=*y; *y=t; }
}
/* 2) При передаче параметров с помощью ссылочного типа (повторение, см. первый семестр) изменённые значения также возвращаются из функции в вызвавшую её функцию. */
void MyMax2 (int &x, int &y)
{ if (x>y) { int t; t=x; x=y; y=t; }
}
/* 3) При передаче параметров по значению (повторение) изменённые значения не возвращаются из функции в вызвавшую её функцию. */
void MyMax3 (int x, int y)
{ if (x>y) { int t; t=x; x=y; y=t; }
}
/*Заметим, что в последних двух вариантах в тексте функции никакие операции над указателями (ни *, ни &) не используются.
Вызов функций зависит от того, работаем мы с простыми переменными (не указателями) или с указателями. */
int main()
{
/* В первом варианте вызова функций используем не указатели, а “простые” переменные. */
int a, b; cout<<"Two int numbers"<<endl;
/* Стандартная функция для ввода scanf и “наша” функция MyMax1 в качестве формальных параметров использует указатели. Поэтому в качестве фактических параметров в функцию передаём &a и &b, т. е. адреса переменных a и b. */
scanf("%d %d", &a, &b);
MyMax1(&a, &b);
cout<<"a="<<a<<"; b="<<b<<endl;
/* Если ввели, например, 5 и 2, то выведем a=2; b=5. Почему? В x передаётся &a — адрес ячейки a. Поэтому a в main и *x в MyMax1 (аналогично b в main и *y в MyMax1) — это одни и те же ячейки памяти, только по-разному называются. */
scanf("%d %d", &a,&b);
MyMax2(a,b);
cout<<"a="<<a<<"; b="<<b<<endl;
/*Аналогично, если ввели, например, 5 и 2, то выведем a=2; b=5, так как a в main и x в MyMax2 (b в main и y в MyMax3) — это одни и те же ячейки памяти, только по-разному называются. */
scanf("%d %d", &a,&b);
MyMax3(a,b);
cout<<"a="<<a<<"; b="<<b<<endl;
/* Здесь a и b не изменились после выполнения функции независимо от их значений, так как a в main и x в MyMax3 (b и y) — это разные ячейки памяти. */
/* Во втором варианте используем указатели, значения которых инициализируем двумя способами. */
int *p1=&a, *p2=new int; //Операцию new смотри в следующем параграфе.
/* Так как функции scanf и MyMax1 требуют, чтобы были переданы адреса, то операция разадресации не используется. В противном случае с этой операцией получили бы не адрес. А поскольку, кроме этого, переменные объявлены как указатели, то при вызове не используем операцию & (взятие адреса). Переменные p1 и p2 уже являются адресами без этой операции. Поэтому вызов будет следующим*/
scanf ("%d %d",p1,p2);
MyMax1(p1,p2);
/* Наоборот, при работе с ячейками, адреса которых в p1 и в p2, надо не забыть использовать операцию разадресации. Без неё были бы выведены адреса, а не числа, с которыми мы работаем. */
cout<<endl<<"*p1="<<(*p1)<<"; *p2="<<(*p2);
scanf ("%d %d",p1,p2);
/* Так как функции MyMax2 и MyMax3 в качестве параметров адреса не используют, то в качестве фактических параметров должны записать содержимое ячеек p1 и p2, которое получается как результат операции разадресации (*). */
MyMax2(*p1,*p2);
cout<<endl<<"*p1="<<(*p1)<<"; *p2="<<(*p2);
scanf ("%d %d",p1,p2);
MyMax3 (*p1,*p2);
cout<<endl<<"*p1="<<(*p1)<<"; *p2="<<(*p2);
getch(); return 0;
}