зачем нужны указатели на указатели

10.21 – Указатели на указатели и динамические многомерные массивы

Этот урок не является обязательным, он предназначен для продвинутых читателей, которые хотят узнать больше о C++. Никакие будущие уроки не будут основаны на этом уроке.

Указатель на указатель – это именно то, что можно ожидать из названия: указатель, содержащий адрес другого указателя.

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

Обычный указатель на int объявляется с помощью одной звездочки:

Указатель на указатель на int объявляется с помощью двух звездочек

Указатель на указатель работает так же, как обычный указатель – вы можете выполнять через него косвенное обращение, чтобы получить значение, на которое он указывает. А поскольку это значение само по себе является указателем, вы можете снова выполнить косвенное обращение уже через этот указатель, чтобы перейти к базовому значению. Эти косвенные обращения могут выполняться последовательно:

Показанный выше код напечатает:

Обратите внимание, что вы не можете установить указатель на указатель, используя непосредственно значение:

Это связано с тем, что оператор адреса ( operator& ) требует l-значение (l-value), но &value является r-значением (r-value).

Однако указатель на указатель может иметь значение null:

Массивы указателей

Указатели на указатели имеют несколько применений. Чаще всего они используется для динамического размещения массива указателей:

Двумерные динамически размещаемые массивы

Другое распространенное использование указателей на указатели – облегчение динамического размещения многомерных массивов (для обзора многомерных массивов смотрите урок «10.5 – Многомерные массивы»).

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

Динамическое размещение двумерного массива немного сложнее. У вас может возникнуть соблазн попробовать что-то вроде этого:

Но это не сработает.

Здесь есть два возможных решения. Если крайнее правое измерение массива является константой времени компиляции, вы можете сделать так:

Круглые скобки здесь необходимы для обеспечения правильного приоритета. В C++11 или новее это подходящий случай для использования автоматического определения типа:

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

Затем мы можем получить доступ к нашему массиву, как обычно:

С помощью этого метода, поскольку каждый столбец массива динамически размещается независимо, можно создавать динамически размещаемые двумерные массивы, которые не являются прямоугольными. Например, мы можем сделать массив в форме треугольника:

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

Обратите внимание, что мы удаляем массив в порядке, обратном его созданию (сначала элементы, затем сам массив). Если мы удалим массив до элементов массива, тогда нам потребуется доступ к освобожденной памяти, чтобы удалить эти элементы. А это приведет к неопределенному поведению.

Поскольку выделение и освобождение памяти для двумерных массивов сложно, и в них легко ошибиться, часто бывает проще «сгладить» двумерный массив (размером x на y) в одномерный массив размером x * y:

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

Передача указателя по адресу

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

Однако если мы хотим, чтобы функция могла изменять то, на что указывает аргумент-указатель, обычно лучше использовать ссылку на указатель. Поэтому мы не будем здесь больше об этом говорить.

Подробнее о передаче по адресу и передаче по ссылке мы поговорим в следующей главе.

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

Также возможно объявить указатель на указатель на указатель:

Это можно использовать для динамического выделения памяти для трехмерного массива. Однако для этого потребуется цикл внутри цикла, что значительно усложняет создание корректного кода.

Вы даже можете объявить указатель на указатель на указатель на указатель:

Или больше, если хотите.

Однако на самом деле в них не видно особой пользы, потому что не часто вам нужно так много косвенных обращений.

Заключение

Если доступны другие варианты, мы рекомендуем избегать использования указателей на указатели, поскольку они сложны в использовании и потенциально опасны. Достаточно легко выполнить косвенное обращение через нулевой или висячий указатель с помощью обычных указателей – а с указателем на указатель это легче вдвойне, поскольку вам нужно выполнить двойное косвенное обращение, чтобы перейти к базовому значению!

Источник

Зачем использовать двойной указатель? или Зачем использовать указатели на указатели?

когда следует использовать двойной указатель в C? Может ли кто-нибудь объяснить на примере?

