LINUX.ORG.RU

Правильная расшифровка int (*a)[2] = new int[n][2];

 , ,


0

1

Имеется определение:

int (*a)[2] = new int[n][2];

И есть два варианта его понимания:

1. Создается переменная-указатель на 2 массива из значений типа int, что может рассматриваться и как указатель на массив из двух элементов типа int (так как общий размер массива одинаковый)

2. Запись int (*a)[2] представляет указатель на массив из двух элементов типа int.

Какой вариант однозначно правильный?

★★★★★

Какой вариант однозначно правильный?

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

ox55ff ★★★★★
()
Ответ на: комментарий от theNamelessOne

https://cdecl.org/

int (*a)[2] - declare a as pointer to array 2 of int

Что бы это значило? Определить a как указатель на массив длинной 2 из int? Определить a как указатель на 2 массива из int?

new int[n][2] - syntax error

В общем, яснее не стало.

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от theNamelessOne

Указатель на массив из 2 элементов типа int.

Хорошо, теперь рассмотрим выражение new int[n][2]. Действуем поэтапно.

new int[n] - выделить динамическую память для массива из n элементов.

new int[n][2] - выделить динамическую память для двух массивов из n элементов.

Правильно или нет?

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от Xintrea

Хорошо, теперь рассмотрим выражение new int[n][2]. Действуем поэтапно.

new int[n] - выделить динамическую память для массива из n элементов.

Ты пропустил этап «почитать учебник»

utf8nowhere ★★★
()

В общем, более правильное описание будет такое:

int (*a)[2] = new int[n][2];

Создается переменная-указатель на массив из 2-х элементов int и она указывает на нуливой элемент массива длинной n. Каждый элемент массива является указателем на соответствующий массив из 2-х элементов типа int.

Xintrea ★★★★★
() автор топика

а вы говорите - Лев Толстой.

для начала предлагаю распарсить

Жили-были кот, дрозд да петушок — золотой гребешок

а там как пойдет.

olelookoe ★★★
()

int (*a)[2] – объявление a как указателя на массив из двух int.

В С++ типом а будет не (int **), а int (*)[2].

new int[n][2] – создание массива из n элементов, каждый элемент которого указывает на массив из двух элементов типа int (которые тоже создаются).

soomrack ★★★★★
()
Последнее исправление: soomrack (всего исправлений: 1)
Ответ на: комментарий от soomrack

В С++ типом а будет не (int **), а int (*)[3].

Размерность, используемая в типе, должна быть фиксированной? И через этот тип нельзя обратиться к массиву длинной 2 а не 3?

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от Xintrea

Можно сделать приведение типов. Но твой пример кода действительно плохой, т.к. сам сопоставь что ты объявляешь и что присваиваешь, есть некоторое несоответствие…

soomrack ★★★★★
()
Ответ на: комментарий от soomrack

Но твой пример кода действительно плохой, т.к. сам сопоставь что ты объявляешь и что присваиваешь, есть некоторое несоответствие…

Не вижу разницы, объясни.

Сам пример взят вот отсюда: https://metanit.com/cpp/tutorial/4.12.php

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от Xintrea

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

Ну формально это одно и тоже, но с т.з. контроля границ массива это несколько мутно.

Если честно, то я не очень понимаю область использования двумерных массивов вида new a[m][n], т.к. это, по-сути, матрица но созданная хз пойми как. Лучше выделить память m*n и самостоятельно разметить ее (если нужно). А если это массив массивов, где длины могут меняться независимо, т.е. точно не матрица, то лучше использовать std::vector и std::array и пр.

soomrack ★★★★★
()
Ответ на: комментарий от soomrack

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

Дык когда работаешь с указателями, какой, к черту, контроль границ массива? Да и без указателей тоже - выходи за границы, никто не отследит если сам следить не будешь. (Речь в данном случае о размерности n).

Если честно, то я не очень понимаю область использования двумерных массивов вида new a[m][n], т.к. это, по-сути, матрица но созданная хз пойми как. Лучше выделить память m*n и самостоятельно разметить ее (если нужно).

Я тоже не понимаю зачем так извращенно сделано. В i386 в машкоде даже есть готовые команды для работы с двумерными массивами (доступ к памяти по X + xSize * Y), но они не могут применяться если делать двумерный массив в стандартном C/C++ стиле. Для 3-х измерений и выше тоже непонятно зачем при доступе к элементу вместо математических вычислений смещения ячейки «на месте», нужно делать цепочку чтений указателей из памяти. Учитывая, что память под многомерный массив выделяется одним блоком.

Xintrea ★★★★★
() автор топика

кто-то не читал главу учебника про n-мерные(двумерные) массивы. Объявление указателя такое. тип_данных (*lbl)[размерность] Если делать указатель на указатель, то компилятор не сможет узнать вторую размерность. Вообще, по-умному здесь надо делать одноразмерный массив и вручную указывать границы. Потом будет меньше гемора.

arrecck ★★★
()
Ответ на: комментарий от Xintrea

Дык когда работаешь с указателями, какой, к черту, контроль границ массива? Да и без указателей тоже - выходи за границы, никто не отследит если сам следить не будешь. (Речь в данном случае о размерности n).

Ну тогда да, тогда без разницы.

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

А это точно так? Я вот не знаю как там в стандарте на этот счет написано. И можно ли потом delete[] a[3] и a[3] = new int[20] сделать?

soomrack ★★★★★
()
Ответ на: комментарий от arrecck

кто-то не читал главу учебника про n-мерные(двумерные) массивы. Объявление указателя такое. тип_данных (*lbl)[размерность]

Учебники разные бывают, ссылку я уже привел. Ты хочешь сказать, что в скобках указывается не размер массива, а размерность? То есть определение int (*ourArray)[5] указывает на пятимерный массив а не на массив длинной 5?

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

Но это же не помешает обращаться к элементу как ourArray[x][y]. А контроля границ массива в C/C++ и так нет.

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

Но за каким-то хреном авторы языка же сделали синтаксис такой как в топике.

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от Xintrea

Но за каким-то хреном авторы языка же сделали синтаксис такой как в топике.

Синтаксис вполне логичный.

int a[2] – массив из 2х элементов.

int* a[2] – массив указателей на int из двух элементов.

int (*a)[2] – указатель на массив из двух элементов.

(*a) в объявлении означает, что а это будет указатель, т.е. как бы объявляешь *a, которое есть понятно что, а пользуешься только a.

soomrack ★★★★★
()
Ответ на: комментарий от soomrack

А это точно так? Я вот не знаю как там в стандарте на этот счет написано. И можно ли потом delete[] a[3] и a[3] = new int[20] сделать?

Я к тому, что вместо циклического выделения (что выделяет память фрагментами):

int** numbers{new int*[rows]{}};
for (unsigned i{}; i < rows; i++)
{
   numbers[i] = new int[columns]{};
}

Можно написать одно выделение:
int (*a)[2] = new int[n][2];

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

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от Xintrea

Ну вот то-то и оно. Что это, упрощенное написание циклической инициализации или создание матрицы с вектором указателей на строки? Надо стандарт смотреть. И то и другое имеет смысл.

soomrack ★★★★★
()
Ответ на: комментарий от Xintrea

Как бы объяснить. объявление int a[2][5] выделит память под 2*5 int’ов и область под 2 указателя. Обращение a[0] и a[1] - это указатели. Объявление int (a)[5] создаст область памяти 15 и область памяти под указатель на эту выделенную область.

arrecck ★★★
()
Ответ на: комментарий от arrecck

Никаких двух указателей оно не выделит. int[2][5] это 10 int-ов и ничего кроме них. [] это синтаксический сахар для двумерной адресации, на суть алгоритма не влияет.

firkax ★★★★★
()

new int[n][m] выделяет блок памяти размером sizeof(int)*m*n байт и возвращает указатель на него, скастованный в тип int(*)[m], который записывается в переменную a. Блок памяти логически (в исходнике) представлен как n строк по m элементов каждая, расположенных одна за другой непрерывно в памяти.

Тип int(*)[m] означает вот что:

a - указатель на начало блока памяти

a[0] - всё тот же указатель на начало блока памяти, но скастованный в int*

a[j] - указатель на начало j-ой строчки (она начинается через j*m*sizeof(int) байт от начала), скастованный в int*

a[j][i] - значение элемента, начинающегося от начала блока через такое количество байт: (j*m+i)*sizeof(int)

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

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 5)
Ответ на: комментарий от firkax

new int[n][m] выделяет блок памяти размером sizeof(int)*m*n байт и возвращает указатель на него, скастованный в тип int(*)[m], который записывается в переменную a. Блок памяти логически (в исходнике) представлен как n строк по m элементов каждая, расположенных одна за другой непрерывно в памяти.

Определение int(*a)[m] означает вот что:

a - указатель на начало блока памяти

a[ 0 ] - всё тот же указатель на начало блока памяти, но скастованный в int*

a[ j ] - указатель на начало j-ой строчки (она начинается через j*m*sizeof(int) байт от начала), скастованный в int*

a[ j ][ i ] - значение элемента, начинающегося от начала блока через такое количество байт: (j*m+i)*sizeof(int)

Другими словами, определение int (*a)[m] дает возможность работать с массивом, созданным через new int[n][m]. Причем массив создается планарным, просто как n x m элементов int без этих ваших указателей на подмассивы.

Правильно?

* * *

Получается, что вот это утверждение неверно:

int (*a)[2] = new int[n][2];

Создается переменная-указатель на массив из 2-х элементов int и она указывает на нуливой элемент массива длинной n. Каждый элемент массива является указателем на соответствующий массив из 2-х элементов типа int.


Оно было бы верно, если бы тип был int**. Но тогда определение с этим типом не скомпилилось бы: int** a = new int[n][2].

Правильно?

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от Xintrea

Другими словами, определение int (*a)[m] дает возможность работать с массивом, созданным через new int[n][m]. Причем массив создается планарным, просто как n x m элементов int без этих ваших указателей на подмассивы.

Да, на низком уровне он плоский, но компилятор даёт наглядный синтаксис для имитации двумерной таблицы.

Можешь даже скастовать его в int* и пользоваться этими n*m элементов вручную одномерно. Или наоборот - сделать new int[n*m] и скастовать в int(*)[m] и пользоваться двумерно.

Получается, что вот это утверждение неверно:

Я не очень понял что там имелось ввиду. Но int** и int[][] - это разные типы, да. Первый и правда можно использовать как массив указателей на вложенные массивы, но выделить такую структуру за один раз невозможно - надо выделять каждый вложенный отдельным new.

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 2)
  • a Бююююкафка Ааааааааа
  • *a Станооовица пааачтальёёёнам
  • (*a) И тут она натужилааась, зимой в тулупчик сууунулааась
  • (*a)[2] И сильно напряглась, чтоб получилось двась

И вот она красивая, в тулупчике идёт
И в сумочках двух синеньких
Нам два письма несёт
Пам-пам пара-рам па-ра-ра-рам
Пам-пам пара-рам пам-free…

LINUX-ORG-RU ★★★★★
()
Ответ на: комментарий от arrecck

int **a; и int a[10][10]; это не только разные типы, но и хранятся они по разному, в первом случае действительно указатели на указатели, во втором плоский массив на 100 элементов.

MOPKOBKA ★★★★
()
Ответ на: комментарий от utf8nowhere

Ну и да и нет

  • a[0] это сокращение к a + ( 0 * sizeof(a))
  • a[1] это сокращение к a + ( 1 * sizeof(a))

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

Ну в смысле операция получения смещения по указателю vs смещённый указатель. Тут как воспринимать. А на этапе объявления это вообще про другое…

LINUX-ORG-RU ★★★★★
()
Последнее исправление: LINUX-ORG-RU (всего исправлений: 1)