Объявление указателя
Объявляет переменную типа указателя или указателя на член.
Синтаксис
Объявлением указателя является любое простое объявление, декларатор которого имеет форму
* attr (необязательно) cv (необязательно) declarator | (1) | |
nested-name-specifier * attr (необязательно) cv (необязательно) declarator | (2) |
S* D; объявляет D указателем на тип, определяемый последовательностью спецификаторов объявления S.S C::* D; объявляет D указателем на нестатический член C типа, определяемого последовательностью спецификаторов объявления S.| nested-name-specifier | - | последовательность имён и операторов разрешения области видимости ::. Она должна обозначать тип класса, который не является анонимным объединением |
| attr | - | (начиная с C++11) список атрибутов |
| cv | - | квалификатор const/volatile, который применяется к объявляемому указателю (а не к типу объекта, на который указывает указатель; его квалификаторы входят в последовательность спецификаторов объявления) |
| declarator | - | любой декларатор, кроме деклараторов ссылок (указателей на ссылки не существует). Декларатор может быть ещё одним декларатором указателя (указатели на указатели разрешены) |
Указателей на ссылки и указателей на битовые поля не существует. Обычно упоминания «указателей» без уточнений не включают указатели на (нестатические) члены.
Указатели
Каждое значение указательного типа является одним из следующих:
- указателем на объект или функцию (в этом случае говорят, что указатель указывает на объект или функцию);
- указателем за конец объекта;
- значением нулевого указателя этого типа;
- недопустимым значением указателя.
Указатель на объект представляет адрес первого байта в памяти, занимаемой объектом. Указатель за конец объекта представляет адрес первого байта в памяти после конца области хранения, занимаемой объектом.
Обратите внимание, что два указателя, представляющие один и тот же адрес, могут иметь разные значения.
struct C
{
int x, y;
} c;
int* px = &c.x; // value of px is "pointer to c.x"
int* pxe= px + 1; // value of pxe is "pointer past the end of c.x"
int* py = &c.y; // value of py is "pointer to c.y"
assert(pxe == py); // == tests if two pointers represent the same address
// may or may not fire
*pxe = 1; // undefined behavior even if the assertion does not fire
Разыменование недопустимого значения указателя и передача недопустимого значения указателя функции освобождения памяти приводят к неопределённому поведению. Любое другое использование недопустимого значения указателя приводит к поведению, определяемому реализацией. Некоторые реализации могут определить, что копирование недопустимого значения указателя вызывает системную ошибку во время выполнения.
Указатели на объекты
Указатель на объект можно инициализировать возвращаемым значением оператора взятия адреса, применённого к любому выражению типа объекта, включая другой указательный тип:
int n;
int* np = &n; // pointer to int
int* const* npp = &np; // non-const pointer to const pointer to non-const int
int a[2];
int (*ap)[2] = &a; // pointer to array of int
struct S { int n; };
S s = {1};
int* sp = &s.n; // pointer to the int that is a member of s
Указатели могут выступать операндами встроенного оператора разыменования (унарного operator*), который возвращает выражение- lvalue, обозначающее объект, на который указывает указатель:
int n; int* p = &n; // pointer to n int& r = *p; // reference is bound to the lvalue expression that identifies n r = 7; // stores the int 7 in n std::cout << *p; // lvalue-to-rvalue implicit conversion reads the value from n
Указатели на объекты классов также могут выступать левыми операндами операторов доступа к членам operator-> и operator->*.
Благодаря неявному преобразованию массива в указатель указатель на первый элемент массива можно инициализировать выражением типа массива:
int a[2];
int* p1 = a; // pointer to the first element a[0] (an int) of the array a
int b[6][3][8];
int (*p2)[3][8] = b; // pointer to the first element b[0] of the array b,
// which is an array of 3 arrays of 8 ints
Благодаря неявному преобразованию производного класса в базовый для указателей указатель на базовый класс можно инициализировать адресом производного класса:
struct Base {};
struct Derived : Base {};
Derived d;
Base* p = &d;
Если Derived является полиморфным, такой указатель можно использовать для вызова виртуальных функций.
Для указателей на элементы массивов определены некоторые операторы сложения и вычитания, инкремента и декремента: такие указатели удовлетворяют требованиям LegacyRandomAccessIterator и позволяют алгоритмам библиотеки C++ работать с обычными массивами.
Операторы сравнения определены для указателей на объекты в некоторых ситуациях: два указателя, представляющие один и тот же адрес, сравниваются как равные; два значения нулевого указателя сравниваются как равные; указатели на элементы одного массива сравниваются так же, как индексы этих элементов в массиве; указатели на нестатические члены данных с одинаковым доступом к членам сравниваются в порядке объявления этих членов.
Многие реализации также предоставляют строгий линейный порядок для указателей произвольного происхождения, например, если они реализованы как адреса в непрерывном виртуальном адресном пространстве. Реализации, которые этого не делают (например, когда не все биты указателя являются частью адреса памяти и их нужно игнорировать при сравнении либо требуется дополнительное вычисление, или когда между указателями и целыми числами нет взаимно-однозначного соответствия), предоставляют для указателей специализацию std::less, гарантирующую это свойство. Благодаря этому указатели произвольного происхождения можно использовать в качестве ключей в стандартных ассоциативных контейнерах, таких как std::set или std::map.
Указатели на void
Указатель на объект любого типа можно неявно преобразовать в указатель на (возможно, cv-квалифицированный) void; значение указателя не изменяется. Обратное преобразование, требующее static_cast или явного преобразования, возвращает исходное значение указателя:
int n = 1; int* p1 = &n; void* pv = p1; int* p2 = static_cast<int*>(pv); std::cout << *p2 << '\n'; // prints 1
Если исходный указатель указывает на подобъект базового класса внутри объекта полиморфного типа, с помощью dynamic_cast можно получить void*, указывающий на полный объект наиболее производного типа.
Указатели на void имеют тот же размер, представление и выравнивание, что и указатели на char.
Указатели на void используются для передачи объектов неизвестного типа, что часто встречается в интерфейсах C: std::malloc возвращает void*, std::qsort ожидает предоставленный пользователем callback, принимающий два аргумента constvoid*. pthread_create ожидает предоставленный пользователем callback, принимающий и возвращающий void*. Во всех случаях вызывающий код отвечает за приведение указателя к правильному типу перед его использованием.
Указатели на функции
Указатель на функцию можно инициализировать адресом нестатической или статической функции-члена. Благодаря неявному преобразованию функции в указатель оператор взятия адреса необязателен:
void f(int); void (*p1)(int) = &f; void (*p2)(int) = f; // same as &f
В отличие от функций и ссылок на функции, указатели на функции являются объектами, поэтому их можно хранить в массивах, копировать, присваивать и т. д.
void (a[10])(int); // Error: array of functions void (&a[10])(int); // Error: array of references void (*a[10])(int); // OK: array of pointers to functions
Примечание: объявления с указателями на функции часто можно упростить с помощью псевдонимов типов:
using F = void(int); // named type alias to simplify declarations F a[10]; // Error: array of functions F& a[10]; // Error: array of references F* a[10]; // OK: array of pointers to functions
Указатель на функцию можно использовать в качестве левого операнда оператора вызова функции; это вызывает функцию, на которую указывает указатель:
int f(int n)
{
std::cout << n << '\n';
return n * n;
}
int main()
{
int (*p)(int) = f;
int x = p(7);
}
Разыменование указателя на функцию даёт lvalue, обозначающее функцию, на которую указывает указатель:
int f(); int (*p)() = f; // pointer p is pointing to f int (&r)() = *p; // the lvalue that identifies f is bound to a reference r(); // function f invoked through lvalue reference (*p)(); // function f invoked through the function lvalue p(); // function f invoked directly through the pointer
Указатель на функцию можно инициализировать из набора перегрузок, который может включать функции, специализации шаблонов функций и шаблоны функций, если только одна перегрузка соответствует типу указателя (подробнее см. взятие адреса перегруженной функции):
template<typename T>
T f(T n) { return n; }
double f(double n) { return n; }
int main()
{
int (*p)(int) = f; // instantiates and selects f<int>
}
Для указателей на функции определены операторы сравнения на равенство (они сравниваются как равные, если указывают на одну и ту же функцию).
Указатели на члены
Указатели на члены данных
Указатель на нестатический объект-член m, являющийся членом класса C, можно инициализировать только выражением &C::m. Выражения, такие как &(C::m) или &m внутри функции-члена класса C, не образуют указатели на члены.
Такой указатель можно использовать в качестве правого операнда операторов доступа через указатель на член operator.* и operator->*:
struct C { int m; };
int main()
{
int C::* p = &C::m; // pointer to data member m of class C
C c = {7};
std::cout << c.*p << '\n'; // prints 7
C* cp = &c;
cp->m = 10;
std::cout << cp->*p << '\n'; // prints 10
}
Указатель на член данных доступного неоднозначно не виртуального базового класса можно неявно преобразовать в указатель на тот же член данных производного класса:
struct Base { int m; };
struct Derived : Base {};
int main()
{
int Base::* bp = &Base::m;
int Derived::* dp = bp;
Derived d;
d.m = 1;
std::cout << d.*dp << ' ' << d.*bp << '\n'; // prints 1 1
}
Обратное преобразование — из указателя на член данных производного класса в указатель на член данных неоднозначно не виртуального базового класса — допускается с помощью static_cast и явного преобразования, даже если базовый класс не содержит этого члена (но наиболее производный класс содержит его, когда указатель используется для доступа):
struct Base {};
struct Derived : Base { int m; };
int main()
{
int Derived::* dp = &Derived::m;
int Base::* bp = static_cast<int Base::*>(dp);
Derived d;
d.m = 7;
std::cout << d.*bp << '\n'; // okay: prints 7
Base b;
std::cout << b.*bp << '\n'; // undefined behavior
}
Тип, на который указывает указатель на член, сам может быть указателем на член: указатели на члены могут быть многоуровневыми, и на каждом уровне могут иметь разные cv-квалификаторы. Также допускаются смешанные многоуровневые комбинации указателей и указателей на члены:
struct A
{
int m;
// const pointer to non-const member
int A::* const p;
};
int main()
{
// non-const pointer to data member which is a const pointer to non-const member
int A::* const A::* p1 = &A::p;
const A a = {1, &A::m};
std::cout << a.*(a.*p1) << '\n'; // prints 1
// regular non-const pointer to a const pointer-to-member
int A::* const* p2 = &a.p;
std::cout << a.**p2 << '\n'; // prints 1
}
Указатели на функции-члены
Указатель на нестатическую функцию-член f, являющуюся членом класса C, можно инициализировать только выражением &C::f. Выражения, такие как &(C::f) или &f внутри функции-члена класса C, не образуют указатели на функции-члены.
Такой указатель можно использовать в качестве правого операнда операторов доступа через указатель на член operator.* и operator->*. Полученное выражение можно использовать только в качестве левого операнда оператора вызова функции:
struct C
{
void f(int n) { std::cout << n << '\n'; }
};
int main()
{
void (C::* p)(int) = &C::f; // pointer to member function f of class C
C c;
(c.*p)(1); // prints 1
C* cp = &c;
(cp->*p)(2); // prints 2
}
Указатель на функцию-член базового класса можно неявно преобразовать в указатель на ту же функцию-член производного класса:
struct Base
{
void f(int n) { std::cout << n << '\n'; }
};
struct Derived : Base {};
int main()
{
void (Base::* bp)(int) = &Base::f;
void (Derived::* dp)(int) = bp;
Derived d;
(d.*dp)(1);
(d.*bp)(2);
}
Обратное преобразование — из указателя на функцию-член производного класса в указатель на функцию-член неоднозначно не виртуального базового класса — допускается с помощью static_cast и явного преобразования, даже если базовый класс не содержит этой функции-члена (но наиболее производный класс содержит её, когда указатель используется для доступа):
struct Base {};
struct Derived : Base
{
void f(int n) { std::cout << n << '\n'; }
};
int main()
{
void (Derived::* dp)(int) = &Derived::f;
void (Base::* bp)(int) = static_cast<void (Base::*)(int)>(dp);
Derived d;
(d.*bp)(1); // okay: prints 1
Base b;
(b.*bp)(2); // undefined behavior
}
Указатели на функции-члены можно использовать в качестве callback-функций или функциональных объектов, часто после применения std::mem_fn или std::bind:
#include <algorithm>
#include <cstddef>
#include <functional>
#include <iostream>
#include <string>
int main()
{
std::vector<std::string> v = {"a", "ab", "abc"};
std::vector<std::size_t> l;
transform(v.begin(), v.end(), std::back_inserter(l),
std::mem_fn(&std::string::size));
for (std::size_t n : l)
std::cout << n << ' ';
std::cout << '\n';
}
Вывод:
1 2 3
Нулевые указатели
Указатели любого типа имеют специальное значение, известное как значение нулевого указателя этого типа. Указатель с нулевым значением не указывает на объект или функцию (поведение при разыменовании нулевого указателя не определено) и сравнивается как равный всем указателям того же типа, значение которых также является нулевым.
Константу нулевого указателя можно использовать для инициализации указателя нулевым значением или присваивания нулевого значения существующему указателю. Это одно из следующих значений:
- Целочисленный литерал со значением ноль.
| (начиная с C++11) |
Также можно использовать макрос NULL, раскрывающийся в константу нулевого указателя, определяемую реализацией.
Нулевая инициализация и инициализация значением также инициализируют указатели их нулевыми значениями.
Нулевые указатели можно использовать для обозначения отсутствия объекта (например, std::function::target()) или других ошибок (например, dynamic_cast). В общем случае функция, получающая указатель в качестве аргумента, почти всегда должна проверять, не является ли его значение нулевым, и обрабатывать этот случай отдельно (например, выражение delete ничего не делает, если ему передан нулевой указатель).
Недопустимые указатели
Значение указателя p допустимо в контексте вычисления e, если выполняется одно из следующих условий:
-
pявляется значением нулевого указателя. -
pявляется указателем на функцию. -
pявляется указателем на объект или за его конецo, аeнаходится в пределах времени существования области хранения дляo.
Если значение указателя p используется при вычислении e и p недопустимо в контексте e, то:
- Если
eявляется разыменованием или вызовом функции освобождения памяти, поведение не определено. - В противном случае поведение определяется реализацией.
int* f()
{
int obj;
int* local_ptr = new (&obj) int;
*local_ptr = 1; // OK, the evaluation “*local_ptr” is
// in the storage duration of “obj”
return local_ptr;
}
int* ptr = f(); // the storage duration of “obj” is expired,
// therefore “ptr” is an invalid pointer in the following contexts
int* copy = ptr; // implementation-defined behavior
*ptr = 2; // undefined behavior: indirection of an invalid pointer
delete ptr; // undefined behavior: deallocating storage from an invalid pointer
Константность
- Если cv стоит перед
*в объявлении указателя, он является частью последовательности спецификаторов объявления и применяется к объекту, на который указывает указатель. - Если cv стоит после
*в объявлении указателя, он является частью декларатора и применяется к объявляемому указателю.
| Синтаксис | значение |
|---|---|
| constT* | указатель на константный объект |
| Tconst* | указатель на константный объект |
| T*const | константный указатель на объект |
| constT*const | константный указатель на константный объект |
| Tconst*const | константный указатель на константный объект |
// pc is a non-const pointer to const int
// cpc is a const pointer to const int
// ppc is a non-const pointer to non-const pointer to const int
const int ci = 10, *pc = &ci, *const cpc = pc, **ppc;
// p is a non-const pointer to non-const int
// cp is a const pointer to non-const int
int i, *p, *const cp = &i;
i = ci; // okay: value of const int copied into non-const int
*cp = ci; // okay: non-const int (pointed-to by const pointer) can be changed
pc++; // okay: non-const pointer (to const int) can be changed
pc = cpc; // okay: non-const pointer (to const int) can be changed
pc = p; // okay: non-const pointer (to const int) can be changed
ppc = &pc; // okay: address of pointer to const int is pointer to pointer to const int
ci = 1; // error: const int cannot be changed
ci++; // error: const int cannot be changed
*pc = 2; // error: pointed-to const int cannot be changed
cp = &ci; // error: const pointer (to non-const int) cannot be changed
cpc++; // error: const pointer (to const int) cannot be changed
p = pc; // error: pointer to non-const int cannot point to const int
ppc = &p; // error: pointer to pointer to const int cannot point to
// pointer to non-const int
В общем случае неявное преобразование одного многоуровневого указателя в другой следует правилам, описанным в разделе преобразования квалификаторов.
Составной тип указателя
Если один из операндов оператора сравнения или второй либо третий операнд условного оператора является указателем или указателем на член, определяется составной тип указателя — общий тип этих операндов.
Пусть два операнда p1 и p2 имеют типы T1 и T2 соответственно. p1 и p2 могут иметь составной тип указателя только при выполнении одного из следующих условий:
| (до C++14) | ||
| (начиная с C++14) |
Составной тип указателя C для p1 и p2 определяется следующим образом:
| (до C++11) |
| (начиная с C++11) |
- Иначе, если выполняются все следующие условия:
-
T1илиT2— это «указатель на cv1 void». - Другой тип — это «указатель на cv2
T», гдеTявляется типом объекта или void.
-
-
C— это «указатель на cv12 void», где cv12 является объединением cv1 и cv2.
| (начиная с C++17) |
- Иначе, если выполняются все следующие условия:
-
T1— это «указатель наC1». -
T2— это «указатель наC2». - Один из
C1иC2связан ссылкой с другим.
-
-
C— это- тип с объединёнными квалификаторами
T1иT2, еслиC1связан ссылкой сC2; или - тип с объединёнными квалификаторами
T2иT1, еслиC2связан ссылкой сC1.
- тип с объединёнными квалификаторами
| (начиная с C++17) |
- Иначе, если выполняются все следующие условия:
-
T1— это «указатель на членC1не-функционального типаM1». -
T2— это «указатель на членC2не-функционального типаM2». -
M1иM2совпадают, за исключением cv-квалификаторов верхнего уровня. - Один из
C1иC2связан ссылкой с другим.
-
-
C— это- тип с объединёнными квалификаторами
T2иT1, еслиC1связан ссылкой сC2; или - тип с объединёнными квалификаторами
T1иT2, еслиC2связан ссылкой сC1.
- тип с объединёнными квалификаторами
- Иначе, если
T1иT2являются подобными типами,Cявляется типом с объединёнными квалификаторамиT1иT2. - Иначе
p1иp2не имеют составного типа указателя; программа, требующая определения такого типаC, некорректна.
using p = void*; using q = const int*; // The determination of the composite pointer type of “p” and “q” // falls into the [“pointer to cv1 void” and “pointer to cv2 T”] case: // cv1 = empty, cv2 = const, cv12 = const // substitute “cv12 = const” into “pointer to cv12 void”: // the composite pointer type is “const void*” using pi = int**; using pci = const int**; // The determination of the composite pointer type of “pi” and “pci” // falls into the [pointers to similar types “C1” and “C2”] case: // C1 = int*, C2 = const int* // they are reference-related types (in both direction) because they are similar // the composite pointer type is the qualification-combined type // of “p1” and “pc1” (or that of “pci” and “pi”): “const int**”
Отчёты об ошибках
Следующие отчёты об ошибках, изменяющие поведение, были применены ретроспективно к ранее опубликованным стандартам C++.
| DR | Применён к | Опубликованное поведение | Корректное поведение |
|---|---|---|---|
| CWG 73 | C++98 | указатель на объект никогда не сравнивается как равный указателю за конец массива | для ненулевых указателей, не являющихся указателями на функции, сравниваются представляемые ими адреса |
| CWG 903 | C++98 | любое целочисленное константное выражение, вычисляющееся в 0, являлось константой нулевого указателя | ограничено целочисленными литералами со значением 0 |
| CWG 1438 | C++98 | поведение при любом использовании недопустимого значения указателя не было определено | поведение, отличное от разыменования и передачи функциям освобождения памяти, определяется реализацией |
|
CWG 1512 (N3624) | C++98 | правило составного типа указателя было неполным и потому не допускало сравнение int** и const int** | правило дополнено |
| CWG 2206 | C++98 | указатель на void и указатель на функцию имели составной тип указателя | у них нет такого типа |
| CWG 2381 | C++17 | преобразования указателей на функции не допускались при определении составного типа указателя | допускаются |
| CWG 2822 | C++98 | завершение времени существования области хранения могло сделать значения указателей недопустимыми | допустимость указателя зависит от контекста вычисления |
| CWG 2933 | C++98 | указатели на функции всегда были недопустимыми | они всегда допустимы |
См. также
| Документация по C о объявлении указателя |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/pointer