3

Мне надо передать в функцию с прототипом:

void print(int **mt, int lines, int columns)

массив, созданный на стеке:

int tmp[3][3] = {
    {5, 1, 6},
    {3, 0, 4},
    {2, 0, 3}
};

Как это синтаксически верно записывается?

UPD:
Я отлично знаю, что массив можно создать разными способами. Но меня интересует строго:

  • print( int ** // обратить внимание, что две звездочки
  • int tmp[3][3] // обратить внимание, что объект на стеке
sys_dev
  • 4,012
  • 1
    Если только это не учебное задание, я бы использовал int **temp c new и с последующим освобождением памяти. Или вектор векторов, тогда и память освобождать не придется. В противном случае @smallFish прав. – BuilderC Sep 27 '14 at 11:53
  • @avp: Мне кажется, ваш код стоит вынести в ответ. (Он, кроме прочего, полезен как адаптер между двумя популярными форматами двумерных массивов.) – VladD Sep 27 '14 at 22:00
  • 1
    @VladD, OK. Перенес. – avp Sep 28 '14 at 09:34
  • 1
    мне кажется, учитывая UPD, ответ @avp является именно ответом на поставленный вопрос. Потому что да, типы разные, и если прям не меняем-не меняем, то нужен адаптер. – Arkady Sep 29 '14 at 07:48

2 Answers2

8
  1. Вы не можете знать, где именно будет создан tmp, а где выделена память под его элементы. Компилятор может что-то писать в регистры, что-то в кучу, что-то в стек, гарантий нет.

    UPD: речь идет именно о том, что выражение int tmp[3][3] НЕ гарантирует, что память будет выделена на стеке.

  2. Вопреки ощущениям, N-мерные массивы, где N > 1 в C/C++ не эквивалентны указателю на указатель (N раз). Связано это с тем, что tmp[3][3] имеет тип int tmp[][3]/int (*tmp)[3] и реально в памяти лежит одним массивом, длиной в 9 int.

    Т.е. int[3][3] в памяти лежит вот так: int[3] int[3] int[3], а каждый из int[3] лежит в памяти как int int int

Как следствие, это просто разные типы данных. И приведение к int**, даже через reinterpret_cast приведет к падению, т.к. фактически там нет int** никуда.

Чтобы заработало, Вам следует изменить void print(int **mt, int lines, int columns) на void print(int mt[][3], int lines, int columns). Или заняться "колдовством" с typedef-ами, и в итоге все равно поменять интерфейс функции или вариант хранения данных из tmp.

Если же Вы уверены, что все должно быть так, как оно есть, то можно сделать костыль.

int* newtmp = reinterpret_cast<int*>(tmp);
void print(int* tmp, int rows, int cols)
{
    for (unsigned x =0; x < rows; ++x)
    {
       for (unsigned y =0; y < cols; ++y)
       {
          cout << tmp[cols*x + y];
       }
    }
}

Но это костыль, потому что закладываться на то, как в памяти лежит какой-то тип (а мы как бы знаем, что tmp[N][K] будет лежать одним массивом длиной N*K) и ковырять его прямыми обращениями к памяти - архитектурное зло, за которое выгоняют из Хогвардса :)

UPD: интерфейс функции print в любом случае не имеет никакого смысла при работе с объектами типа int tmp[X][Y]

Arkady
  • 1,501
  • Ваша версия print с другим типом указателя. В вашем используется int* , я же привел версию int**. Обратите внимание на то, что звездочек две, а не одна.

  • Я отлично знаю, что можно по-разному создать массив. Можно на стеке, можно в куче и т.д. и т.п. Но в данном варианте строго "int **" и строго на стеке "int tmp[3][3]".

  • Вопрос как раз и состоит в том, что, зная, что только п.2. и никаких вариаций, правильно передать tmp.

    – sys_dev Sep 27 '14 at 14:27
  • @sys_dev, "int tmp[3][3]" - это НЕ гарантирует создания на стеке, о чем я и написал. Это НЕ строго на стеке, именно об этом написано в моем ответе. Про то, что int tmp[3][3] и int** - принципиально разные типы - я тоже написал. Я написал, как в памяти выглядит int tmp[3][3] и почему приведение его к int** бессмысленно. Соответственно, и интерфейс print становится бессмысленным. И я предложил 2 варианта, как его поменять, чтобы вернуть смысл. С костылем и без. Поэтому там предложен костыль, который позволит, зная организацию в памяти int[3][3], работать с ним через указатель. – Arkady Sep 27 '14 at 17:35
  • 1
    @sys_dev, откровенно говоря, стек тут вообще ни при чем (стек к решению этой задачи никакого отношения не имеет).

    --

    Просто все мы знаем, что на самом-то деле данные при таком описании:

    int tmp[3][3] внутри функции,

    компилятор разместит последовательно в стеке.

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

    – avp Sep 27 '14 at 21:40
  • 1
    @avp, компилятор не обязан размещать это на стеке. Что-то может быть размещено, например, в регистрах, точно может. И, насколько я помню, он даже может разместить это на куче при определенных условиях. Мы по факту не управляем этими процессами. Т.е. на самом деле мы не знаем, где разместит это компилятор, но предполагаем, что скорее всего на стеке. – Arkady Sep 28 '14 at 06:08
  • @smallFish, Вы говорите о каком-то абстрактном компиляторе.

    Реально же в регистрах (например, в архитектуре x86-64) передаются несколько первых параметров функции.

    Однако, это ни на что (в смысле исходного кода) не влияет. В любом случае можно написать так:

    void f (int a) {
      printf ("a: %p %d\n", &a, a);
    }
    
    
    

    В этом случае (увидев выражение взятия адреса аргумента) компилятор выделит место под вспомогательную переменную (в стеке) и скопирует в прологе функции в нее значение аргумента (из регистра).

    – avp Sep 28 '14 at 10:21
  • @avp, мои слова про стек был просто формой занудства, к вопросу он отношения не имеет, но сам по себе, имхо, интересен, поэтому я и написал про него в первом абзаце. Однако да, программист языка C++ не управляет тем, где именно будет выделена память, если жестко не указывает директиву, вроде register. И это важно понимать, даже если мы знаем, что современные популярные реализации языка C++ и выделят такое на стеке, говорить "это я выделяю на стеке" программисту С++, имхо, неправильно, когда язык не гарантирует выделения на стеке. – Arkady Sep 29 '14 at 07:53
  • 1
    Сделал я это просто "из любви к искусству") вдруг зародится дискуссия и я узнаю что-то новое. Или кто-то узнает что-то новое. – Arkady Sep 29 '14 at 07:55