- •1.1. Понятие указателя
- •Int* ptrInt;
- •1.2. Синтаксис описания указателя
- •1.3. Адрес переменной и значение переменной по адресу
- •Int main()
- •1.4. Адресная арифметика
- •1.5. Обращение к элементам массива
- •1.6. Приведение типов при работе с указателями
- •1.7. Работа с динамическими переменными
- •2.2. Передача параметров по адресу
- •Void swap(int* a, int* b)
- •2.3. Передача больших объемов данных
- •2.4. Инициализация указателей
- •2.5. Применение ссылок при работе с функциями
- •Void swap(int& a, int& b)
- •Int main()
- •Int Carrage(int* cr);
2.2. Передача параметров по адресу
Чтобы заставить функцию работать так, как нужно, следует передавать в нее не значения переменных x и y, а их адреса. Для этого необходимо выполнить следующее:
1. Формальные параметры функции объявить указателями (для того, чтобы в них можно было записывать адреса фактических параметров).
Void swap(int* a, int* b)
{
int temp = *a;
*a = *b;
*b = temp;
}
2. Изменить вызов функции. Т.к. теперь она ожидает получить указатели, то необходимо передавать адреса фактических параметров x и у:
swap(&x, &y);
Теперь в функцию передаются адреса. И работа ведется относительно переданных адресов:
Таким образом, если функция должна менять значение переменной, нужно передавать ей адрес этой переменной.
Примечание: У тех, кто только начинает программировать на C, есть одна распространенная ошибка. При вводе с клавиатуры с помощью функции scanf() они передают значение переменной, а не ее адрес. В этом случае функция scanf(), используя полученное значение в качестве адреса, будет работать неверно.
2.3. Передача больших объемов данных
Предположим, что нам нужно передать в функцию целое число типа int. Таким образом, мы передаем в функцию sizeof(int) байт. Обычно это 4 байта (размер будет зависеть от архитектуры компьютера и компилятора). 4 байта — это немного, они будут выделены в стеке, т.к. имеет место передача по значению.
В современных задачах функциям приходится иметь дело с большими объемами данных, это могут быть десятки и сотни мегабайт. При передаче по значению в таком случае возникает 2 проблемы:
1. Каждый раз при вызове функций придется затрачивать время на копирование всего объема передаваемых данных.
2. Стек программы не всегда в состоянии вместить такой объем.
Поэтому большой объем данных передается в функцию не по значению, а по адресу. Все массивы, даже если они состоят из одного элемента, передаются по адресу.
2.4. Инициализация указателей
Указатели — это мощный инструмент. Указатели эффективно и быстро работают, но являются не слишком безопасными, т.к. вся ответственность за их использования ложится на разработчика. Не стоит забывать, что человеку свойственно ошибаться.
Представим ситуацию.
int x;
int* p;
В большинстве компиляторов C и С++ неинициализированные локальные переменные имеют случайное значение. Глобальные обнуляются. Если мы захотим разыменовать указатель и присвоить ему значение, то нужно записать следующую команду:
*p = 10;
Синтаксически все записано верно, тем не менее это выражение содержит ошибку, которая проявится в процессе выполнения - неинициализированный указатель p хранит случайный адрес. Мы можем попытаться получить значение по этому адресу и что-то туда записать. Но совсем не факт, что нам можно что-то делать с памятью по этому адресу.
Указатели нужно обнулять. Для этого есть специальное значение NULL. Для языка С это:
int* p = NULL;
В C++ обычно можно инициализировать указатель нулем.
int* p = 0;
Это возможно из-за того, что в библиотечных файлах языка, дано определение для NULL.
#define NULL (void*)0
То есть NULL — это нулевой указатель.
Давайте решим следующую задачу. Папа Карло дал Буратино 5 яблок. Злой Карабас Барабас отобрал 3 яблока. Сколько яблок осталось у Буратино?
Ответ: неизвестно. Так как нигде не сказано, сколько яблок у Буратино было изначально.
Мораль: При объявлении переменных всегда выполняйте их инициализацию.
