C++. Бархатный путь. Часть 1 - Массив и константный указатель

ОГЛАВЛЕНИЕ

Массив и константный указатель

Несмотря на некоторое сходство с константным указателем, массив является особым типом данных. В этом разделе мы рассмотрим основные отличия массива и константного указателя.

Прежде всего, рассмотрим варианты инициализации указателя:

char * const pcchVal_1 = chArray_2;
char * const pcchVal_2 = new char[5];
char * const pcchVal_3 = (char *) malloc(5*sizeof(char));

Для инициализации последнего константного указателя был использован вызов функции malloc().

Каждый из этих операторов демонстрирует один из трёх возможных способов инициализации константного указателя: непосредственное присвоение значения, использование операции new, вызов функции. Операция new и функции распределения памяти, выделяют соответствующие участки памяти и возвращают начальный адрес выделенной области памяти. Ни один из этих способов не подходит для инициализации массива.

В свою очередь, при определении константных указателей не используются уже известные инициализаторы массивов с явным указанием размерности и списком инициализаторов.

Определим массив и константный указатель на область памяти:

int intArray[5]= {11,22,33,44,55};
int * const pciVal = new int[5];

К константным указателям и массивам применимы одни и те же методы навигации, связанные с использованием операции индексации:

intArray[-25] = 10;
*(intArray + 25) = 10;
pciVal[2] = 100;
*(pciVal + 5) = 100;

А теперь применим операцию sizeof по отношению к проинициализированным указателям:

cout << "pciVal:"<< sizeof(pciVal)<<
" intArray:"<< sizeof(intArray);

Для Borland C++ 4.5, операция sizeof покажет размер области памяти, занимаемой указателем (4 байта) и размер массива (размер элемента * размерность массива)==(10 байт). Операция sizeof различает указатели и имена массивов.

Кроме того, следующий тест также даёт различные результаты.

if (intArray == &intArray)
cout << "Yes, массив." << endl;
else
cout << "No, массив." << endl;
if (pciVal == &pciVal)
cout << "Yes, указатель. " << endl;
else
cout << "No, указатель." << endl;

Результат выполнения:

Yes, массив.
No, указатель.

Значение указателя, представляющего массив, совпадает с адресом первого элемента массива.

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

Интересно, что сравнение значения указателя с результатом выполнения операции взятия адреса не является абсолютно корректным с точки зрения соответствия типов. Операция взятия адреса возвращает лишь определённое значение адреса. И при этом после выполнения этой операции как бы ничего не известно о типе операнда, чей адрес определяли с помощью этой самой операции взятия адреса. Транслятор отслеживает это нарушение принципа соответствия типов и выдаёт предупреждение "Nonportable pointer comparison".

Поскольку это всего лишь предупреждение, выполнение процесса трансляции не прерывается и загрузочный модуль, построенный на основе этого программного кода, корректно выполняется. "Успокоить" транслятор можно с помощью операции явного преобразования типа, которая отключает контроль над типами:

if (intArray == (int *)&intArray)
cout << "Yes";
else
cout << "No";