So it does not compile

class A { private: int arr[10][10]; public: int** getArr() {return arr;} } 

So it is going, but we get an error at runtime (code 11 - an attempt to access locked memory) https://ideone.com/Pq9gLn

 class A { private: int arr[10][10]; public: int** getArr() {return (int**)arr;} } ... A a; int** arr = a.getArr(); cout << arr[0][0]; 

Why is that? After all, in theory, int ** and int [] [] are the same. What is the difference?

  • one
    int** и int[][] одно и то же - no. - PinkTux
  • one
    In fact, there is a difference. int ** is the address of a variable that points to an int array (i.e., int [] ), but int [][] from t. memory locations are the same int [] (a sequence of 32-bit words in memory). So, return &arr[0][0] as int * , and in the calling code, refer to the matrix as a one-dimensional array a[i][j] == a[i * N + j] (where N is the number of columns ( ie, the elements of each row of the matrix). - avp
  • You can read my article , I sorted out the differences there in detail. - ixSci

1 answer 1

The array in expressions is converted to a pointer to its first element.

If you have, for example, an array declaration

 T a[N]; 

where T is a type and N is the number of elements in the array, then using the name a in expressions is converted to type T * . This can be represented as

 T *tmp = a; 

A two-dimensional array is an array of arrays. That is, if you have an array of the form

 int a[10][10]; 

then a is an array of 10 elements, which in turn are arrays of type int[10] .

You can enter a typedef declaration for these items.

For example,

 typedef int T[10]; 

And then the array declaration will look like

 T a[10]; 

As stated above, in expressions, an array is converted to a pointer to its first element.

Therefore, this transformation can be represented as

 T *tmp = a; 

where T is an alias for type int[10] Therefore, if you remove the typedef declaration, you will get

 int ( *tmp )[10] = a; 

The types int ( * )[10] and int ** are two different types.

For example, print to the console the size of the objects for which these pointers are defined and compare them

 #include <iostream> int main() { int **p; int ( *q )[10]; std::cout << sizeof( *p ) << std::endl; std::cout << sizeof( *q ) << std::endl; return 0; } 

The output of the program may look like this.

 4 40 

That is, in the first case the size of the scalar object is displayed, and in the second case the size of the array.

Therefore, the correct method definition in your class will look like this.

 class A { private: int arr[10][10]; public: int ( * getArr() )[10] { return arr; } }; 

or

 class A { private: int arr[10][10]; public: typedef int ( *T )[10]; T getArr() { return arr; } }; 

As for your example

 class A { private: int arr[10][10]; public: int** getArr() {return (int**)arr;} }; ... A a; int** arr = a.getArr(); cout << arr[0][0]; 

then the variable arr will get the address of the extent occupied by the original two-dimensional array. When the arr[0] expression is used, the array is accessed, where its first element is stored. It is assumed that arr[0] , equivalent to the expression *arr , in turn, will return a pointer. But the source array does not store pointers. It generally stores arbitrary values. Therefore, a memory access error occurs.

For clarity, consider the following example. Suppose that sizeof( int ) and sizeof( int * ) are equal. For the arr[0][0] construction to work for you, where arr is of type int ** , the source array must be defined as shown in the following demo program.

 #include <iostream> int main() { int a[][2] = { { reinterpret_cast<int>( &a[1][0] ), 20 }, { 30, 40 }, }; int **arr = reinterpret_cast<int **>( a ); std::cout << arr[0][0] << std::endl; return 0; } 

In this case, arr[0] returns a pointer to the element of the array a[1][0] , that is, &a[1][0] . By applying the indexing operator to the resulting expression again, you get the integer 30 .

However, if the first element of the array contains an arbitrary integer, such as 10 , then arr[0] returns this value, which in the expression arr[0][0] will be interpreted as a memory address, and a memory access error will occur.

Thus pointer

 int **arr; 

interprets the array

 int a[N][N]; 

like an array of type

 int * tmp[N]; 

That is, it considers the elements of the original array as objects that store the actual values ​​of the pointers, and this is generally not the case.

  • Thanks, It turns out, **p is an address to an address, (*q)[10] is a static array of addresses to the first elements. So at *q + 2 is the second address, i.e. the first element of array number 2. Then, according to the idea *(q + 2) , should return this element, BUT if the cast (int**)q was performed and returned from the function, as in the example of the question, it does not allow accessing the memory - you don't know what the problem is - Nikita
  • @Nikita int (* q) [10] is a pointer to an object that is an array of 10 elements. Therefore, for example, the operation ++ q will change the value in q by the value of 10 * sizeof (int), and as follows from the example in the response to 40 bytes. The expression * (q + 2) returns a reference to the third element of a two-dimensional array, that is, figuratively speaking, to the third line in a two-dimensional array. - Vlad from Moscow
  • cpp.sh/7wv5w The first option is not compiled, cpp.sh/7ahs the second with typedef works. Please add to the answer. - Nikita
  • @Nikita It was a typo on forgetfulness. :) - Vlad from Moscow