2016-08-06 3 views
9

Насколько я знаю, оператор устранения помех * возвращает значение, сохраненное в адресе указателя. То, что меня смущает, - это поведение, когда оператор используется с указателем массива. Например,Путаница относительно оператора разыменования ("*") в C

int a[4][2]; 

Затем a внутренне преобразуется в указатель первого элемента массива 4 элементов 2 целых чисел. Тогда какое значение делает *a? Я действительно смущен!

+0

Нет указателя на массив. Массив не является указателем. «Тогда' a' внутренне преобразован в указатель первого элемента массива из 4 элементов из 2-х целых чисел ». просто неправильно! – Olaf

+0

@Olaf Что вы имеете в виду, нет указателя на массив? Разве это не означает (int (* x) [4] [2]), что x является указателем на массив из 4 элементов из 2 ints? – Jin

+0

Где это появляется в вашем вопросе ?? Если вы прочтете комментарий до конца и поняли ответы, это должно быть ясно. 'a' - массив, а не указатель! И это само по себе «не превращается в указатель». Почему так сложно для новичков понять, что разные синтаксисы генерируют разные типы? – Olaf

ответ

5

Это:

int a[4][2]; 

определен a массив из 4 элементов, каждый из которых представляет собой массив из 2 int элементов. (2-мерный массив не больше или меньше массива массивов.)

Выражение массива в большинстве контекстов неявно преобразуется в указатель на исходный (нулевой) элемент объекта массива. (Обратите внимание на предположение о том, что существует массив объект, что вызвало некоторую тоску, но это не имеет значения здесь.)

случаев, когда выражение массива является не преобразуется в указатель является:

  • Когда это операнд sizeof;
  • Когда это операнд унарного &; и
  • Когда это строковый литерал в инициализаторе, используемом для инициализации объекта массива.

(Compiler специфические расширения, такие как ССЗ typeof может создать больше исключений.)

Таким образом, в выражении *a, подвыражение a (который имеет тип int[4][2]) неявно преобразуется в указатель типа int(*)[2] (указатель на массив из 2 int s).Применяем унарные * разыменования этого указателя, давая нам выражение типа int[2].

Но мы не довольно сделано еще. *a также является выражением типа массива, что означает, что в зависимости от того, как он используется, он будет , вероятно, снова преобразован в указатель, на этот раз типа int*.

Если записать sizeof *a, Подвыражение a преобразуется из int[4][2] в int(*)[2], но Подвыражение *a является не преобразуются из int[2] в int*, так что выражение дает размер типа int[2].

Если мы напишем **a, произойдет преобразование . *a имеет тип int[2], который преобразуется в int*; разыменование, которое дает выражение типа int.

Обратите внимание, что, несмотря на то, что мы можем законно сослаться на **a, используя две операции указателя разыменования, нет указателя объектов. a - объект массива, состоящий целиком из 8 int объектов. Неявные преобразования дают указатель значения.

правило преобразования Неявного массив в-указателя в N1570 разделе пункта 6.3.2.1 3. (Этот пункт неправильно дает _Alignof в качестве четвертого исключения, но _Alignof не может быть применена к выражению. Опубликованная стандарт C11 исправлена ​​ошибка .)

Рекомендуемое чтение: Раздел 6 раздела comp.lang.c FAQ.

+1

В предложении «Если мы пишем sizeof * a, преобразование не происходит» означает ли среднее значение преобразования из массива из 2 ints для указателя на начальный элемент массива из 2 ints? Также я думаю, что следующий абзац должен быть отредактирован как «* a - это тип int [2], который преобразован в int» – Jin

+1

'* a' не имеет типа' int (*) [2] '; Он имеет тип 'int [2]'. – 2501

+0

@ 2501: Совершенно верно. Я обновил ответ. –

9

Тип a является int[4][2], поэтому тип *a (или, что эквивалентно a[0]) является int[2].

Это не так же, как a[0][0]. Если вы сделаете это:

int a[4][2]; 
printf("%d\n",*a); 

компилятор скажет вам это:

предупреждение: формат '% d' рассчитывает ввести 'Int', но аргумент 2 имеет тип 'Int *'

Поскольку массив (в этом случае один из типов int [2]) передается функции, он распадается на указатель на первый элемент в этом контексте.

С другой стороны, у вас был **a, что эквивалентно a[0][0] и имеет тип int.

+0

Если вы смущены сообщением об ошибке, ссылающимся на 'int *', то его, поскольку выражения типа массива в любом контексте, отличном от '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' Таким образом, 'int [2]' становится 'int *' –

+0

@ChrisDodd: Что вы имеете в виду "связанные запросы типа"? Выражение массива преобразуется в указатель, если оно не является (a) операндом унарных '&', (b) операндом 'sizeof' или (c) строковым литералом в инициализаторе, используемом для инициализации массива (sub) объект. Это единственные исключения. (В проекте N1570 C11 неправильно указан четвертый, но оператор '_Alignof' не может быть применен к выражению.) –

+0

@KeithThompson:' decltype', 'typeof' и' alignof', которые существуют в различных расширениях, а также '_Generic'. Также возможно 'offsetof', хотя в этом случае преобразование в указатель не будет иметь эффекта. –

Смежные вопросы