Можно применить описатель const к формальному параметру функции. Это будет означать, что запрещается попытка изменить этот параметр внутри тела функции. Например, описатель const в прототипе
printf (const char* format,...);
означает, что не разрешено изменять значение строки format внутри тела printf. Заодно отметьте, что многоточие при объявлении прототипа функции означает, что количество параметров не определено и может быть произвольным. Прототип известной функции копирования символьных строк
char* strcpy (char* dest, const char* source);
ясно указывает, какую строку и куда она собирается копировать. Обратите внимание, как компактно выглядит оператор копирования константной строки символов buff в динамически выделенную область памяти и отныне адресуемую переменной char* FileName.
char buff[256], *FileName;
gets(buff);
FileName = strcpy(new char[strlen(buff)+1],buff);
Функция strlen возвращает количество значащих символов в строке buff. Эта и другие функции работы со строками символов определены в файле string.h.
Большая свобода в выборе средств манипуляции элементами массивов, предоставляемая языками С и C++, иногда вносит путаницу в понимание основных положений, которые позволяют эффективно работать с массивами более высоких размерностей. Действительно, надо затратить некоторые усилия, чтобы осознать эквивалентность следующих выражений: arr[2] и 2[arr] при условии, что был объявлен массив, например, double arr[6];. Или эквивалентность a[i][j] и *(*(a+i)+j) при условии, что был объявлен двухмерный массив, например, int a[3][4];. Следует отметить, что можно никогда не использовать выражения типа 2[arr] и, тем не менее, создавать работоспособные программы, но нужно уметь производить анализ выражений такого типа, чтобы понять как компилятор интерпретирует переменные с индексами.
Как было отмечено выше, если объявлен одномерный массив, например, float arr[6];, то имя массива arr (без последующего индекса) может быть использовано как константный указатель на его первый элемент. Особенностью языков С и C++ является тот факт, что выражение arr[5] трактуется компилятором, как *(arr+5). Действительно, так как arr — адрес начала массива, то arr+5 означает (с учетом правил адресной арифметики) адрес шестого, последнего элемента массива. Следовательно, *(arr+5) — это содержимое по адресу arr+5, то есть равенство arr[5]==*(arr+5) истинно. Теперь проанализируем, как компилятор трактует выражение 5[arr]. Сначала он преобразовывает 5[arr] в *(5+arr), после чего очевидно:
5[arr] Ы *(5+arr) Ы *(arr+5) Ы arr[5]
Таким образом, имя массива и его индекс можно менять местами, а результат при этом остается тем же. Неслыханная вещь для других языков программирования. Рассмотрим теперь двухмерный массив int a[2][3];. Встретив описание такого типа, компилятор отводит в памяти место для линейного размещения массива в виде последовательности ячеек.
1-й элемент |
Последний эл-т |
1-й элемент |
Последний элемент |
||
1-й строки |
1-й строки |
2-й строки |
последней строки |
||
Ї |
Ї |
Ї |
Ї |
||
a[0][0] |
a[0][1] |
a[0][2] |
a[1][0] |
a[1][1] |
a[1][2] |
Двухмерный массив рассматривается как массив массивов. Элементами главного массива из двух элементов являются одномерные массивы, каждый из трех элементов типа int. В языке имеют смысл такие объекты:
¨ a[0][0] — первый элемент массива типа int;
¨ a[0] — адрес первого элемента массива типа int*;
¨ a — адрес первой строки массива типа int**.
Выражение a+1 означает адрес второй строки массива, то есть адрес, сдвинутый на один элемент массива массивов, а таким элементом является строка двухмерного массива. Выражение a+1 подразумевает сдвиг от a на размер одной строки, (а не на размер числа типа int). Адресная арифметика всегда осуществляется в единицах базового типа данных. Теперь такой единицей является строка двухмерного массива или массив целых из трех элементов. Имеют место следующие равенства:
a==&a[0], a[0]==&a[0][0]
Если вывести содержимое этих адресов: a, &a[0], a[0], &a[0][0], (в формате %p), то мы, может быть с удивлением, обнаружим, что все они представляют собой одно и то же число, являющееся адресом первого элемента. Но, несмотря на численное равенство, объекты из первого равенства и объекты из второго равенства, например a[0] и a, принадлежат к разным типам и их не следует смешивать в одном выражении, так как, несмотря на численное равенство, это объекты разной природы. Поучительно в режиме пошагового выполнения просмотреть следующую программу:
void main()
{
int a[2][3] = { {1,2,3}, {4,5,6}};
printf("\n **a = %d\t\t a[0][0] = %d"
"\n a = %p\t a[0] = %p"
"\n &a[0]= %p\t &a[0][0]= %p"
"\n a+1 = %p\t a[0]+1 = %p\n",
**a,a[0][0],a,a[0], &a[0],&a[0][0],a+1,a[0]+1);
printf("\na == &a[0] = %d\na[0]== &a[0][0] = %d\n\n",
a==&a[0],a[0]==&a[0][0]);
}
Массив a[2][3] можно инициализировать в точке его определения так, как показано в примере. Первым параметром функции printf всегда является строка символов. Несмотря на переносы в тексте, все части одной строки склеятся при компиляции. Результат работы первой printf занимает четыре строки на экране. Первая строка вывода будет такой: **a=1 a[0][0]=1. Так как a является адресом адреса, то понадобилась двойная разадресация (**a) для того, чтобы добраться до первого элемента массива. Таким образом, выражение **a==a[0][0] является истинным. Вторая и третья строки дают одинаковые численные результаты. Все числа являются адресом первого элемента массива. Они могут быть, например: 0012FF68. Четвертая строка вывода дает два разных числа: первое — адрес первого элемента второй строки, второе — адрес второго элемента первой строки, например:
Уважаемый посетитель!
Чтобы распечатать файл, скачайте его (в формате Word).
Ссылка на скачивание - внизу страницы.