Я знаю, что двойной указатель-это указатель на указатель. Зачем мне указатель на указатель?

19 ответов

если вы хотите получить список символов (слово), вы можете использовать char *word

если вы хотите список слов (предложение), вы можете использовать char **sentence

если вам нужен список предложений (монолог), Вы можете использовать char ***monologue

если вам нужен список монологов (биография), вы можете использовать char ****biography

если вам нужен список биографий (био-библиотека), вы можете использовать char *****biolibrary

да, я знаю, что это может не быть лучшим структуры данных!—23—>

одна из причин-вы хотите изменить значение указателя, переданного в функцию в качестве аргумента функции, для этого вам требуется указатель на указатель.

простыми словами, использовать ** Если вы хотите сохранить (или сохранить изменение) выделение или назначение памяти даже вне вызова функции. (Итак, передайте такую функцию с двойным указателем arg.)

Это может быть не очень хороший пример, но покажет вам основное использование:

но! если вы хотите, чтобы функция сделала это для вас, и вы хотите, чтобы результат сохранялся после завершения функции, вам нужно выполнить дополнительную работу, вам нужен новый pointer3, чтобы указать pointer1 и передать pointer3 функции.

вот забавный пример (сначала взгляните на выходной сигнал, чтобы понять!):

добавлять к Аша response, если вы используете один указатель на пример ниже (например, alloc1 ()), вы потеряете ссылку на память, выделенную внутри функции.

добавление дополнительных указателей расширяет размерность типа данных, от символа к строке, к массиву строк и так далее. Вы можете связать его с 1D, 2d, 3D-матрицей..

таким образом, использование указателя зависит от того, как вы объявите его.

выход будет AA. Это не работает, так как вы» передали значение » функции.

теперь расширьте это требование для обновления строки вместо характер.
Для этого необходимо получить параметр в функции в виде двойного указателя.

в этом примере метод ожидает, что двойной указатель в качестве параметра обновит значение строки.

Я видел очень хороший пример сегодня, с этот блог, как я описываю ниже.

представьте, что у вас есть структура для узлов в связанном списке, который, вероятно, является

теперь вы хотите реализовать remove_if функция, которая принимает критерий удаления rm как один из аргументов и пересекает связанный список: если запись удовлетворяет критерию (что-то вроде rm(entry)==true ), его узел будет удален из списка. В конце концов, remove_if возвращает заголовок (который может отличаться от исходного заголовка) связанного списка.

в своем for петли. Сообщение такое:без двойных указателей, вы должны поддерживать prev переменная для повторной организации указателей, и обрабатывать два разных случая.

но с двойными указателями вы можете написать

тебе не нужен prev вот так вы можете напрямую изменить что prev->next указал на.

чтобы сделать вещи яснее, давайте проследим код немного. Во время удаления:

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

надеюсь, это поможет,

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

ниже приведен очень простой пример C++, который показывает, что если вы хотите использовать функцию для установки указателя на объект, вам нужен указатель на указатель. В противном случае, указатель будет продолжать возвращаться к null.

(ответ на C++, но я считаю, что это то же самое в C.)

(также для справки: Google («pass by value c++») = » по умолчанию аргументы в C++ передаются по значению. Когда аргумент передается по значению, значение аргумента копируется в параметр функции.»)

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

простой пример, который вы, вероятно, видели много раз раньше

во втором параметре у вас есть: указатель на указатель на char.

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

см. также ответ для получения более подробной информации о вышеуказанной сигнатуре функции.

например, вы можете убедиться, что, когда вы освобождаете память о чем-то, вы устанавливаете указатель на null впоследствии.

когда вы вызываете эту функцию, вы вызываете ее с адресом указателя

Теперь myMemory имеет значение NULL, и любая попытка повторного использования будет очень очевидно неправильной.

например, если вы хотите случайный доступ к несмежным данным.

