int i = 5;
int& ci = i;
const int& cci = i; // Здесь все будет нормально
const int n = 10;
int& cn = n; // Такое компилятор запретит
int& cn = const_cast<int&>(n);
const int& ccn = n;
В C++ указатели и массивы тесно связаны
int a[10];
int* p = &a[0]; // адрес первого элемента
*p = 5;
a[0] | a[1] | a[2] | a[3] | a[4] | a[5] | a[6] | a[7] | a[8] | a[9] |
---|---|---|---|---|---|---|---|---|---|
5 | |||||||||
*p |
В Pascal таких операций нет т.к он является более высокоуровневым языком.
Выполним следующую операцию p++;
. Теперь указатель p
указывает на следующий(второй) элемент массива.
a[0] | a[1] | a[2] | a[3] | a[4] | a[5] | a[6] | a[7] | a[8] | a[9] |
---|---|---|---|---|---|---|---|---|---|
5 | |||||||||
*p |
Операция p++
увеличивает адрес в зависимости от типа указателя т.е.
p++ ~ p += sizeof(int)
p += 1; // Переход на следующий элемент массива
p += n; // Увеличение на n элементов массива
p+1 // Адрес следующего элемента
p+n = p1; // Записать в `p1` адрес n-го элемента
p1 - p = n; // Количество между указателями
А вот складывать указатели нельзя!!!
*(p+0) и a[0] - являются ссылками на первый элемент массива
*(p+1) и a[1] - являются ссылками на второй элемент массива
*(p+2) и a[2] - являются ссылками на третий элемент массива
т.е a[n] == *(p+n)
- связь массивов и указателей
Имя массива a
может быть неявно преобразовано к указателю на свой первый
элемент. Т.е на самом деле а
это указатель на первый элемент массива.
Значит мы можем написать проще:
int* p = a;
a
является константным указателем на свой первый элемент т.е он как-бы
описан таким образом int* const a;
Отсюда становится понятно, почему нельзя писать a = a1
. Т.к имя массива константный указатель , то нельзя присваивать один массив другому.
Вообще говоря, более строгая связь массивов и указателей выглядит следующим
образом: a[n] == *(a+n)
Отсюда следует вывод(крамольная истина):
В языке C массивов нет - есть только указатели!!!
Следствие 1. Понятно, почему нет контроля выхода за границы массива.
Следствие 2. Понятно почему массивы индексируются с нуля. Это самое эффективное по этой формуле.
Следствие 3. a[n] == *(a+n) == *(n+a) == n[a]
Идиома - устойчивое выражение, которое воспринимается как единое целое.
Теперь допустим, нам необходимо сделать следующее:
int a[10];
int* p = a;
*p = 3;
p++;
Для краткости это хочется заменить на *(p++) = 3;
А что, если записать *p++
? Это можно воспринимать как *(p++)
или как *(p)++
. В C/C++ унарные операции ассоциируются справа налево, поэтому в данном случае ++
относится к указателю,
следовательно *p++ ~ *(p++)
.
Пример 1. Заполнить массив a
нулями
int a[10];
//int* p = a; // Однако это можно перенести в раздел инициализации for(;;)
for(int* p = a; p != a+10; *p++ = 0;);
void InitZero(int* a, int n)
{
for(int* p = a; p != a+n;) *p++ = 0;
}
// int* a ~ int a[]
Пример 2. Даны 3 массива int a[10], b[10], c[10]
. Необходимо заполнить
массив c[10]
суммой элементов массивов a[10]
и b[10]
.
int *pa = a, *pb = b, *pc = c;
//for(; pa != a + 10;)
while(pa != a + 10)
*pc++ = *pa++ + *pb++;
Пример 1.
char s[10] = "Hello";
s[0] | s[1] | s[2] | s[3] | s[4] | s[5] | s[6] | s[7] | s[8] | s[9] |
---|---|---|---|---|---|---|---|---|---|
H | e | l | l | o | \0 | \0 | \0 | \0 | \0 |
char* pc = s;
//while(*pc != '\0') // Но можно короче
while(*pc)
cout << *pc++ << ' ';
Пример 2. Копирование строк
char s[10] = "Hello";
char s1[10];
s1 = s; // Нельзя т.к. s1 объявлен как char* const s1
char* mysrtcpy(char* p, const char* q)
{
while(*q)
*p++ = *q++;
*p = 0;
return p-1;
// Или можно сделать короче, но менее понятно
while(*p++ = *q++);
return p-1;
}