Я хотел бы прояснить концепцию, которую я не смог полностью понять.
Почему в C тип int **m
совместим с int *m[N]
, но не с int m[N][N]
? 2D-массив можно рассматривать как указатель на массив целых чисел, поэтому int (*m)[N]
. Однако его также можно рассматривать как массив указателей на целые числа, поэтому int *m[N]
. Чего я не понимаю, так это почему int **m
нельзя рассматривать как int m[][]
, а как int *M[]
, а int m[][]
можно рассматривать как int (*m)[]
, но не как int **m.
Прошу прощения, если это объяснение покажется немного запутанным.
An array can be seen as a pointer to an array of integers
. неправильный
Этот вопрос похож на: Что такое преобразование массива в указатель. разлагаться?. Если вы считаете, что это другое, отредактируйте вопрос, поясните, чем он отличается и/или как ответы на этот вопрос не помогают решить вашу проблему.
Совет: int m[n][n]
— это непрерывный блок целых чисел, имеющий один адрес. int *m[n]
— непрерывный блок указателей. Каждый элемент этого массива указывает на целое число. Местоположение этих целых чисел может быть разбросано где угодно в памяти. Нарисуйте и обозначьте картинки, полные коробочек и стрелок, и вы начнете понимать больше...
@Chris Вопрос совершенно ясен и, конечно, не требует подробностей отладки. Это также не совсем тот вопрос, который указан в вашем комментарии: Конечно, проблема, лежащая в основе путаницы, заключается в «распаде» массива, но здесь вопрос касается именно двумерных массивов. Кроме того, связанный вопрос касается C++, и, следовательно, принятый ответ также говорит о ссылках; Сторонники вычислительной правильности здесь всегда настаивают на том, что эти два языка очень разные и их не следует путать, перекрестно помечать, упоминать в одном предложении или даже думать о них вместе, так что нет: проголосовали за повторение.
int m[X][Y]
не может распадаться на int **
, поскольку m
не содержит указателей, на которые мог бы указывать int **
.
Массив T m[X]
превращается в указатель на свой первый элемент (&m[0]
) при использовании в качестве указателя. Поскольку элемент имеет тип T
, этот указатель, очевидно, будет иметь тип T *
.
m
T
, тип m[0]
T *
, тип &m[0]
int m[X]
int
int *
int m[X][Y]
int[Y]
int (*)[Y]
Вот как вы можете передать 2d-массив (так сказать) в функцию:
void f( size_t x, size_t y, int (*m)[y] ) {
// ...
}
int m[X][Y];
f( X, Y, m );
Вероятно, это сбивает с толку ОП, поскольку это исключение из правила C, согласно которому объявление имитирует использование, вызванное (повторяющимся!) Преобразованием типа из массива в указатель.
Там может быть такое исключение, @Peter-ReinstateMonica, но я не уверен, что именно вы под этим подразумеваете.
Путаница является нормальной (даже распространенной), поскольку указатели и массивы вполне взаимозаменяемы (хотя, очевидно, не совсем так). Было бы неудивительно, если бы int m[X][Y]
распалось на int **
. На мой взгляд, единственное, что сдерживает это — необходимость выделения указателей Y
. (Здесь есть две проблемы: 1. Не в каждой системе есть динамическое распределение памяти, и 2. Их невозможно освободить после выделения.)
int = 0, *pi = &i, **ppi = &pi, ai2d[1][1];
использование pi и ppi отражает их декларацию: *pi = 0; **ppi = 0;
. Это универсальная функция объявлений C, полезная для анализа типов указателей функций и т. д. Обычно использование ai2d тоже подходит: ai2d[0][0] = 0;
Но также возможно (правильно) написать **ai2d = 0;
из-за распада массива. Конечно, это справедливо уже для 1D-массивов: после int ai[1];
возможны как обычные ai[0]=0
, так и «неправильные» *ai = 0;
. Двойное затухание в 2D-случайе еще больше сбивает с толку, возможно, еще и из-за запутанных массивов указателей и т. д.
Прочтите это: Правильное размещение многомерных массивов. TLDR: указатели — это переменные, которые содержат адрес, который может быть действительным, а может и не быть. Массивы — это области памяти, имеющие адрес. Это не одно и то же.