См. разделы 17.4 и 18.5.4.
Перечислим операции над указателями на объекты (не void
). Операции сравнения <
, <=
, >
, >+
можно применять только к указателям одного и того же типа внутри одного и того же объекта или массива.
Подчеркнем, что операции арифметики указателей (например, ++p
и p+=7
) могут применяться только к указателям, ссылающимся на элементы массива, а эффект разыменования указателя, ссылающегося на область памяти за пределами массива, не определен (и, скорее всего, не сможет быть проверен компилятором или системой выполнения программ).
Только операции над указателем типа void*
являются копированием (присваиванием или инициализацией) и приведением (преобразованием типа).
Указатель на функцию (см. раздел 27.2.5) можно только копировать и вызывать. Рассмотрим пример.
typedef void (*Handle_type)(int);
void my_handler(int);
Handle_type handle = my_handler;
handle(10); // эквивалент my_handler(10)
A.8.2. Массивы
int a[10]; // 10 целых чисел
Если массив является глобальным, то его элементы могут быть инициализированы соответствующим значением, принятым для данного типа по умолчанию. Например, значение a[7]
равно 0
. Если массив является локальным (переменная объявлена в функции) или создан с помощью оператора new
, то элементы встроенных типов останутся неинициализированными, а элементы, имеющие пользовательский тип, будут инициализированы его конструкторами.
Имя массива неявно преобразуется в указатель на его первый элемент. Рассмотрим пример.
int* p = a; // указатель p ссылается на элемент a[0]
Массив или указатель на элемент массива может индексироваться с помощью оператора []
. Рассмотрим пример.
a[7] = 9;
int xx = p[6];
Элементы массива нумеруются начиная с нуля (разделы 18.5).
Диапазон индексов массива не проверяется. Кроме того, поскольку они часто передаются с помощью указателей, информация, необходимая для проверки диапазона, передается пользователям ненадежным способом. Рекомендуем использовать класс vector
. Размер массива — это сумма размеров его элементов. Рассмотрим пример.
int a[max]; // sizeof(a) == sizeof(int)*max
Можно определить и использовать массив массивов (двумерный массив), массив массивов массивов (многомерный массив) и т.д. Рассмотрим пример.
double da[100][200][300]; // 300 элементов типа, состоящего из
da[7][9][11] = 0;
Нетривиальное использование многомерных массивов — тонкое и уязвимое для ошибок дело (см. раздел 24.4). Если у вас есть выбор, следует предпочесть класс Matrix
(как в главе 24).
A.8.3. Ссылки
int a = 7;
int& r = a;
r = 8; // переменная a становится равной 8
Ссылки часто используются в качестве параметров функций, чтобы предотвратить копирование.
void f(const string& s);
// ...
f("эту строку слишком дорого копировать, \\
поэтому используется ссылка");
См. разделы 8.5.4–8.5.6.
A.9. Функции
char f(string, int);
Итак, f
— это функция, принимающая объекты типа string
и int
и возвращающая объект типа char
. Если функция должна быть просто объявлена, но не определена, то ее объявление завершается точкой с запятой. Если функция должна быть определена, то за объявлением аргументов следует тело функции.
char f(string s, int i) { return s[i]; }
Телом функции должен быть блок (см. раздел 8.2) или блок try
(см. раздел 5.6.3).
Функция, в объявлении которой указано, что она возвращает какое-то значение, должна его возвращать (используя оператор return
).
char f(string s, int i) { char c = s[i]; } // ошибка: ничего
// не возвращается
Функция main
представляет собой странное исключение из этого правила (см. раздел A.1.2). За исключением функции main
, если не хотите возвращать значение, то поставьте перед именем функции ключевое слово void
. Другими словами, используйте слово void
как тип возвращаемого значения.
void increment(int& x) { ++x; } // OK: возвращать значение