Указатели – аргументы функций. Передача аргументов по указателю.
Так как в C передача аргументов функциям осуществляется "по значению", вызванная процедура не имеет непосредственной возможности изменить переменную из вызывающей программы. Что же делать, если вам действительно надо изменить аргумент? Например, программа сортировки захотела бы поменять два нарушающих порядок элемента с помощью функции с именем swap. Для этого недостаточно написать
swap(a, b); |
определив функцию swap при этом следующим образом:
void swap(int x, int y) { int temp;
temp = x; x = y; y = temp; }
|
Из-за вызова по значению swap не может воздействовать на аргументы а и b в вызывающей функции.
К счастью, все же имеется возможность получить желаемый эффект. Вызывающая программа передает указатели подлежащих изменению значений:
swap(&a, &b); |
Так как операция & выдает адрес переменной, то &a является указателем на a. В самой swap аргументы описываются как указатели и доступ к фактическим операндам осуществляется через них
void swap(int*px, int*py) { int tmp;
temp = *px; *px = *py; *py = temp; } |
Указатели и const
Использование ключевого слова const с указателями характеризуется рядом тонких моментов. Применять ключевое слово const с указателями можно двумя разными способами. Первый — заставить указатель указывать на константный объект, тем самым предотвращая модификацию объекта через указатель. Второй способ — сделать сам указатель константным, запретив переустанавливать его на что-нибудь другое. Теперь обратимся к деталям.
Сначала объявим pt как указатель на константу:
int age = 39;
const int * pt = &age;
Это объявление устанавливает, что pt указывает на const int (в данном случае — 39). Таким образом, вы не сможете использовать pt для изменения этого значения. Другими словами, значение *pt является константным и не может быть изменено:
*pt += 1; // НЕПРАВИЛЬНО, потому что pt указывает на const int
cin >> *pt; // НЕПРАВИЛЬНО по той же причине
Теперь проанализируем тонкие моменты. Такое объявление pt не обязательно значит, что значение, на которое он указывает, действительно является константой; это значит лишь, что значение постоянно, только когда к нему обращаются через pt. Например, pt указывает на age, a age — не константа. Вы можете изменить значение age непосредственно, используя переменную age, но вы не можете изменить это значение через указатель pt:
*pt = 20; // НЕПРАВИЛЬНО, потому что pt указывает на const int
age = 20; // ПРАВИЛЬНО, потому что age не объявлено как const
В предыдущих примерах вы присваивали адрес обычной переменной обычному указателю. В этом примере вы присваиваете адрес обычной переменной указателю на константу. Это оставляет две другие возможности: присваивание адреса константной переменной указателю на константу и присваивание адреса константной переменной обычному указателю. Возможно ли то и другое? Первое — да, второе — нет:
const float g_earth =9.80;
const float * ре = &g_earth; // ПРАВИЛЬНО
const float g_moon = 1.63;
float * pm = &g_moon; // НЕПРАВИЛЬНО
В первом случае вы не сможете использовать ни g_earth, ни ре для изменения значения 9. 8. Второй случай в C++ не допускается по простой причине: если вы можете присвоить адрес g_moon указателю pm, то можете схитрить и применить pm для изменения g_moon. Это сводит на нет константный статус g_moon, поэтому C++ запрещает присваивание адреса константной переменной не константному указателю.
На заметку!
Вы можете присваивать адрес как константных, так и не константных данных указателю на константу, предполагая, что эти данные сами не являются указателем, но присвоить адрес не константных данных допускается только не константному указателю.
Предположим, что есть массив константных данных:
const int months[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
Запрет на присваивание адреса константного массива означает, что вы не можете передавать имя такого массива в качестве аргумента функции, используя не константный формальный аргумент:
int sum (int arr[], int n) ; // должен быть const int arr[]
int j = sum (months, 12); // не допускается
Этот вызов функции пытается присвоить константный указатель (months) не константному указателю (агг), и компилятор не разрешает такой вызов.
Используйте const, когда это возможно
Существуют две серьезных причины объявлять аргументы-указатели указателями на константные данные.
• Это защищает от программных ошибок, из-за которых могут непреднамеренно изменяться данные.
• Использование const позволяет функции обрабатывать как константные, так и не константные аргументы, в то время как функция, в прототипе которой const опущено, может принимать только не константные данные.
Вы должны объявлять формальные аргументы-указатели как указатели на const, где это у только возможно.
Касательно еще одного тонкого момента рассмотрим следующее объявление:
int age = 3 9;
const int * pt = &age;
const во втором объявлении только предотвращает изменение того значения, на которое указывает pt, в данном случае 39. Это не предотвращает изменение самого pt.
То есть вы вполне можете присвоить ему другой адрес:
int sage = 80;
pt = &sage; // может указывать на другое место
Но вы по-прежнему не сможете использовать pt для изменения того значения, на которое он указывает.
