Так не компилируется
class A {
private: int arr[10][10];
public: int** getArr() {return arr;}
}
Так собирается, но получаем ошибку во время исполнения(код 11 - попытка доступа к заблокированной памяти) 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];
Почему так? Ведь по идее int** и int[][] одно и то же. В чём разница?
Ответ
Массив в выражениях преобразуется к указателю на свой первый элемент.
Если у вас есть, например, объявление массива
T a[N];
где T это некоторый тип, а N - число элементов в массиве, то использование имени a в выражениях преобразуется к типу T *. Это можно представить как
T *tmp = a;
Двумерный массив - это массив массивов. То есть если у вас есть массив вида
int a[10][10];
то a - это массив из 10 элементов, которые в свою очередь массивы с типом int[10]
Вы можете ввести объявление typedef для этих элементов.
Например,
typedef int T[10];
И тогда объявление массива будет выглядеть как
T a[10];
Как сказано выше, в выражениях массив преобразуется в указатель на свой первый элемент.
Следовательно это преобразование можно представить как
T *tmp = a;
где T - это алиас для типа int[10] Следовательно, если убрать объявление typedef, то вы получите
int ( *tmp )[10] = a;
Типы int ( * )[10] и int ** - два разных типа.
Например, выведите на консоль размер объектов, для которых определены эти указатели и сравните их
#include
int main()
{
int **p;
int ( *q )[10];
std::cout << sizeof( *p ) << std::endl;
std::cout << sizeof( *q ) << std::endl;
return 0;
}
Вывод программы может выглядеть следующим образом
4
40
То есть в первом случае выводится размер скалярного объекта, а во втором случае размер массива.
Поэтому правильное определение метода в вашем классе будет выглядеть так
class A
{
private:
int arr[10][10];
public:
int ( * getArr() )[10] { return arr; }
};
или
class A
{
private:
int arr[10][10];
public:
typedef int ( *T )[10];
T getArr() { return arr; }
};
Что касается вашего примера
class A {
private: int arr[10][10];
public: int** getArr() {return (int**)arr;}
};
...
A a;
int** arr = a.getArr();
cout << arr[0][0];
то переменная arr получит адрес экстента, занимаемого исходным двумерным массивом. При использовании выражения arr[0] происходит обращение к памяти массива, где хранится его первый элемент. При этом предполагается, что arr[0] , эквивалентное выражению *arr, в свою очередь вернет указатель. Но исходный массив не хранит указатели. Он хранит в общем случае произвольные значения. Поэтому происходит ошибка обращения к памяти.
Для наглядности рассмотрите следующий пример. Допустим, что sizeof( int ) и sizeof( int * ) равны между собой. Чтобы у вас работала конструкция arr[0][0], где arr имеет тип int **, исъодный массив должен быть определен, как показано в следующей демонстрационной программе.
#include
int main()
{
int a[][2] =
{
{ reinterpret_cast
int **arr = reinterpret_cast
std::cout << arr[0][0] << std::endl;
return 0;
}
В этом случае arr[0] возвратит указатель на элемент массива a[1][0], то есть &a[1][0] . Применяя к полученному выражению снова оператор индексирования, вы получите целое число 30
Однако если первый элемент массива содержит произвольное целое число, как, например, 10, то arr[0] вернет это значение, которое в выражении arr[0][0] будет интерпретироваться как адрес памяти, и произойдет ошибка обращения к памяти.
Таким образом указатель
int **arr;
интерпретирует массив
int a[N][N];
как массив, имеющий тип
int * tmp[N];
То есть рассматривает элементы исходного массива как объекты, хранящие действительные значения указателей, а это в общем случае не так.
Комментариев нет:
Отправить комментарий