I need to pass to the function with the prototype:

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

array created on the stack:

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

How is this syntactically correct?

UPD :
I know perfectly well that an array can be created in different ways. But I am strictly interested in:

  • print (int ** // note that two asterisks
  • int tmp [3] [3] // note that the object is on the stack
  • one
    Unless this is a learning task, I would use int ** temp c new and then free up memory. Or a vector of vectors, then the memory does not have to be released. Otherwise, @smallFish is right. - BuilderC
  • @avp: I think your code should be returned. (It, among other things, is useful as an adapter between two popular formats of two-dimensional arrays.) - VladD
  • one
    @VladD, OK. Transferred - avp
  • one
    It seems to me, given the UPD, the answer @avp is exactly the answer to the question posed. Because yes, the types are different, and if we don’t change directly, we don’t change, then we need an adapter. - Arkady

2 answers 2

  1. You cannot know exactly where tmp will be created, and where memory is allocated for its elements. The compiler can write something in registers, something in a heap, something in a stack, there are no guarantees.

    UPD: we are talking about the fact that the expression int tmp [3] [3] does not guarantee that the memory will be allocated on the stack.

  2. Contrary to sensations, N-dimensional arrays, where N> 1 in C / C ++ are not equivalent to a pointer to a pointer (N times). This is due to the fact that tmp[3][3] is of type int tmp[][3] / int (*tmp)[3] and is actually in memory in one array, 9 int. Long.

    Those. int[3][3] in memory is like this: int[3] int[3] int[3] , and each of int[3] is in memory as an int int int

As a result, these are just different data types. And casting to int** , even through reinterpret_cast will lead to a fall, because in fact, there is no int** anywhere.

To make it work, you should change void print(int **mt, int lines, int columns) to void print(int mt[][3], int lines, int columns) . Or do "witchcraft" with typedefs, and in the end, anyway, change the function interface or the data storage option from tmp.

If you are sure that everything should be as it is, then you can make a crutch.

 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]; } } } 

But this is a crutch, because it lays on how some type of memory lies in memory (and we kind of know that tmp [N] [K] will lie in one array of length N * K) and pick it with direct memory calls - architectural evil for which they are expelled from Hogwarts :)

UPD: interface print function in any case does not make any sense when working with objects of type int tmp[X][Y]

  • 1. Your print version with a different pointer type. In your int * is used, I resulted the int ** version. Note that there are two stars, not one. 2. I know perfectly well that you can create an array in different ways. It is possible on the stack, it is possible in the heap, etc. etc. But in this version it is strictly "int **" and strictly on the "int tmp [3] [3]" stack. The question is precisely that, knowing that only p.2. and no variations, correctly pass tmp. - sys_dev
  • @sys_dev, "int tmp [3] [3]" - this does NOT guarantee creation on the stack, which I wrote about. This is NOT strictly on the stack, that’s what my answer says. About the fact that int tmp[3][3] and int** are fundamentally different types - I also wrote. I wrote how int tmp[3][3] looks in memory and why casting it to int** makes no sense. Accordingly, the print interface becomes meaningless. And I suggested 2 options for how to change it to return the meaning. With crutch and without. Therefore, there is proposed a crutch, which allows, knowing the organization in the memory int [3] [3], to work with him through the pointer. - Arkady
  • one
    @sys_dev, frankly, the stack has nothing to do with it (the stack has nothing to do with this task). “We just all know that, in fact, the data is in this description: int tmp[3][3] inside a function, the compiler will post sequentially on the stack. But the source code itself does not matter where it is (stack, heap, static data area, shared data segment ...) in principle this sequence of ints can be placed when the program is executed does not change. - avp
  • one
    @avp, the compiler is not required to put it on the stack. Something can be placed, for example, in registers, just can. And, as I recall, he can even place it on a heap under certain conditions. We in fact do not manage these processes. Those. in fact, we don’t know where the compiler will place it, but we assume that it’s most likely on the stack. - Arkady
  • one
    I made it just “for the love of art”) a discussion suddenly arose and I would learn something new. Or someone learns something new. - Arkady

@sys_dev , if you are sure that a function with such a prototype will work correctly with the matrix that you described, then you can call it, for example, like this:

  print((int **)&tmp[0][0], 3, 3); 

The code of correctly working print() with such (foolish for a given case) prototype can be, say, like this:

 void print(int **mt, int lines, int columns) { int *a = (int *)mt, i, j; // просто приведем тип к подходящему для доступа к последовательно расположенным в памяти элементам массива for (i = 0; i < lines; i++) for (j = 0; j < columns; j++) printf("%d%c", *a++, j == columns - 1 ? '\n' : ' '); } 

And what to do if the print function still works with data that actually corresponds to its prototype?

Then the function expects a matrix, actually constructed as an array of pointers to rows. This is often done when creating "dynamic" matrices (i.e., an array of previously unknown size on the heap).

Typically, the code that creates such a matrix is ​​something like

 int **create_matrix (int n_lines, int n_cols) { int **res = (int **)malloc(sizeof(*res) * n_lines * n_cols); if (res) { int i; for (i = 0; i < n_lines; i++) if (!(res[i] = (int *)malloc(sizeof(**res) * n_cols))) return 0; } return res; } 

And the function code print() will be, for example, like this:

 void print(int **mt, int lines, int columns) { for (int i = 0; i < lines; i++) for (int j = 0; j < columns; j++) printf("%d%c", mt[i][j], j == columns - 1 ? '\n' : ' '); } 

Then in the calling function you just need to make an array "adapter" (adapter), from pointers to tmp lines and pass it to print.

  int *pt[3] = {&tmp[0][0], &tmp[1][0], &tmp[2][0]}; print(pt, 3, 3); 

Naturally, this array can be filled dynamically (in a loop).

  for (i = 0; i < 3; i++) pt[i] = &tmp[i][0]; 

UPDATE

And so it is still possible to write (at least in gcc 4.8.2 ).

 #include <stdio.h> #include <stdlib.h> void print(int columns, int a[][columns], int lines) { int i, j; for (i = 0; i < lines; i++) { for (j = 0; j < columns; j++) printf("%d ", a[i][j]); puts(""); } } int main (int ac, char *av[]) { int t[3][4] = {{1,2,3,4}, {5,6,7,8}, {21,22,23,24}}; print(4, t, 3); return 0; } 

Pay attention to the description of the int a[][columns] parameter in the print() argument list and the dynamic (by the current value also of the int columns parameter) its size!

It is with such a record that the compiler has enough information to calculate the addresses a[i][j] .

  • Thanks for your reply. Can you find lines describing your answer in the standard? - sys_dev
  • @sys_dev, on health :). And about the standards, so (unfortunately (or fortunately?)) I am not a fan of such literature . (I once read K & R, and from time to time I look at other similar resources here). - avp
  • @avp, It is very difficult to mark any answer with a real answer. And I used to choose one of the answers! ) - sys_dev
  • one
    @sys_dev, we are not at school, and the marks here do not matter. Better arm yourself with your favorite text editor, compiler (including with the option to get the assembler code) and debugger, and learn what practically happens in different cases. - avp
  • @avp, I always do this. Because I develop software protection and I am much more dearly mash.kod. But, seeing that the stack variable does not work with the print () method, it really surprises me. As a rule, compilers build sub esp, <size of all local variables> and are addressed mov eax, [ebp + local variable offset]. Surprisingly enough, the print code does not work. I never would have suspected a trick in this situation. - sys_dev