Почему int **m не равно int m[][]?

Я хотел бы прояснить концепцию, которую я не смог полностью понять.

Почему в 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.

Прошу прощения, если это объяснение покажется немного запутанным.

Прочтите это: Правильное размещение многомерных массивов. TLDR: указатели — это переменные, которые содержат адрес, который может быть действительным, а может и не быть. Массивы — это области памяти, имеющие адрес. Это не одно и то же.

Andrew Henle 27.07.2024 21:01
An array can be seen as a pointer to an array of integers. неправильный
Raildex 27.07.2024 21:02

Этот вопрос похож на: Что такое преобразование массива в указатель. разлагаться?. Если вы считаете, что это другое, отредактируйте вопрос, поясните, чем он отличается и/или как ответы на этот вопрос не помогают решить вашу проблему.

Chris 27.07.2024 21:08

Совет: int m[n][n] — это непрерывный блок целых чисел, имеющий один адрес. int *m[n] — непрерывный блок указателей. Каждый элемент этого массива указывает на целое число. Местоположение этих целых чисел может быть разбросано где угодно в памяти. Нарисуйте и обозначьте картинки, полные коробочек и стрелок, и вы начнете понимать больше...

Fe2O3 28.07.2024 03:16

@Chris Вопрос совершенно ясен и, конечно, не требует подробностей отладки. Это также не совсем тот вопрос, который указан в вашем комментарии: Конечно, проблема, лежащая в основе путаницы, заключается в «распаде» массива, но здесь вопрос касается именно двумерных массивов. Кроме того, связанный вопрос касается C++, и, следовательно, принятый ответ также говорит о ссылках; Сторонники вычислительной правильности здесь всегда настаивают на том, что эти два языка очень разные и их не следует путать, перекрестно помечать, упоминать в одном предложении или даже думать о них вместе, так что нет: проголосовали за повторение.

Peter - Reinstate Monica 29.07.2024 18:38
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
5
121
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

int m[X][Y] не может распадаться на int **, поскольку m не содержит указателей, на которые мог бы указывать int **.

Массив T m[X] превращается в указатель на свой первый элемент (&m[0]) при использовании в качестве указателя. Поскольку элемент имеет тип T, этот указатель, очевидно, будет иметь тип T *.

Пример mT, тип m[0]T *, тип &m[0]int m[X]intint *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 - Reinstate Monica 27.07.2024 22:14

Там может быть такое исключение, @Peter-ReinstateMonica, но я не уверен, что именно вы под этим подразумеваете.

John Bollinger 29.07.2024 18:25

Путаница является нормальной (даже распространенной), поскольку указатели и массивы вполне взаимозаменяемы (хотя, очевидно, не совсем так). Было бы неудивительно, если бы int m[X][Y] распалось на int **. На мой взгляд, единственное, что сдерживает это — необходимость выделения указателей Y. (Здесь есть две проблемы: 1. Не в каждой системе есть динамическое распределение памяти, и 2. Их невозможно освободить после выделения.)

ikegami 29.07.2024 18:31
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-случайе еще больше сбивает с толку, возможно, еще и из-за запутанных массивов указателей и т. д.
Peter - Reinstate Monica 29.07.2024 18:47

Другие вопросы по теме