Часто встречаю вот такую конструкцию:
int ** p;
Я так понимаю, что это указатель на указатель. Зачем такая двойственность нужна и где применяется?
Указатель в C — не семантика, а механизм. Он сам по себе не несёт смысла, но может использоваться для выражения того или иного смысла. То же относится и к двойному указателю: он может использоваться для разных вещей. Вот несколько примеров.
В C параметры передаются по значению, то есть из коробки нету передачи параметров «по ссылке» (они же &
-параметры C++, они же ref
-/out
-параметры C#). Для того, чтобы объявить такой параметр, пользуются указателем на фактический параметр (то есть, передают в функцию адрес параметра). Если тип самого параметра — указатель, получается двойной указатель. Пример:
void split_half(char* input, char** first_half, char** second_half)
{
var len = strlen(input);
var halflen = len / 2;
*first_half = malloc(halflen + 1);
strncpy(*first_half, input, halflen);
(*firsthalf)[halflen] = 0;
*second_half = strdup(&input[halflen]);
}
В C указатель может обозначать массив. Если тип элемента массива — указатель, получается двойной указатель. Классический пример:
int main(int argc, char** argv) { ... }
Двойной указатель можно использовать для массива массивов. Например, квадратная матрица:
struct matrix
{
int** data;
int width;
int height;
}
void init_matrix(int width, int height, struct matrix* matrix)
{
matrix->width = width;
matrix->height = height;
matrix->data = malloc(height * sizeof(int*));
for (int y = 0; y < height; y++)
matrix->data[y] = malloc(width * sizeof(int));
}
В C++ обычно ручное управление памятью не приветствуется, поэтому там кратные указатели встречаются куда реже.
Ни в языке С++, ни в языке С, нет такого понятия, как "указатель на указатель" в виде самостоятельной сущности с какими-то новыми качественными свойствами. Поэтому в строгом смысле слова, вопрос о том "зачем нужен указатель на указатель" не имеет никакого смысла.
В языках С и С++ есть такое понятие, как указатель. Просто указатель. Т.е. если у вас есть некий тип T
, то вы можете объявить указатель p
на этот тип
T *p;
и заставить этот указатель указывать на объект t
типа T
T t;
p = &t;
После этого выражения t
и *p
будут обозначать один и тот же объект. Т.е. если вы, например, поменяете значение объекта *p
, то тем самым вы поменяете и объект t
(и наоборот). Вы можете также завести еще сколько угодно указателей на один и от же объект.
Это - элементарные основы идеи указателя.
Ну так а далее можно просто заметить, что тип T
сам по себе может быть указательным типом. Но это совершенно ничего не меняет. Нет ничего принципиально разного между ситуацией, когда T
- это int
, и ситуацией, когда T
- это double *
. Все вышесказанное относится к обоим случаям в одинаковой мере.
Вот, собственно и все. Т.е. нет никакого смысла вводить в рассмотрение такую сущность, как "указатель на указатель", и устраивать вокруг нее какие-то обсуждения. Все, что нам нужно - это обычный указатель, который может просто-напросто указывать и на другой указатель. Но эти два уровня указательности (три, четыре, пять уровней...) совершенно отдельны, ничего о друг друге не знают и знать не хотят.
И рассматривать такие указатели надо как обычные указатели. То же самое в полной мере справедливо и об "указателях на указатели на указатели", "указателях на указатели на указатели на указатели" и т.д. до бесконечности.
Когда-то объяснял это на примере холодильника.
int **fridge; // Холодильник с полочками на которых еда
*fridge; // Полочка в холодильнике
**fridge; // Колбаса
Эта конструкция нужна для создания динамического массива, и чтоб не копировать все данные заново, копируются только их указатели-индексы с расширением индекса и с последующим его заполнением.
Оборудование для ресторана: новинки профессиональной кухонной техники
Частный дом престарелых в Киеве: комфорт, забота и профессиональный уход
Кто-нибудь когда-то встречался с подобной реализацией скип списка? Рассматривал варианты реализации с помощью vector и setНо возникают затруднения...
Как можно, не используя итератор begin() и end(), двигаться в списке? Нужно просто nextСудя по описанию, то там нет просто next() итератора