если sizeof(T) большой невозможно выделить непрерывный блок (т. е. с помощью malloc) из sizeof(T) * n байт.

затем создайте массив отсортированных указателей на объекты.

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

как сказано, одним из приложений double poinnter является обновление строки, чтобы сделанные изменения отражались обратно.

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

вы должны использовать двойные указатели при работе с указателями, которые изменяются в других местах вашего приложения. Вы также можете найти двойные указатели, чтобы быть необходимостью, когда вы имеете дело с оборудованием, которое возвращает и адрес вы.

почему двойные указатели?

цель состоит в том, чтобы изменить, на что указывает studentA, используя функцию.

применение двойной указатель, как показано на Матур Bhavuk кажется неправильным. Вот следующий пример является допустимым

следующий пример, который я даю, даст понимание или интуицию о том, как работают двойные указатели, я пройду через шаги

ниже приведен код, к которому вы можете относиться к вышеуказанным пунктам (a,b,c), чтобы понять

теперь для печати значения i.строки e в массиве, просто посмотрите на точку b, В случае строки значение, а также адрес одинаковы, поэтому нет необходимости разыменовать его снова.

теперь печатать строку

теперь, чтобы напечатать только символы в строке, обратитесь к пункту c)

теперь, чтобы напечатать следующий символ строки i.e » b» выйдите из 1 оператора разыменования и увеличьте его i.e переход от * * str к * str и do * str++

теперь выведите символ

С два массива («abcdefghij», «xylmnopqr») хранятся в непрерывном блоке памяти, если то же самое делается для увеличения адреса, все символы двух строк будут напечатаны

Источник

Указатели

Указатели

Э то, пожалуй, самая сложная и самая важная тема во всём курсе. Без понимания указателей дальнейшее изучении си будет бессмысленным. Указатели – очень простая концепция, очень логичная, но требующая внимания к деталям.

Определение

У казатель – это переменная, которая хранит адрес области памяти. Указатель, как и переменная, имеет тип. Синтаксис объявления указателей

Например
float *a;
long long *b;
Два основных оператора для работы с указателями – это оператор & взятия адреса, и оператор * разыменования. Рассмотрим простой пример.

Рассмотрим код внимательно, ещё раз

Была объявлена переменная с именем A. Она располагается по какому-то адресу в памяти. По этому адресу хранится значение 100.

Создали указатель типа int.

Теперь переменная p хранит адрес переменной A. Используя оператор * мы получаем доступ до содержимого переменной A.
Чтобы изменить содержимое, пишем

После этого значение A также изменено, так как она указывает на ту же область памяти. Ничего сложного.
Теперь другой важный пример

Будет выведено
4
4
8
4
Несмотря на то, что переменные имеют разный тип и размер, указатели на них имеют один размер. Действительно, если указатели хранят адреса, то они должны быть целочисленного типа. Так и есть, указатель сам по себе хранится в переменной типа size_t (а также ptrdiff_t), это тип, который ведёт себя как целочисленный, однако его размер зависит от разрядности системы. В большинстве случаев разницы между ними нет. Зачем тогда указателю нужен тип?

Арифметика указателей

В о-первых, указателю нужен тип для того, чтобы корректно работала операция разыменования (получения содержимого по адресу). Если указатель хранит адрес переменной, необходимо знать, сколько байт нужно взять, начиная от этого адреса, чтобы получить всю переменную.
Во-вторых, указатели поддерживают арифметические операции. Для их выполнения необходимо знать размер.
операция + N сдвигает указатель вперёд на N*sizeof(тип) байт.
Например, если указатель int *p; хранит адрес CC02, то после p += 10; он будет хранить адрес СС02 + sizeof(int)*10 = CC02 + 28 = CC2A (Все операции выполняются в шестнадцатиричном формате). Пусть мы создали указатель на начало массива. После этого мы можем «двигаться» по этому массиву, получая доступ до отдельных элементов.

Заметьте, каким образом мы получили адрес первого элемента массива

Массив, по сути, сам является указателем, поэтому не нужно использовать оператор &. Мы можем переписать пример по-другому

Если же указатели равны, то они указывают на одну и ту же область памяти.

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

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

Очевидно, ничто не мешает создать и указатель на указатель на указатель, и указатель на указатель на указатель на указатель и так далее. Это нам понадобится при работе с двумерными и многомерными массивами. А вот простой пример, как можно работать с указателем на указатель.

Указатели и приведение типов

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

В этом примере мы пользуемся тем, что размер типа int равен 4 байта, а char 1 байт. За счёт этого, получив адрес первого байта, можно пройти по остальным байтам числа и вывести их содержимое.

У казатель до инициализации хранит мусор, как и любая другая переменная. Но в то же время, этот «мусор» вполне может оказаться валидным адресом. Пусть, к примеру, у нас есть указатель. Каким образом узнать, инициализирован он или нет? В общем случае никак. Для решения этой проблемы был введён макрос NULL библиотеки stdlib.
Принято при определении указателя, если он не инициализируется конкретным значением, делать его равным NULL.

По стандарту гарантировано, что в этом случае указатель равен NULL, и равен нулю, и может быть использован как булево значение false. Хотя в зависимости от реализации NULL может и не быть равным 0 (в смысле, не равен нулю в побитовом представлении, как например, int или float).
Это значит, что в данном случае

вполне корректная операция, а в случае

поведение не определено. То есть указатель можно сравнивать с нулём, или с NULL, но нельзя NULL сравнивать с переменной целого типа или типа с плавающей точкой.

Примеры

Теперь несколько примеров работы с указателями
1. Пройдём по массиву и найдём все чётные элементы.

2. Когда мы сортируем элементы часто приходится их перемещать. Если объект занимает много места, то операция обмена местами двух элементов будет дорогостоящей. Вместо этого можно создать массив указателей на исходные элементы и отсортировать его. Так как размер указателей меньше, чем размер элементов целевого массива, то и сортировка будет происходить быстрее. Кроме того, массив не будет изменён, часто это важно.

3. Более интересный пример. Так как размер типа char всегда равен 1 байт, то с его помощью можно реализовать операцию swap – обмена местами содержимого двух переменных.

В этом примере можно поменять тип переменных a и b на double или любой другой (с соответствующим изменением вывода и вызова sizeof), всё равно мы будет обменивать местами байты двух переменных.

4. Найдём длину строки, введённой пользователем, используя указатель

Источник

Указатели в C++: зачем нужны, когда использовать и чем отличаются от обращения к объекту напрямую

Авторизуйтесь

Указатели в C++: зачем нужны, когда использовать и чем отличаются от обращения к объекту напрямую

зачем нужны указатели на указатели. point to pointer. зачем нужны указатели на указатели фото. зачем нужны указатели на указатели-point to pointer. картинка зачем нужны указатели на указатели. картинка point to pointer.

Даже если большинство программистов понимают, в чем разница между объектами и указателями на них, иногда бывает не совсем понятно, в пользу какого из способов обращения к объекту стоит делать выбор. Ниже мы постарались ответить на этот вопрос.

Вопрос

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

Аналогично с методами. Почему вместо этого:

мы должны писать вот это:

Я так понимаю, что это дает выигрыш в скорости, т.к. мы обращаемся напрямую к памяти. Верно? P.S. Я перешел с Java.

Ответ

Заметим, кстати, что в Java указатели не используются в явном виде, т.е. программист не может в коде обратиться к объекту через указатель на него. Однако на деле в Java все типы, кроме базовых, являются ссылочными: обращение к ним происходит по ссылке, хотя явно передать параметр по ссылке нельзя. И еще, на заметку, new в C++ и в Java или C# — абсолютно разные вещи.

Для того, чтобы дать небольшое представление, что же такое указатели в C++, приведем два аналогичных фрагмента кода:

Ближайший эквивалент на C++:

Однако вот это – совершенно другая вещь (C++):

