2-D Массивы и указатели - PullRequest
       28

2-D Массивы и указатели

1 голос
/ 19 апреля 2019

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

int main()
{
    int a[2][3] = {{1,2,3}, {5,6,7}}, (*p)[3];
    p = a[0];
    for(int i = 0; i < 3; i++)
        printf("%d", (*p)[i]);
}

Вывод был 123 с кодовыми блоками.

Я пытался решить это как:

(*p)[i]) должно совпадать с *((*p)+i) .... (1)

Как говорилось в правиле, «Имя массива преобразуется в указатель на 1-й элемент массива». Я преобразовал 2-ю инструкцию p=a[0] в p=&a[0][0] (поскольку a[0] является подэлементом имя массива, чей 1-й элемент a[0][0]). Так что (*p) можно записать как (*(&a[0][0])). Который такой же как a[0][0]. Таким образом, уравнение (1) становится

*(a[0][0]+i). 

И вот где я застрял. a[0][0]+i это элемент, а не адрес. Поэтому применение оператора * к нему недопустимо. Но я также заменил утверждение p=a[0]; на p=&a[0][0]; в исходном коде, ответ остается тем же. Также я заменил его на p=&a[0];. Ответ остается прежним. А теперь я в замешательстве.

Ответы [ 2 ]

0 голосов
/ 19 апреля 2019

Эта строка неверна:

p = a[0];

Тип p является указателем на массив 3 int.Тип a[0] является указателем на int.Ваш компилятор должен был предупредить вас об этом.Если это не так, включите все необходимые параметры, чтобы включить большинство или все предупреждения вашего компилятора.Когда компилятор выдает предупреждение, не игнорируйте его - убедитесь, что вы понимаете, почему компилятор выдает предупреждение, прежде чем продолжить.Было бы неплохо включить функцию компилятора, которая превращает предупреждения в ошибки.

Правильный оператор будет p = a; или p = &a[0];.Но давайте рассмотрим, что произошло в вашей программе.В выражении a[0]:

  • По определению подстрочного оператора a[0] эквивалентно *(a+0) (технически (*((a)+(0))), но в этом случае нам не нужны дополнительные скобки).
  • a - это массив, поэтому он преобразуется в указатель на свой первый элемент.Таким образом, a преобразуется в указатель на a[0].Таким образом, выражение теперь *(&a[0] + 0).
  • Добавление 0 к &a[0] не меняет его, поэтому результат равен &a[0], а выражение теперь *&a[0].
  • a[0] - это массив из 3 int.Поскольку это массив, он преобразуется в указатель на свой первый элемент.Таким образом, результат фактически равен &a[0][0].

Обратите внимание, что &a[0][0] является указателем на int, как я писал выше.Итак, теперь мы видим, что p = a[0]; имеет указатель на int справа, но указатель на массив 3 int слева.Это нарушает ограничение в стандарте C, и компилятор должен выдать ему диагностическое сообщение.

После предупреждения ваш компилятор, вероятно, сделал преобразование указателя, в результате чего p устанавливается в значение a[0].

Я преобразовал 2-ю инструкцию p=a[0] в p=&a[0][0] (, потому что a[0] - это имя подмассива, чей 1-й элемент - a[0][0]).Так что (*p) можно записать как (*(&a[0][0])).

Это ошибка.Хотя вы выполнили присваивание p = a[0], это не присвоило &a[0][0] p, и p не ведет себя так, как если бы оно было &a[0][0].Назначение преобразуется &a[0][0] в тип p.p остается типом, который был объявлен, указателем на массив 3 int.По сути, p заканчивается значением &a[0].

Теперь мы можем оценить (*p)[i]:

  • p равно &a[0], поэтому выражениестановится (*&a[0])[i]).
  • *&a[0] равно a[0], поэтому выражение становится a[0][i].
  • Оттуда мы можем приступить к обычной оценке выражения, и ясно, чторезультат - элемент i элемента 0 из a.
0 голосов
/ 19 апреля 2019

2D-массивы: Массив 1D-массивов

В 1-мерных массивах

a[0] => *(a)     -> first element of array
a[1] => *(a + 1) -> second element of array

в 2-мерных массивах,

a => the adress of first array
a[0] => *(a)        -> the first array 
a[0][0] => *(*(a))     -> the first element of first array
a[0][1] => *(*(a) + 1) -> second element of first array
a[1][1] => *(*(a + 1) + 1) -> second element of second array
...