Я правильно понял связь указатель на указатель и двумерный массив?
Добрый день! В пейнте нарисовал такую схему, в которой выразил свое понимание устройства двумерного массива. Подскажите пожалуйста, насколько оно является точным:
Нет, понял неправильно. int[2][2] != int **.
Но расположение элементов нарисовал правильное.
Идея в том, что двумерный массив -- это массив массивов. Т.е. int [2][2] == int ([2])[2].
Указатель на указатель -- совсем другая песня.
Лол, так и float можно привести к void **, и вообще, всё что угодно можно привести к любому другому типу достаточно сильным заклинанием, но это не значит, что это правильно или будет работать.
Нужно понять одну простую вещь: в С нет многомерных массивов. Есть просто массивы. Измерение у них всегда одно. Но их элементами могут быть массивы.
Массив можно привести только к типу "указатель на элемент массива". Потому что у указателя тоже одно измерение. Т.е. int a[2][2] можно присвоить int (*p)[2]. Это правильно и будет работать. Мало того, такое приведение типа не требует явного оператора приведения, оно будет работать без него: int a[2][2]; int (*p)[2]; p = a;.
jcmvbkbc, мне казалось, что массивы в C++ это не что иное как более понятная абстракция, за которыми идут указатели. Привык, что с одномерным массивом можно работать через указатель, а с двумерными я до сих пор не могу никак разобраться, не могу понять их взаимосвязь с указателями
Чтобы понять, в чём разница int ** и int [][], нужно спросить себя: на что ссылается выражение после первого разыменования? В случае массива ответом будет "на массив". Т.е. int a[2][2]; a[1] тип a[1] -- int [2]. В памяти лежат подряд два int. В случае указателя ответом будет "на указатель". Т.е. int **p; p[1] тип p[1] -- int *. В памяти лежит указатель, чей-то адрес. Как по-твоему приведение типа может поменять содержимое памяти с двух целых чисел на адрес?
jcmvbkbc, спасибо огромное за объяснение
действительно, int** указывает на область памяти в которой лежит адрес на Int, а значит int a[2][2] != (int**)a
теперь понял, что int a[2][2] это int(*a)[2], а int b[2][2][2] == int(*b)[2][2] == int(*b)[2]
теперь один вопрос остается загадочным - как же перемещаться по указателям на массив? долго ломал голову, пока писал функцию для распечатывания трехмерного массива, в голову пришло только то, что нужно брать указатель на этот массив и как-то перемещаться:
void print_multiple(int* pint, int dim1, int dim2, int dim3){
int (*pointer3d)[dim2][dim3];
int (*pointer2d)[dim3];
for(int i = 0; i < dim1; ++i){
pointer3d += i;
for(int j = 0; j < dim2; ++j){
pointer2d += j;
for(int k = 0; k < dim3; ++k){
//???
}
}
}
}
дальше, к сожалению, вариантов ну вообще никаких не вижу
долго ломал голову, пока писал функцию для распечатывания трехмерного массива, в голову пришло только то, что нужно брать указатель на этот массив и как-то перемещаться
int (*pointer3d)[dim2][dim3];
int (*pointer2d)[dim3];
Вы не объявляете массив, вы объявляете указатель! Почувствуйте разницу!
И пока вы указателю не присвоите адрес реального массива, он у вас будет указывать фиг знает куда - в сегфолт, скорее всего.
Хорошую тему вы подняли. Вчера, ради интереса, по мотивам вашего диалога с jcmvbkbc наваял небольшой тест с использованием в С99 указателей на массив совместно с динамическим массивом.
Жаль, что msvc это не собирает. Но gcc и clang - все нормально.