На самом деле, совсем нет. Работа с указателями оформлена в виде кучи, в то время как работа с объектами – это стек, более простая и быстрая структура. Если вы новичок, то у нас для вас есть материал, в котором мы подробно рассказываем, что такое стек и куча.

Строго говоря, этот вопрос объединяет в себе два различных вопроса. Первый: когда стоит использовать динамическое распределение памяти? Второй: когда стоит использовать указатели? Естественно, здесь мы не обойдемся без общих слов о том, что всегда необходимо выбирать наиболее подходящий инструмент для работы. Почти всегда существует реализация лучше, чем с использованием ручного динамического распределения (dynamic allocation) и / или сырых указателей.

Динамическое распределение

Обычно принудительное установления срока жизни применяется в следующих ситуациях:

Указатели

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

Случаями, когда использование указателей можно рассматривать как возможный вариант, можно назвать следующие:

Источник

Урок №93. Указатели на указатели

Обновл. 13 Сен 2021 |

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

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

Обычный указатель типа int объявляется с использованием одной звёздочки:

Указатель на указатель типа int объявляется с использованием двух звёздочек:

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

Результат выполнения программы:

Обратите внимание, вы не можете инициализировать указатель на указатель напрямую значением:

Это связано с тем, что оператор адреса ( & ) требует l-value, но &value — это r-value. Однако указателю на указатель можно задать значение null:

Массивы указателей

Указатели на указатели имеют несколько применений. Наиболее используемым является динамическое выделение массива указателей:

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

Двумерные динамически выделенные массивы

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

Динамическое выделение двумерного массива немного отличается. У вас может возникнуть соблазн написать что-то вроде следующего:

Здесь вы получите ошибку. Есть два возможных решения. Если правый индекс является константой типа compile-time, то вы можете сделать следующее:

Скобки здесь потребуются для соблюдения приоритета. В C++11 хорошей идеей будет использовать ключевое слово auto для автоматического определения типа данных:

К сожалению, это относительно простое решение не работает, если правый индекс не является константой типа compile-time. В таком случае всё немного усложняется. Сначала мы выделяем массив указателей (как в примере, приведенном выше), а затем перебираем каждый элемент массива указателей и выделяем динамический массив для каждого элемента этого массива. Итого, наш динамический двумерный массив — это динамический одномерный массив динамических одномерных массивов!

Доступ к элементам массива выполняется как обычно:

Этим методом, поскольку каждый столбец массива динамически выделяется независимо, можно сделать динамически выделенные двумерные массивы, которые не являются прямоугольными. Например, мы можем создать массив треугольной формы:

В примере, приведенном выше, array[0] — это массив длиной 1, а array[1] — массив длиной 2 и т.д.

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

Обратите внимание, мы удаляем массив в порядке, противоположном его созданию. Если мы удалим массив перед удалением элементов массива, то нам придется получать доступ к освобожденной памяти для удаления элементов массива. А это, в свою очередь, приведет к неожиданным результатам.

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

Простая математика используется для конвертации индексов строки и столбца прямоугольного двумерного массива в один индекс одномерного массива:

Указатель на указатель на указатель на указатель и т.д.

Также можно объявить указатель на указатель на указатель:

Они могут использоваться для динамического выделения трехмерного массива. Тем не менее, для этого потребуется цикл внутри цикла и чрезвычайная аккуратность и осторожность, чтобы не наделать ошибок. Вы даже можете объявить указатель на указатель на указатель на указатель:

Или сделать еще большую вложенность, если захотите. Однако на практике такие указатели редко используются.

Заключение

Рекомендуется применять указатели на указатели только в самых крайних случаях, так как они сложны в использовании и потенциально опасны. Достаточно легко разыменовать нулевой или «висячий» указатель в ситуациях с использованием обычных указателей, вдвое легче это сделать в ситуациях с указателем на указатель, поскольку для получения исходного значения потребуется выполнить двойное разыменование!

