Перейти к содержимому

Зачем нужен указатель на указатель

  • автор:

Указатели (C++)

Указатель — это переменная, в которой хранится адрес памяти объекта. Указатели широко используются как в C, так и в C++ для трех основных целей:

  • для выделения новых объектов в куче,
  • передача функций другим функциям
  • для итерации элементов в массивах или других структурах данных.

В программировании в стиле C необработанные указатели используются для всех этих сценариев. Однако необработанные указатели являются источником многих серьезных ошибок программирования. Поэтому их использование настоятельно не рекомендуется, за исключением случаев, когда они обеспечивают значительное преимущество производительности, и нет неоднозначности в отношении того, какой указатель является владельцем указателя, который отвечает за удаление объекта. Современный C++ предоставляет интеллектуальные указатели для выделения объектов, итераторов для обхода структур данных и лямбда-выражений для передачи функций. Используя эти средства языка и библиотеки вместо необработанных указателей, вы сделаете программу более безопасной, проще отладить и упростить понимание и обслуживание. Дополнительные сведения см . в смарт-указателях, итераторах и лямбда-выражениях .

В этом разделе

  • Необработанные указатели
  • Константные и переменные указатели
  • новые и удаленные операторы
  • Интеллектуальные указатели
  • Практическое руководство. Создание и использование экземпляров unique_ptr
  • Практическое руководство. Создание и использование экземпляров shared_ptr
  • Практическое руководство. Создание и использование экземпляров weak_ptr
  • Практическое руководство. Создание и использование экземпляров CComPtr и CComQIPtr

Указатели на указатели — многочисленное перенаправление

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

Здесь p объявляется как указатель на целое, a q — это указатель на указатель на целое. Вызов printf() выводит число 10 на экран.

Указатель на указатель?

В современном С++ вообще указателей стараются избегать. Но вот несколько примеров:

Псевдо2D массив — массив массивов

Массив указателей. Например вектор хранит внутри указатель на буфер. .data() возвращает этот указатель. Если там хранятся указатели, получается указатель на указатель.

Когда нужно изменить указатель и используется АПИ С: указатель на объект используется если нужно изменить объект так, чтобы ето было видно вне изменяющей функции. В данном случае объект — другой указатель.

Ответ написан более трёх лет назад
Нравится 1 4 комментария

xozzslip

Что значит псевдо2d? Как тогда настоящие nd массивы делаются по-вашему?

Хасан Хафизов: > «Что значит псевдо2d?»
Значит что их тип это не «массив массивов» и все гарантии к 2D массивам на них не распространяются.

int a[5][5]; int** b = new int *[5]< new int [5], new int [5], new int [5], new int [5], new int [5], >; assert(&(a[2][4]) + 1 == &(a[3][0])); //Всегда true assert(&(b[2][4]) + 1 == &(b[3][0])); //Скорее всего false

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

xozzslip

В первой строке вы выделяете прямоугольный массив (подходит для таблиц). А при помощи указателей можно создавать массивы, каждый элемент которого будет массив различной длинны (подходит для строк). И создавать их надо, конечно, динамически, но изначально некоторого небольшого размера, а при надобности вызывать realloc. Так не придется знать всех размеров.

Хасан Хафизов: Проблема с указателями на указатели в невозможности обеспечить cache locality и data prefetch. В результате скорость обработки ощутимо падает.

xozzslip

Указатели — весьма низкоуровневая вещь, но без них нельзя представить современного программирования.

Подумайте, что есть переменная? Ведь память — это, на деле, просто лента, на которую можно написать 0 и 1. Так вот переменная — лишь небольшой участок этой памяти. Если мы выделяем под переменную 4 байта, то она будет занимать 32 ячейки, в каждой из которых будут записаны 0 или 1. Ну хорошо, записали мы ее, а как же нам теперь ее считать? Тут и требуются указатели! Указатель указывает на первый бит участка, в котором и хранится переменная. Дальше, действуя подобным образом можно получить массивы, а дальше и все современное программирование.

Понимать эту абстракцию — совершенно необходимо. Научитесь писать на C. Советую Д. Ритчи «язык программирования си». Язык очень прост, содержательная часть книги всего 100 страниц. Но пару неделек придется потратить.

Указатель на указатель — что это?

Указатель в 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]); >
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++ обычно ручное управление памятью не приветствуется, поэтому там кратные указатели встречаются куда реже.

Отслеживать
ответ дан 25 ноя 2014 в 14:36
207k 28 28 золотых знаков 293 293 серебряных знака 526 526 бронзовых знаков

Ни в языке С++, ни в языке С, нет такого понятия, как «указатель на указатель» в виде самостоятельной сущности с какими-то новыми качественными свойствами. Поэтому в строгом смысле слова, вопрос о том «зачем нужен указатель на указатель» не имеет никакого смысла.

В языках С и С++ есть такое понятие, как указатель. Просто указатель. Т.е. если у вас есть некий тип T , то вы можете объявить указатель p на этот тип

и заставить этот указатель указывать на объект t типа T

T t; p = &t; 

После этого выражения t и *p будут обозначать один и тот же объект. Т.е. если вы, например, поменяете значение объекта *p , то тем самым вы поменяете и объект t (и наоборот). Вы можете также завести еще сколько угодно указателей на один и от же объект.

Это — элементарные основы идеи указателя.

Ну так а далее можно просто заметить, что тип T сам по себе может быть указательным типом. Но это совершенно ничего не меняет. Нет ничего принципиально разного между ситуацией, когда T — это int , и ситуацией, когда T — это double * . Все вышесказанное относится к обоим случаям в одинаковой мере.

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

И рассматривать такие указатели надо как обычные указатели. То же самое в полной мере справедливо и об «указателях на указатели на указатели», «указателях на указатели на указатели на указатели» и т.д. до бесконечности.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *