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.

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

★★★★★

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

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

Это сокращение к *(a + 1) (ну или *((a)+(1)), если прям со всеми скобками пейсать)

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

По адресам его не отличить от int a[100]; int **a; отличить легко, каждый подмассив будет по своему адресу если выделять это через for () new

И для int **a еще оверхед на указатели будет, помимо ненужного выделения динамической памяти многораз.

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

У а есть тип, у типа есть конкретный размер в байтах, число в [скобочках] говорит нам разное,
на этапе объявления type a[N] грубо говоря a = allocа(sizeof(type) * N)
на этапе обращения x = a[N] равно x = (a + (sizeof(a) * N))[0].

С нуликом в конце корректнее если мы про байты говорим, а не просто * разыменовываем. a для примера, псевдокодный конечно. До операции сложения мы считаем что указатель приведён к char, а зизеоф берёт уже реальный размер, если читать иначе белиберда получится

LINUX-ORG-RU ★★★★★
()
Последнее исправление: LINUX-ORG-RU (всего исправлений: 6)
Ответ на: комментарий от utf8nowhere

во втором плоский массив на 100 элементов.

Нет, это был бы int a[100]
int a[10][10] это 10 массивов по 10 int.

... которые занимают столько же места, что и int a[100]:

    int a[100];
    int b[10][10];
    
    cout << sizeof( a ) << endl;
    cout << sizeof( b ) << endl;

400
400

Что ты на это скажешь?

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

Если у тебя 10 массивов, то где-то должны храниться 10 указателей на начало каждого массива. Итого размер такой конструкции никак не может быть 400 байт.

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

Не должно, хранится может только 400 байт. Для int a[10][10]; не хранится ни одного указателя.

Есть разница между указателями на указатели и настоящим многомерным массивом.

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

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

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

Но это указатель, тем не менее

Нет, тут уже читай стандарт.

Как он может нигде не храниться, если он хранит адрес первого элемента?

Компилятору известно и так что первый элемент это какой нибудь rsp-64, зачем это хранить в какой то переменной?

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

Моя мысль в том, что utf8nowhere написал про 10 массивов:

int a[10][10] это 10 массивов по 10 int

Но это не 10 отдельных массивов. Это просто двумерный массив 10x10, где элементы стоят «вплотную» друг к другу. Если бы это были полноценные 10 массивов, то потребовалось бы хранить еще 10 указателей на первый элемент, чтобы с ними работать. Ведь по мнению utf8nowhere это 10 массивов.

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

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

Но про хранение [10][10] ты верно говоришь, он действительно будет выглядеть как int[100], однако это и есть полноценные массивы в С/C++.

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

Угу.

int a[n][10][20] это трехмерный массив (матрица) n на 10 на 20 элементов. Выделяется одним блоком размера n*10*20 * sizeof(int) + возможно доп. место под выравнивание и вообще.

можно использовать a, a[1], a[1][2], a[1][2][3], каждый будет соотв. типа.

a[2] будет эквивалентно *(a+2) т.е. а сдвинутое на 2 * 10 * 20 * sizeof(int) и тип указателя на массив 10х20.

PS: нашел в стандарте описание a[][][] и new int[][][].

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

Это указатель не массив, а массив в кишках всегда указатель в итоге =) И везде всегда хранится всё. На любую область памяти (если она есть отдельная, а не регистровая) всегда есть указатель по другому ЭВМ просто не работают в принципе и по определению. (Справедливости ради вместо указателей могут хранится лишь смещения и то не как есть, а вычисляемые в процессе работы программы по ходу дела)

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

Это как двое обсуждают леса и спорят сильно, один про леса в смысле лес, леса, деревья гнутся. А второй про леса строительные. И один говорит что леса после пожара восстанавливаются, а втрой у виска крутит и говорит, ты мол чё дурак, как леса могут восстановится они же деревянные были, да и железные тоже как!!. Вроде слова одни, а смысл разный :D

LINUX-ORG-RU ★★★★★
()
Последнее исправление: LINUX-ORG-RU (всего исправлений: 2)
Ответ на: комментарий от LINUX-ORG-RU

а массив в кишках всегда указатель в итоге =)

Нет.

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

Нет.

по другому ЭВМ просто не работают в принципе и по определению

У тебя какая то другая ЭВМ, у меня есть команда mov [rax+8], 0xBEEF, я могу вообще не знать какой адрес у rax+8

Просто тут как бэ в рамках указателей массивов просто не существует

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

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

Автор темы тащит C-style приемы работы с массивами в C++.

Это кстати очень характерный момент, где в C++ МОЖНО делать проще, но из-за обратной совместимости с C люди начинают вместо С++ писать на «С с классами». И получаются вот такие вот вопросы.

Еще раз подчеркну - вопросы абсолютно уместные, если речь идет о чистом C, но у ТС в тегах «с++» !

В 2023 году, в языке, в который завезли всякие лямбды, constexpr и прочую сатану(настолько, что уже наверное нет человека, кто понимает ВЕСЬ современный стандарт C++ и объем имеющихся из-за этого граблей), люди до сих пор задаются вопросами как объявлять массивы в C-стиле.

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

А как ты еще определишь 3dimension массив с фиксированным размером (но размер известен будет только в рантайме) выделенный одним куском? Придется вспоминать объявления массивов из С.

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

Начнем с того, что если тебе нужна прям такая байтоебля и оптимизации(память обязательно одним куском, вот это вот всё), то тогда тебя ждет удивительный мир C++-аллокаторов и кастомных классов.

И да, с 99,(9)% вероятностью так под капотом будет вот эти вот чудо-конструкции из C.

Ты, конечно, можешь возразить - зачем использовать какие-то ненужные абстракции С++, если можно хреначить low level код в C-семантике? Но абстракции в C++ как раз и нужны, чтобы не хреначить low level код без нужды(преждевременной оптимизации).

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

Тащить ее в прикладное ПО никто технически не запрещает конечно, но это сравнимо с сексом в гамаке.

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

Начнем с того, что если тебе нужна прям такая байтоебля и оптимизации(память обязательно одним куском, вот это вот всё), то тогда тебя ждет удивительный мир C++-аллокаторов и кастомных классов.

А причем тут аллокаторы, new тоже может использовать кастомный аллокатор, просто добавив «умных слов» задачу не решишь. Я написал в общем то конкретный запрос.

чудо-конструкции из C.
машкодах

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

Так вот, никакие тебе аллокаторы не помогут сделать массив который я прошу, а если знать как определяются типы в C/C++ то можно это сделать в одну строчку без всяких абстракций, и еще, где же ты возьмешь эти классы? А их только писать самому, но тогда ты поимеешь даже больше проблем, ведь придется реализовывать массив с нуля со всем его поведением, и это очень серьезная работа.

Поэтому наиболее правильным решением будет не обижаться на легкие вопросы на которые не можешь дать ответ, а просто узнать про описание типов, а конкретнее:

1. Как и где ставится const
2. Как определяются указатели на функции, или указатели на функции которые принимают функции
3. Как определяются массивы, обычные, VLA

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

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

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

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

x = new int[n] y = new int[n][2]

Правильно. В первом случае получается указатель на int, во втором указатель на int[2]. Что тоже по сути указатель на int.

Практическая разница только такая что x++ увеличит указатель на 8 а y++ на 16.

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

в общем типичное такое поведение обезьяна

О, а вот и ярлыки подъехали. Конструктивненько! Я, кстати, если ты не заметил, ни на тебя, ни на ТСа с криками «уволить» не наезжал.

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

Мне эта конструкция абсолютно понятна в рамках C. Мне нихрена непонятно зачем в рамках описанной ТС задачи тащить это говно в C++. Чтобы что?

Вот тебе аналогия: в C можно использовать ассемблерные вставки(а я, будучи еще школьником, дико угарал по ассемблеру - настолько что написал GUI-приложение аналог taskmgr.exe на чистом ассемблере с использованием WinAPI). Итак, о чём это я. Ах, да:

1) в C можно использовать ассемблерные вставки;
2) В ассемблерных вставках можно реализовать циклы;
3) Стоит ли выкинуть нахрен циклы из C++ и использовать вместо них ассемблерные вставки с циклами?

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

см. выше мой пассаж про выкидывание циклов

Но злиться из за использования возможностей языка, когда это в тему, это слишком.

Так оно как раз и НЕ В ТЕМУ. Вот что ты получаешь, отказываясь от использования классов в C++ для этой задачи?

Читабельность кода? Нет(не надо меня убеждать в обратном, я насмотрелся на творения всяких гуру, которые мне читать было непросто, а моим коллегам подчас и вовсе невозможно).

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

В задаче намечаются такие объемы? Если да, тогда конечно можно подумать о том, чтобы спуститься пониже(благо C++ позволяет) и позаниматься байтоебством. Но не раньше. И не надо заливать мне про то, что код надо сразу писать, чтобы он скейлился под увеличение объемов данных в 10, тысячу, миллион раз, не надо.

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

Мне нихрена непонятно зачем в рамках описанной ТС задачи тащить это говно в C++. Чтобы что?

Я выше описал другую задачу и предложил тебе дать решение для нее, что там у ТС я не знаю, может там в реальности другая размерность, другие размеры, и научные расчеты, а может ему нужно выделить массив int 2x2 и это можно сделать даже на стеке, поэтому рассуждать о его задаче бессмысленно, я предложил свою но тебе видимо о ней рассуждать неинтересно или ты пропустил, в общем тогда мне ответить нечего, говорим о разном.

Очевидно что там где можно легко и удобно использовать std::array или std::vector их использовать конечно же нужно. Но они не всегда подходят.

MOPKOBKA ★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 1)

int (*a)[2] =

Читаем как компилятор.

‘a’ это… (идем вправо, упираемся в скобку)

‘a’ это… (идем влево, видим *) указатель

‘a’ это указатель … (идем вправо, видим [2]) на массив из двух …

‘a’ это указатель на массив из двух … (идем вправо, упираемся в = )

‘a’ это указатель на массив из двух … (идем влево, видим int)

‘a’ это указатель на массив из двух объектов типа int

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

Ни в C, ни в C++ так никто не пишет.

Код должен +- быть такой:


#define SIZE_X 100
#define SIZE_Y 100

#define AT2D(ARRAY, X, Y) ARRAY[(X) + (Y)*SIZE_X]

int
main (void)
{
  int* array = malloc(sizeof(int) * SIZE_X * SIZE_Y);
  if (!array)
  {
    perror("Array allocation error");
    exit(1);
  }
  
  AT2D(array, 3, 4) = 5;
  
  free(array);

  return 0;
}

А на C++ соответственно +- такой:

template<class T>
class Matrix2D
{
private:
  Vector<T> m_data;
  const size_t m_x_size, m_y_size; 
public:
  Matrix2D (size_t x, size_t y) : m_x_size(x), m_y_size(y), m_data(x*y)
  {
  }
  
  T& operator() (size_t x, size_t y)
  {
    return m_data[m_x_size*y + x];
  }

  T operator() (size_t x, size_t y) const
  {
    return m_data[m_x_size*y + x];
  }

};

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

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

И потом, конечно же, нам очень удобно работать через указатель на массив из двух объектов с двумерным массивом. Да, понятно что через указатель на один int* мы может работать с массивом int-ов. То же самое и с указателем на массив из двух ячеек можно работать с двумерным массивом то есть с «массивом из массивов двух ячеек int». Но это так дико выглядит.

Но и это не главное. Главное то, что задача динамического выделения памяти под многомерный массив все равно толком не решена. Через подобный указатель и new, динамически можно создать только массив, у которого переменным может быть только одно (первое) измерение. Все остальные измерения должны быть фиксированы прямо в коде.

Т. е. только так:

size_t n;
std::cout << "Enter first array dimension:";
std::cin >> n;

float (*a)[250][1000] = new float[n][250][1000];

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

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

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

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

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

Вот так вот, как я показал. Выбросьте ваш учебник. Читайте «Программирование. Принципы и практика с использованием C++» Страуструпа. Автор языка пишет нормально.

zx_gamer ★★
()