Объявление указателей
Указатель — это тип объекта, который ссылается на функцию или объект другого типа, возможно, с добавлением квалификаторов. Указатель также может ссылаться на ничего, что обозначается специальным значением нулевого указателя.
Синтаксис
В грамматике объявления указателя, последовательность type-specifier определяет тип указываемого объекта (который может быть типом функции или объекта, и может быть неполным), а declarator имеет вид:
* attr-spec-seq(необязательно) qualifiers(необязательно) declarator |
где 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, которые появляются между * и идентификатором (или другим вложенным declarator), квалифицируют тип объявляемого указателя:
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, или любая другая целочисленная константа со значением ноль). Статическая инициализация также инициализирует указатели своими нулевыми значениями.
Нулевые указатели могут указывать на отсутствие объекта или могут использоваться для обозначения других типов ошибок. В общем случае функция, принимающая аргумент-указатель, почти всегда должна проверять, является ли значение нулевым, и обрабатывать этот случай по-разному (например, free ничего не делает, когда передаётся нулевой указатель).
Примечания
Хотя любой указатель на объект может быть приведён к указателю на объект другого типа, разыменование указателя на тип, отличающийся от объявленного типа объекта, почти всегда является неопределённым поведением. См. строгое алиасирование для получения подробной информации.
|
Возможна передача функции информации о том, что указанные объекты не имеют алиасов. См. restrict для получения подробной информации. |
(с C99) |
Выражения lvalue типа массива, при использовании в большинстве контекстов, подвергаются неявной конверсии в указатель на первый элемент массива. См. массив для получения подробной информации.
char *str = "abc"; // "abc" is a char[4] array, str is a pointer to 'a'
Указатели на char часто используются для представления строк. Для представления корректной строки байтов указатель должен указывать на char, являющийся элементом массива char, и должен существовать char со значением ноль в некотором индексе, большем или равном индексу элемента, на который указывает указатель.
Ссылки
- Стандарт C17 (ISO/IEC 9899:2018):
- 6.7.6.1 Pointer declarators (с. 93-94)
- Стандарт C11 (ISO/IEC 9899:2011):
- 6.7.6.1 Pointer declarators (с. 130)
- Стандарт C99 (ISO/IEC 9899:1999):
- 6.7.5.1 Pointer declarators (с. 115-116)
- Стандарт C89/C90 (ISO/IEC 9899:1990):
- 3.5.4.1 Pointer declarators
См. также
| Документация C++ для Объявление указателя |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/c/language/pointer