Что такое ** в C++?
Описание проблемы
Я столкнулся с некоторым кодом, а также с ошибками, которые сгенерировал мой компилятор, содержащими токен <code>**</code>
перед переменной (например, имя_переменной unreferenced - или что-то подобное, точно не помню). Я довольно уверен, что это связано с указателями. Если бы мне пришлось догадываться, то это выглядит так, будто компилятор пытается разыменовать указатель дважды. Токен <code>**</code>
довольно сложно найти в интернете. Можете ли вы указать на хороший сайт или документацию, или кто-то мог бы объяснить это здесь?
Спасибо.
Также, если можно, добавлю: в каких ситуациях полезно иметь указатель на указатель? Разве не следует использовать оригинальный указатель вместо создания еще одного указателя на оригинальный указатель?
5 ответ(ов)
В языке C **
действительно представляет собой не только указатель на указатель (как в объявлении), но и разыменование разыменования (в выражении).
Этот подход часто используется в C, так как язык не поддерживает нотацию &
для ссылок. Например, он полезен для обновления возвращаемого значения, которое является указателем:
int alloc_foo(struct foo **foo_ret)
{
*foo_ret = malloc(sizeof(struct foo));
return 1; /* указывает на успешное выполнение; возвращаемое значение в foo_ret */
}
В этом примере foo_ret
является указателем на указатель на структуру foo
. Разыменование *foo_ret
позволяет выделить память для нового объекта типа struct foo
и установить адрес этого объекта в память, на которую указывает foo_ret
. Это способ, позволяющий функции изменять значение foo_ret
в вызывающем коде.
Вы можете узнать сигнатуру функции main() следующим образом:
int main(int argc, char* argv[])
Следующий вариант является эквивалентным:
int main(int argc, char** argv)
В этом случае, argv
представляет собой указатель на массив указателей на char
.
В языке C оператор индексации []
является просто другим способом выполнения арифметики указателей. Например,
foo[i]
выдает такой же код, как и
*(foo + i)
Это означает, что использование []
в C — это просто более удобная форма записи работы с указателями.
Это не токен **
. Это просто токен *
, за которым следует другой токен *
. В вашем случае у вас есть указатель на указатель, и он разыменовывается дважды, чтобы получить то, на что действительно указывает.
**
— это указатель на указатель.
Он может использоваться для представления матрицы (массива массивов) или массива строк (массива типа char
), и так далее.
Это двойная разыменовывающая операция.
В приведённом вами коде мы имеем три переменные:
int i = 3;
— обычная переменная типаint
, которая хранит значение 3.int* ptr_to_i = &i;
— указательptr_to_i
, который указывает на адрес переменнойi
.int** ptr_to_ptr_to_i = &ptr_to_i;
— указатель на указательptr_to_ptr_to_i
, который указывает на адрес указателяptr_to_i
.
Когда вы выполняете std::cout << **ptr_to_ptr_to_i << std::endl;
, происходит следующее:
*ptr_to_ptr_to_i
разыменовывает первый указатель, т.е. получает значениеptr_to_i
, что является адресом переменнойi
.- Далее, второе разыменование
**ptr_to_ptr_to_i
получает значение по этому адресу, т.е. значение переменнойi
, которое равно 3.
Таким образом, код выводит на экран значение 3.
Сравнение регулярного приведения типов, static_cast и dynamic_cast
Разница между const int*, const int * const и int * const?
Почему следует использовать указатель вместо самого объекта?
Почему код явно вызывает статический метод через указатель, равный null?
Статические глобальные переменные в C++