Namespaces
Variants

Pointer declaration

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

Объявляет переменную типа указатель или указатель-на-член.

Содержание

Синтаксис

Объявление указателя — это любое простое объявление, чей декларатор имеет вид

* attr  (необязательно) cv  (необязательно) declarator (1)
nested-name-specifier * attr  (необязательно) cv  (необязательно) declarator (2)
1) Pointer declarator : объявление S * D ; объявляет D как указатель на тип, определяемый последовательностью спецификаторов объявления S .
2) Декларатор указателя на член : объявление S C :: * D ; объявляет D как указатель на нестатический член класса C с типом, определяемым последовательностью спецификаторов объявления S .
nested-name-specifier - a последовательность имен и операторов разрешения области видимости ::
attr - (since C++11) список атрибутов
cv - квалификация const/volatile, которая применяется к объявляемому указателю (не к указываемому типу, чьи квалификаторы являются частью последовательности спецификаторов объявления)
declarator - любой декларатор

Не существует указателей на ссылки и не существует указателей на битовые поля . Обычно упоминания "указателей" без уточнения не включают указатели на (нестатические) члены.

Указатели

Каждое значение указательного типа является одним из следующих:

Указатель, который указывает на объект, представляет адрес первого байта в памяти, занимаемого объектом. Указатель за пределами объекта представляет адрес первого байта в памяти после конца области памяти, занимаемой объектом.

Обратите внимание, что два указателя, представляющие один и тот же адрес, тем не менее могут иметь разные значения.

struct C
{
    int x, y;
} c;
int* px = &c.x;   // значение px - "указатель на c.x"
int* pxe= px + 1; // значение pxe - "указатель за концом c.x"
int* py = &c.y;   // значение py - "указатель на c.y"
assert(pxe == py); // == проверяет, представляют ли два указателя один адрес
                   // может сработать или не сработать
*pxe = 1; // неопределенное поведение, даже если утверждение не срабатывает

Разыменование через недействительное значение указателя и передача недействительного значения указателя функции освобождения памяти приводит к неопределенному поведению. Любое другое использование недействительного значения указателя имеет поведение, определяемое реализацией. Некоторые реализации могут определять, что копирование недействительного значения указателя вызывает системную ошибку времени выполнения.

Указатели на объекты

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

int n;
int* np = &n;          // указатель на int
int* const* npp = &np; // неконстантный указатель на константный указатель на неконстантный int
int a[2];
int (*ap)[2] = &a;     // указатель на массив int
struct S { int n; };
S s = {1};
int* sp = &s.n;        // указатель на int, который является членом s

Указатели могут выступать в качестве операндов встроенного оператора разыменования (унарный operator * ), который возвращает lvalue expression , идентифицирующий указываемый объект:

int n;
int* p = &n;     // указатель на n
int& r = *p;     // ссылка привязывается к lvalue-выражению, идентифицирующему n
r = 7;           // сохраняет целое число 7 в n
std::cout << *p; // неявное преобразование lvalue-to-rvalue считывает значение из n

Указатели на объекты классов также могут выступать в качестве левых операндов операторов доступа к членам operator-> и operator->* .

Из-за array-to-pointer неявного преобразования, указатель на первый элемент массива может быть инициализирован выражением типа массива:

int a[2];
int* p1 = a; // указатель на первый элемент a[0] (типа int) массива a
int b[6][3][8];
int (*p2)[3][8] = b; // указатель на первый элемент b[0] массива b,
                     // который является массивом из 3 массивов по 8 элементов типа int

Из-за неявного преобразования указателя derived-to-base указатель на базовый класс может быть инициализирован адресом производного класса:

struct Base {};
struct Derived : Base {};
Derived d;
Base* p = &d;

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

Определенные операторы сложения, вычитания , инкремента и декремента определены для указателей на элементы массивов: такие указатели удовлетворяют требованиям LegacyRandomAccessIterator и позволяют алгоритмам стандартной библиотеки C++ algorithms работать с сырыми массивами.

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

Многие реализации также предоставляют строгий полный порядок для указателей произвольного происхождения, например, если они реализованы как адреса в непрерывном виртуальном адресном пространстве. Те реализации, которые этого не делают (например, когда не все биты указателя являются частью адреса памяти и должны игнорироваться при сравнении, или требуется дополнительное вычисление, или иным образом указатель и целое число не находятся в отношении 1 к 1), предоставляют специализацию 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'; // выводит 1

