Разница между ** и * в трехмерном массиве - PullRequest
0 голосов
/ 03 июля 2019

Оба кода, показанные ниже, дают одинаковые выходные данные, т.е. для ** a и * a (в printf). Почему это так?

Я не очень хорошо разбираюсь в многомерных массивах, я пробовал разные блоги, чтобы понять, но все же не смог. Я не понимаю, как вывод одинаков для обоих случаев. Заранее спасибо.

ВЫХОД ДЛЯ ОБА СЛУЧАЙ ЕСТЬ ABCDEFGHIJKLM

// CODE 1
int main() 
{

  char a[2][3][3] = {'a','b','c','d','e','f','g', 
                    'h','i','j','k','l','m'};
  printf("%s ", **a); 
  return 0; 
} 

/// код 2

int main () {

  char a[2][3][3] = {'a','b','c','d','e','f','g', 
                    'h','i','j','k','l','m'};
  printf("%s ", *a); 
  return 0; 
} 

1 Ответ

0 голосов
/ 04 июля 2019

Это весело.

Помните, что элементы массива располагаются непрерывно - a выглядит примерно так в памяти:

   +---+
a: |'a'| a[0][0][0]
   +---+ 
   |'b'| a[0][0][1]
   +---+
   |'c'| a[0][0][2]
   +---+
   |'d'| a[0][1][0]
   +---+
   |'e'| a[0][1][1]
   +---+
   |'f'| a[0][1][2]
   +---+
   |'g'| a[0][2][0]
   +---+
   |'i'| a[0][2][1]
   +---+
   |'j'| a[0][2][2]
   +---+
   |'k'| a[1][0][0]
   +---+
   |'l'| a[1][0][1]
   +---+
   |'m'| a[1][0][2]
   +---+
   | 0 | a[1][1][0]
   +---+
   | 0 | a[1][1][1]
   +---+
    ...

Поскольку вы предоставили меньше инициализаторов, чем размер массива для хранения (12 против18), остальные элементы инициализируются в 0 (это важно позже).

Во-первых, некоторый фон для массивов:

Если это не операнд sizeof или унарный & операторов или строковый литерал, используемый для инициализации массива символов в объявлении, выражение типа "массив N-элементов из T" будет преобразовано ("распад") в выражениетипа "указатель на T", а значением выражения будет адрес первого элемента массива.

Операция подстановки массива a[i] имеет значение , определенное как *(a + i) - с учетом начального адреса a, смещения i элементов (не байтов!) От этого адреса и разыменования результата.Это означает, что *a == *(a + 0) == a[0].

Итак, как это применимо к вашему коду?

Выражение a имеет тип "2-элементный массив из 3-массив элементов трехэлементного массива char ".Если a не является операндом операторов sizeof или унарных &, это выражение «затухает», чтобы напечатать «указатель на массив из 3 элементов из массива из 3 элементов char», а также значениеВыражение - это адрес первого элемента массива - &a[0].

Выражение *a разыменовывает этот указатель, и типом выражения является "массив из 3 элементов из массива из 3 элементов char".Опять же, это выражение не является операндом sizeof или унарным & операндам, поэтому оно преобразуется в «указатель на массив из 3 элементов из char», а значением является адрес первого элементамассив или &(*a).Начиная с *a == a[0], это также можно рассматривать как &a[0][0].

Выражение **a разыменовывает результат *a (у которого был тип "указатель на массив из 3 элементов char")тип выражения - «3-элементный массив из char».Опять же, это выражение не является операндом операторов sizeof или унарных &, поэтому оно «затухает» при вводе «указателя на char».

Что, по совпадению, это то, что %s ожидает.Спецификатор преобразования %s ожидает, что его аргумент будет иметь тип char * и будет указывать на первый в последовательности символов, за которой следует 0-значный терминатор.Помните ранее, когда я говорил, что остальные 6 элементов массива будут инициализированы в 0?Возможно, не имея значения, вы сохранили строку в a.

Итак, вот почему вы получаете вывод, который вы делаете с **a.Но почему *a также работает?

Адрес массива совпадает с адресом первого элемента массива - &a[0] == &a[0][0] == &a[0][0][0] (что вы можете видеть из рисунка ASCII выше). тип из *a неправильный - char (*)[3] вместо char * - так что, строго говоря, поведение не определено, но его значение (по крайней мере, его логическое значение) являетсятакой же как **a.Разные типы указателей могут иметь разные представления, поэтому может существовать платформа, на которой представление char (*)[3] может отличаться от char *, так что этот код не будет работать.Однако на платформах, таких как x86, все типы указателей имеют одинаковое представление, поэтому значение *a интерпретируется так же, как и значение **a.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...