Spec-Zone.ru › C

Объявление указателя

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

Синтаксис

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

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

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

float *p, **pp; // p is a pointer to float
                // pp is a pointer to a pointer to float
int (*fp)(int); // fp is a pointer to function with type int(int)

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

int n;
const int * pc = &n; // pc is a non-const pointer to a const int
// *pc = 2; // Error: n cannot be changed through pc without a cast
pc = NULL; // OK: pc itself can be changed

int * const cp = &n; // cp is a const pointer to a non-const int
*cp = 2; // OK to change n through cp
// cp = NULL; // Error: cp itself cannot be changed

int * const * pcp = &cp; // non-const pointer to const pointer to non-const int

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

Пояснение

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

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

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

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 = {1}
int* sp = &s.n; // pointer to the int that is a member of s

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

int n;
int* p = &n; // pointer p is pointing to n
*p = 7; // stores 7 in n
printf("%d\n", *p); // lvalue-to-rvalue conversion reads the value from n

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

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

int a[2];
int *p = a; // pointer to a[0]

int b[3][3];
int (*row)[3] = b; // pointer to b[0]

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

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

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

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

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

void f(int);
void (*pf1)(int) = &f;
void (*pf2)(int) = f; // same as &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;    // pointer p is pointing to f
(*p)(); // function f invoked through the function designator
p();    // function f invoked directly through the pointer

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

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

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* to void*
int* p2 = pv; // void* to int*
printf("%d\n", *p2); // prints 1

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

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

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

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

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

Примечания

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

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

(начиная с C99)

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

char *str = "abc"; // "abc" is a char[4] array, str is a pointer to '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++ для объявления указателя

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/c/language/pointer

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API