Namespaces
Variants

Pointer declaration

From cppreference.net

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

Содержание

Синтаксис

В грамматике объявлений объявления указателя, спецификатор-типа обозначает указываемый тип (который может быть функциональным или объектным типом и может быть неполным), а декларатор имеет вид:

* attr-spec-seq  (необязательно) qualifiers  (необязательно) declarator

где declarator может быть идентификатором, который именует объявляемый указатель, включая другой декларатор указателя (что будет указывать на указатель на указатель):

float *p, **pp; // p - указатель на float
                // pp - указатель на указатель на float
int (*fp)(int); // fp - указатель на функцию с типом int(int)

Квалификаторы, qualifiers которые появляются между * и идентификатором (или другим вложенным декларатором) квалифицируют тип объявляемого указателя:

int n;
const int * pc = &n; // pc — неконстантный указатель на константный int
// *pc = 2; // Ошибка: n нельзя изменить через pc без приведения типа
pc = NULL; // OK: сам pc может быть изменён
int * const cp = &n; // cp — константный указатель на неконстантный int
*cp = 2; // OK: можно изменить n через cp
// cp = NULL; // Ошибка: сам cp не может быть изменён
int * const * pcp = &cp; // неконстантный указатель на константный указатель на неконстантный int

attr-spec-seq (C23) является необязательным списком атрибутов , применяемых к объявленному указателю.

Объяснение

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

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

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

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

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

int n;
int* p = &n; // указатель p указывает на n
*p = 7; // сохраняет 7 в n
printf("%d\n", *p); // преобразование lvalue-to-rvalue читает значение из n

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

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

int a[2];
int *p = a; // указатель на a[0]
int b[3][3];
int (*row)[3] = b; // указатель на b[0]

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

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

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

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

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

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

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

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

#include <stdio.h>
int f(int n)
{
    printf("%d\n", n);
    return n * n;
}
int main(void)
{
    int (*p)(int) = f;
    int x = p(7);
}

Разыменование указателя на функцию даёт обозначение функции для указываемой функции:

int f();
int (*p)() = f;    // указатель p указывает на f
(*p)(); // функция f вызвана через обозначение функции
p();    // функция f вызвана напрямую через указатель

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

Поскольку совместимость типов функций игнорирует квалификаторы верхнего уровня параметров функций, указатели на функции, параметры которых различаются только квалификаторами верхнего уровня, являются взаимозаменяемыми:

int f(int), fc(const int);
int (*pc)(const int) = f; // OK
int (*p)(int) = fc;       // OK
pc = p;                   // OK

Указатели на void

Указатель на объект любого типа может быть неявно преобразован в указатель на void (опционально const или volatile -квалифицированный), и наоборот:

int n=1, *p=&n;
void* pv = p; // преобразование int* в void*
int* p2 = pv; // преобразование void* в int*
printf("%d\n", *p2); // выводит 1

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

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

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

Чтобы инициализировать указатель значением null или присвоить нулевое значение существующему указателю, можно использовать нулевой указатель-константу ( NULL , или любую другую целочисленную константу со значением ноль). Статическая инициализация также инициализирует указатели их нулевыми значениями.

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

Примечания

Хотя любой указатель на объект может быть приведён к указателю на объект другого типа, разыменование указателя на тип, отличный от объявленного типа объекта, почти всегда является неопределённым поведением. Подробности см. в разделе strict aliasing .

Можно указать функции, которая обращается к объектам через указатели, что эти указатели не создают псевдонимы. Подробности смотрите в разделе restrict .

(since C99)

Выражения lvalue типа массива, используемые в большинстве контекстов, подвергаются неявному преобразованию в указатель на первый элемент массива. Подробности см. в разделе array .

char *str = "abc"; // "abc" представляет собой массив char[4], str - указатель на 'a'

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

Ссылки

  • Стандарт C23 (ISO/IEC 9899:2024):
  • 6.7.6.1 Деклараторы указателей (стр.: TBD)
  • Стандарт C17 (ISO/IEC 9899:2018):
  • 6.7.6.1 Деклараторы указателей (стр. 93-94)
  • Стандарт C11 (ISO/IEC 9899:2011):
  • 6.7.6.1 Указатели в объявлениях (стр. 130)
  • Стандарт C99 (ISO/IEC 9899:1999):
  • 6.7.5.1 Деклараторы указателей (стр: 115-116)
  • Стандарт C89/C90 (ISO/IEC 9899:1990):
  • 3.5.4.1 Деклараторы указателей

Смотрите также

C++ documentation для Pointer declaration