Если исходный указатель указывает на подобъект базового класса внутри объекта полиморфного типа, dynamic_cast может быть использован для получения void * , указывающего на полный объект наиболее производного типа.

Указатели на void имеют тот же размер, представление и выравнивание, что и указатели на char .

Указатели на void используются для передачи объектов неизвестного типа, что часто встречается в C-интерфейсах: std::malloc возвращает void * , std::qsort ожидает пользовательскую функцию обратного вызова, принимающую два аргумента типа const void * . pthread_create ожидает пользовательскую функцию обратного вызова, которая принимает и возвращает void * . Во всех случаях ответственность за приведение указателя к правильному типу перед использованием лежит на вызывающей стороне.

Указатели на функции

Указатель на функцию может быть инициализирован адресом функции-не-члена или статической функции-члена. Из-за неявного преобразования функции в указатель оператор взятия адреса является опциональным:

void f(int);
void (*p1)(int) = &f;
void (*p2)(int) = f; // то же самое, что &f

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

void (a[10])(int);  // Ошибка: массив функций
void (&a[10])(int); // Ошибка: массив ссылок
void (*a[10])(int); // OK: массив указателей на функции

Примечание: объявления с указателями на функции часто можно упростить с помощью псевдонимов типов:

using F = void(int); // именованный псевдоним типа для упрощения объявлений
F a[10];  // Ошибка: массив функций
F& a[10]; // Ошибка: массив ссылок
F* a[10]; // OK: массив указателей на функции

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

int f(int n)
{
    std::cout << n << '\n';
    return n * n;
}
int main()
{
    int (*p)(int) = f;
    int x = p(7);
}
**Примечание:** Весь код C++ внутри тегов `
` и `` оставлен без изменений, как и требовалось в инструкциях. HTML-разметка и атрибуты также сохранены в оригинальном виде.

Разыменование указателя на функцию даёт lvalue, идентифицирующее указываемую функцию:

int f();
int (*p)() = f;  // указатель p указывает на f
int (&r)() = *p; // lvalue, идентифицирующее f, связывается со ссылкой
r();             // функция f вызывается через lvalue-ссылку
(*p)();          // функция f вызывается через lvalue функции
p();             // функция f вызывается напрямую через указатель

Указатель на функцию может быть инициализирован из набора перегруженных функций, который может включать функции, специализации шаблонов функций и шаблоны функций, если только одна перегрузка соответствует типу указателя (см. взятие адреса перегруженной функции для более подробной информации):

template<typename T>
T f(T n) { return n; }
double f(double n) { return n; }
int main()
{
    int (*p)(int) = f; // создает экземпляр и выбирает f<int>
}

Операторы сравнения на равенство определены для указателей на функции (они сравниваются как равные, если указывают на одну и ту же функцию).

Указатели на члены

Указатели на члены данных

Указатель на нестатический член-объект m , который является членом класса C , может быть инициализирован выражением & C :: m точно. Выражения такие как & ( C :: m ) или & m внутри функции-члена класса C не формируют указатели на члены.

Такой указатель может использоваться в качестве правого операнда для операторов доступа к члену по указателю operator. * и operator - > * :

struct C { int m; };
int main()
{
    int C::* p = &C::m;          // указатель на член данных m класса C
    C c = {7};
    std::cout << c.*p << '\n';   // выводит 7
    C* cp = &c;
    cp->m = 10;
    std::cout << cp->*p << '\n'; // выводит 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'; // выводит 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'; // корректно: выводит 7
    Base b;
    std::cout << b.*bp << '\n'; // неопределенное поведение
}

Тип, на который указывает указатель на член, сам может быть указателем на член: указатели на члены могут быть многоуровневыми и могут иметь разные cv-квалификаторы на каждом уровне. Также допускаются смешанные многоуровневые комбинации указателей и указателей на члены:

struct A
{
    int m;
    // константный указатель на неконстантный член
    int A::* const p;
};
int main()
{
    // неконстантный указатель на член данных, который является константным указателем на неконстантный член
    int A::* const A::* p1 = &A::p;
    const A a = {1, &A::m};
    std::cout << a.*(a.*p1) << '\n'; // выводит 1
    // обычный неконстантный указатель на константный указатель-на-член
    int A::* const* p2 = &a.p;
    std::cout << a.**p2 << '\n'; // выводит 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; // указатель на функцию-член f класса C
    C c;
    (c.*p)(1);                  // выводит 1
    C* cp = &c;
    (cp->*p)(2);                // выводит 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);
}
**Примечание:** Весь код C++ внутри тегов `
` и `` оставлен без изменений, как и требовалось. HTML-разметка и атрибуты также сохранены в оригинальном виде.

Преобразование в обратном направлении, из указателя на член-функцию производного класса в указатель на член-функцию однозначного невиртуального базового класса, разрешено с помощью 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); // корректно: выводит 1
    Base b;
    (b.* bp)(2); // неопределенное поведение
}

Указатели на функции-члены могут использоваться как колбэки или как функциональные объекты, часто после применения 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

Нулевые указатели

Указатели любого типа имеют специальное значение, известное как нулевое значение указателя этого типа. Указатель со значением null не указывает на объект или функцию (поведение при разыменовании нулевого указателя не определено) и сравнивается равным со всеми указателями того же типа, значение которых также является нулевым .

Нулевая константа указателя может использоваться для инициализации указателя значением null или присвоения нулевого значения существующему указателю, это одно из следующих значений:

  • Целочисленный литерал со значением ноль.
  • Правостое значение типа std::nullptr_t (обычно nullptr ).
(since C++11)

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

Zero-initialization и value-initialization также инициализируют указатели их нулевыми значениями.

Нулевые указатели могут использоваться для обозначения отсутствия объекта (например, std::function::target() ), или как индикаторы других ошибочных состояний (например, dynamic_cast ). В общем случае, функция, получающая аргумент-указатель, почти всегда должна проверять, является ли значение нулевым, и обрабатывать этот случай отдельно (например, delete expression не выполняет никаких действий при передаче нулевого указателя).

Некорректные указатели

Значение указателя p является допустимым в контексте вычисления e если удовлетворяется одно из следующих условий:

  • p является нулевым указателем.
  • p является указателем на функцию.
  • p является указателем на объект или за его пределами o , и e находится в периоде времени хранения для o .

Если значение указателя p используется в вычислении e , и p не является допустимым в контексте e , тогда:

int* f()
{
    int obj;
    int* local_ptr = new (&obj) int;
    *local_ptr = 1; // OK, вычисление "*local_ptr" находится
                    // в периоде хранения "obj"
    return local_ptr;
}
int* ptr = f();  // период хранения "obj" завершился,
                 // поэтому "ptr" является недействительным указателем в следующих контекстах
int* copy = ptr; // поведение, определяемое реализацией
*ptr = 2;        // неопределенное поведение: разыменование недействительного указателя
delete ptr;      // неопределенное поведение: освобождение памяти из недействительного указателя

Константность

  • Если cv указывается перед * в объявлении указателя, это является частью последовательности спецификаторов объявления и применяется к указываемому объекту.
  • Если cv указывается после * в объявлении указателя, это является частью декларатора и применяется к объявляемому указателю.
Синтаксис Значение
const T * указатель на константный объект
T const * указатель на константный объект
T * const константный указатель на объект
const T * const константный указатель на константный объект
T const * const константный указатель на константный объект
// pc - неконстантный указатель на константный int
// cpc - константный указатель на константный int
// ppc - неконстантный указатель на неконстантный указатель на константный int
const int ci = 10, *pc = &ci, *const cpc = pc, **ppc;
// p - неконстантный указатель на неконстантный int
// cp - константный указатель на неконстантный int
int i, *p, *const cp = &i;
i = ci;    // корректно: значение константного int копируется в неконстантный int
*cp = ci;  // корректно: неконстантный int (на который указывает константный указатель) может быть изменён
pc++;      // корректно: неконстантный указатель (на константный int) может быть изменён
pc = cpc;  // корректно: неконстантный указатель (на константный int) может быть изменён
pc = p;    // корректно: неконстантный указатель (на константный int) может быть изменён
ppc = &pc; // корректно: адрес указателя на константный int является указателем на указатель на константный int
ci = 1;    // ошибка: константный int не может быть изменён
ci++;      // ошибка: константный int не может быть изменён
*pc = 2;   // ошибка: указываемый константный int не может быть изменён
cp = &ci;  // ошибка: константный указатель (на неконстантный int) не может быть изменён
cpc++;     // ошибка: константный указатель (на константный int) не может быть изменён
p = pc;    // ошибка: указатель на неконстантный int не может указывать на константный int
ppc = &p;  // ошибка: указатель на указатель на константный int не может указывать на
           // указатель на неконстантный int

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