Поделиться в социальных сетях:

Урок №92. Указатели типа void

Комментариев: 26

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

Я точно не знаю. Есть версия:
Место под локальные переменные выделяется в стеке при входе в функцию. Делается это одной инструкцией — декрементом регистра rsp на размер ВСЕХ локальных переменных (ну и плюс еще константный размер на служебные нужды). Одна инструкция с одним регистром на выделение стека под все локальные переменные. Мегабыстро. От этого вызов функции сравнительно не дорогая операция. А вот если заложить возможность динамического определения размера, то так дешево вызывать функцию уже не получится.
Ну то есть, моя версия — как все в плюсах это продиктовано стремлением к высокой производительности. То есть: делаем самым производительным способом путь даже это будет неудобно. А удобным непроизводительным способом делать лучше вообще не будем, ведь это все таки C++.

Когда я запускаю этот код:

Появляются две ошибки:
Ошибка (активно) E0137 выражение должно быть допустимым для изменения левосторонним значением

Ошибка C1090 Произошел сбой при вызове API PDB, код ошибки «3»

Тут проблема не в коде, а в чем-то другом.
У меня это компилится clang-ом и работает без проблем.

Почему в функции освобождении памяти компилятор ругается, что «Использование неинициализированной памяти » на delete[]parray[i]?

Здесь в delete[] parray лишнее. За цикл нужно вынести, чтоб освободить память только 1 раз

В параметрах функции мне необходимо сделать константный указатель на указатель(т.к. функция только выводит двумерный массив), но тогда при ее вызове компилятор ругается на то, что невозможно преобразовать аргумент из «double **» в «const double **. Почему так происходит?
Вот сама функция:

После прочтения этой темы понимаю, что мозг возмущенно отказывается переваривать эти указатели на указатели массивов и тут твой комментарий, поржал)

1) Почему при передачи в функции двухмерных массивов типы указателей разнятся?
пример:

2) Как правильно почистить переменную через delete чтобы не произошла утечка памяти в этом коде:

Покумекал сам и думаю пришел к правильным выводам:

1) Фиксированный массив передается в функцию таким образом потому что является настоящим двухмерным массивом, где все данные хранятся в памяти поочередно и не содержут указатель на другие подмассивы как это сделано в динамическом.

2) Чтобы удалить динамический указатель на указатель без утечки памяти нужно сделать так:

Объясните,пожалуйста,конкретнее,как происходит конвертации индексов строки и столбца прямоугольного двумерного массива в один индекс одномерного массива.

Ну как одну строку на 100 символов, разбить на 5 строк по 20 символов понятно?

Понятно. Я прошу объяснить на данном примере. Непонятно, что означают числа 9,4,5,что такое array[9,4] и как получается формула в return.

Но поднапрягшись таки вспомнил)
Смотри. 9 и 4 это координаты элемента массива который нам нужен для присваивания 3ки (в случае выше)
Число 5 — это количество столбцов в нашем массиве (не адрес элемента) Нужно нам количество столбцов чтобы умножить их на количество строк. Таким образом мы как бы восстанавливаем 2мерный массив из одномерного.
Для этого мы умножаем 9 (строка на самом деле 10я, но отсчет с 0)
на реальное количество столбцов (тут разумеется отсчёт с 1)) на 5.

Получаем «индекс» 45 (9*5).
Теперь осталось подобраться к самому элементу.
Добавляем 4. Тем самым мы перепрыгиваем с координат(8,4 — адрес 45ого элемента для ПК(для нас, по человечески это 9я строка 5 столбец) и оказываемся уже на 10строке (для ПК 9я, ведь с 0 считаем)
В общем по человечески это 50й элемент в массиве. Для ПК индекс 49.
Знаю, мудрено очень обьяснил, но надеюсь хоть часть моей инфы, будет в помощь.

строка на этот момент 9я для человека и 8я для ПК. После того как добавим 4ку, строка +1

Источник

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

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