2-й массив: почему ptr и *ptr одинаковы, когда я объявил ptr как int ptr[4][4];?

Я тестировал некоторые коды, чтобы узнать, как 2d-массив реализован в c. Затем я столкнулся со следующей проблемой.

Код:

int main(){
   int a[4][4];
   printf("a: %p, *a: %p, **a: 0x%x\n",a,*a,**a);
}

Я скомпилировал это с помощью 32-битной Ubuntu gcc.

В результате:

a: 0xbf9d6fdc, *a: 0xbf9d6fdc, **a: 0x0

Я ожидал другого значения для a и *a, но они одинаковы.

  1. почему a и *a одинаковы в этом случае? Разве a не int** тип?

  2. Тогда какова роль *оператора в *a?

«Разве a не int** тип?» Ни в коем случае нет int ** в Любые этого. Где бы вы ни читали/выводили/вам говорили это, они ошибались.
WhozCraig 22.05.2019 07:26

Обратите внимание, что технически вы должны привести аргументы, соответствующие форматеру %p, к void*. В большинстве архитектур передача любого типа указателя будет иметь тот же эффект, но это не гарантируется.

aschepler 22.05.2019 08:05

Выполнение **a может вызвать неопределенное поведение, поскольку оно оценивается как a[0][0], которое не было инициализировано/установлено ранее.

alk 22.05.2019 08:21

@alk Педантично, адрес a занят, и его нельзя объявить с помощью квалификатора register. В основных системах без экзотических представлений ловушек это означает, что переменная принимает неопределенное значение, но это не UB. stackoverflow.com/a/40674888/584518

Lundin 22.05.2019 08:44
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
4
56
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Проверьте типы!!

Учитывая определение как int a[4][4];

  • a имеет тип int [4][4] - массив массива из 4 целых чисел. Это нет то же, что и int **.
  • a[n] имеет тип int [4] - массив из 4 целых чисел. Это нет то же, что и int *
  • a[n][m] относится к типу int. - целое число.

Теперь, учитывая тот факт, что адрес массива также является адресом первого элемента в массиве, значения одинаковы, но они различаются по типам.

Чтобы визуально проверить

int a[4][4]

      +-------+--------+-------+-----------+
      |       |        |       |           |
      |a[0][0]| a[0][1]| a[0][2]   a[0][3] |
      |       |        |       |           |
      +------------------------------------+
      |       |        |       |           |
      |a[1][0]|        |       |           |
      |       |        |       |           |
      +------------------------------------+
      |       |        |       |           |
      |a[2][0]|        |       |           |
      |       |        |       |           |
      +------------------------------------+
      |       |        |       |           |
      |       |        |       |           |
      |       |        |       |           |
      |       |        |       |           |
      +-------+--------+-------+-----------+

Затем, цитируя C11, §6.3.2.1

Except when it is the operand of the sizeof operator, the _Alignof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue. [...]

Таким образом, при передаче аргумента типа массива в качестве аргумента функции он распадается на указатель на первый элемент массива.

Итак, давайте посмотрим.

  • a, распадается на &(a[0]) - адрес первого элемента типа int (*)[4].
  • *a, который совпадает с a[0], распадается на int *, указатель на первый элемент.
  • **a что то же самое, что *(*a) == *(a[0]) == a[0][0] - это значение int по этому индексу.

Теперь еще раз внимательно посмотрите на изображение выше — видите ли вы, что первый элемент int [0] и int [0][0] в основном находится в одном и том же адрес? Другими словами, начальный адрес тот же.

Это причина вывода, который вы видите.

Я предположил a как тип указателя, поскольку [] оператор * работает одинаково для int** и int arr[][] в большинстве случаев. Спасибо за разрешение моего недоразумения.

DongRyeong 22.05.2019 07:50

@DongRyeong Я добавил некоторые уточнения, надеюсь, это поможет.

Sourav Ghosh 22.05.2019 07:52

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