Составной тип указателя

Когда операнд оператора сравнения или любой из второго и третьего операндов условного оператора является указателем или указателем-на-член, составной тип указателя определяется как общий тип этих операндов.

Даны два операнда p1 и p2 с типами T1 и T2 соответственно, p1 и p2 могут иметь составной тип указателя только при выполнении любого из следующих условий:

  • p1 и p2 являются указателями.
  • Один из p1 и p2 является указателем, а другой операнд - константой нулевого указателя.
  • p1 и p2 являются константами нулевого указателя, и хотя бы один из T1 и T2 является нецелочисленным типом.
(since C++11)
(until C++14)
  • Хотя бы один из T1 и T2 является типом указателя, типом указателя на член или std::nullptr_t .
(since C++14)

Составной тип указателя C для p1 и p2 определяется следующим образом:

(до C++11)
  • Если p1 и p2 обе являются константами нулевого указателя , C является std::nullptr_t .
  • Иначе, если p1 является константой нулевого указателя, C является T2 .
  • Иначе, если p2 является константой нулевого указателя, C является T1 .
(начиная с C++11)
  • В противном случае, если выполняются все следующие условия:
  • T1 или T2 является "указателем на cv1 void ".
  • Другой тип является "указателем на cv2 T ", где T является объектным типом или void .
C является "указателем на cv12 void ", где cv12 является объединением cv1 и cv2 .
  • В противном случае, если выполняются все следующие условия:
  • T1 или T2 является "указателем на тип функции F1 ".
  • Другой тип является "указателем на тип noexcept-функции F2 ".
  • F1 и F2 идентичны за исключением спецификатора noexcept.
C является "указателем на F1 ".
(начиная с C++17)
  • В противном случае, если выполняются все следующие условия:
  • T1 является "указателем на C1 ".
  • T2 является "указателем на C2 ".
  • Один из C1 и C2 является reference-related по отношению к другому.
C является
  • qualification-combined type для T1 и T2 , если C1 является reference-related к C2 , или
  • qualification-combined type для T2 и T1 , если C2 является reference-related к C1 .
  • В противном случае, если выполняются все следующие условия:
  • T1 или T2 является "указателем на член C1 типа функции F1 ".
  • Другой тип является "указателем на член C2 типа noexcept-функции F2 ".
  • Один из C1 и C2 является ссылочно-связанным с другим.
  • F1 и F2 идентичны за исключением спецификатора noexcept.
C является
  • "указателем на член C2 типа F1 ", если C1 является ссылочно-связанным с C2 , или
  • "указателем на член C1 типа F1 ", если 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*;
// Определение составного типа указателя для "p" и "q"
// подпадает под случай ["указатель на cv1 void" и "указатель на cv2 T"]:
// cv1 = пусто, cv2 = const, cv12 = const
// подставляем "cv12 = const" в "указатель на cv12 void":
// составной тип указателя - "const void*"
using pi = int**;
using pci = const int**;
// Определение составного типа указателя для "pi" и "pci"
// подпадает под случай [указатели на схожие типы "C1" и "C2"]:
// C1 = int*, C2 = const int*
// это ссылочно-родственные типы (в обоих направлениях), поскольку они схожи
// составной тип указателя - это тип с объединенной квалификацией
// "p1" и "pc1" (или "pci" и "pi"): "const int**"

Отчеты о дефектах

Следующие отчеты об изменениях в поведении, содержащие описания дефектов, были применены ретроактивно к ранее опубликованным стандартам C++.

DR Applied to Behavior as published Correct behavior
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 для Объявление указателей