Будет ли прототип a [1] [2] таким: int ** a? - PullRequest
3 голосов
/ 13 апреля 2011

a[1][2] расширяется компилятором следующим образом: *( *(a+1) +2 ). Так что если у a есть такой прототип: int **a,

Вышеупомянутое выражение должно быть объяснено так:

  1. Получить адрес a из таблицы символов. Обратите внимание, что это a указатель на указатель

  2. Теперь мы добавим его к 1, затем оно будет указывать на где-то рядом с где a указывают на.

  3. Тогда мы разыменовываем это. Я думаю, что здесь неопределенное поведение , поскольку мы не знаем, является ли a+1 действительным, и мы произвольно получаем к нему доступ.

  4. Хорошо, если нам повезет, что мы успешно получим значение *(a+1). Мы добавим это на 2.

  5. На этом шаге мы разыменовываем (*(a+1) +2 ). Удастся ли нам сейчас?

Я читал это в Программировании на Expert C в Глава 10 . Это правильно?

Ответы [ 2 ]

3 голосов
/ 13 апреля 2011

Новый ответ после отредактированного вопроса:

Чтобы a[1][2] был действительным, учитывая, что a имеет значение int **a;, оба значения должны быть истинными:

  • a должен указывать на первый из двух последовательных int * объектов;
  • Второй из этих int * объектов должен указывать на первый из трех последовательных int объектов.

Самый простой способ это устроить:

int x[3];
int *y[2] = { 0, x };
int **a = y;

Оригинальный ответ:

Если выражение a[1][2] является допустимым, тогда существует много различных возможностей для типа a (даже без учета таких квалификаторов, как const):

  1. type **a; (указатель на указатель на тип )
  2. type *a[n]; (массив из n указателей на тип )
  3. type (*a)[n]; (указатель на массив n типа )
  4. type a[m][n]; (массив из m массивов n типа )

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

Первый a + 1 рассчитывается. Если a сам является указателем (случай 1 или случай 3), то значение a загружается напрямую. Если a является массивом (случай 2 или случай 4), то загружается адрес первого элемента a (который идентичен адресу самого a).

Этот указатель теперь смещен на 1 объект того типа, на который он указывает. В случае 1 и 2 он будет смещен на 1 "указатель на объект type "; в случае 3 и 4 он будет смещен на 1 "массив объекта n type ", что аналогично установке n type объектов.

Расчетный (смещенный) указатель теперь разыменовывается. В случаях 1 и 2 результат имеет тип «указатель на тип », в случаях 3 и 4 результат имеет тип «массив n тип ».

Далее *(a + 1) + 2 рассчитывается. Как и в первом случае, если *(a + 1) является указателем, то значение используется напрямую (на этот раз, случаи 1 и 2). Если *(a + 1) является массивом (случаи 3 и 4), то берется адрес первого элемента этого массива.

Результирующий указатель (который на данный момент всегда имеет тип «указатель на тип ») теперь смещен на 2 типа объектов. Указатель окончательного смещения теперь разыменовывается, и получается объект type .

0 голосов
/ 13 апреля 2011

Допустим, определение a выглядит примерно так:

int a[2][2] = {{1, 2}, {3, 4}};

Вот как выглядит символ a:

[ 1 ][ 2 ][ 3 ][ 4 ]

В C, когда вы выполняете арифметику с указателем, фактическая величина, на которую значение указателя увеличивается или уменьшается, зависит от размера типа, хранящегося в массиве. Тип, содержащийся в первом измерении a, равен int[2], поэтому, когда мы просим C вычислить значение указателя (a + 1), он берет местоположение, названное a, и увеличивает его на размер * 1012. *, в результате чего указатель ссылается на ячейку памяти, содержащую целочисленное значение [3]. Так что да, когда вы разыменовываете этот указатель, а затем добавляете к нему 2, результатом является целочисленное значение 5. Когда вы затем пытаетесь разыменовать это целочисленное значение, это не имеет смысла.

Итак, давайте предположим, что массив содержит указатели:

char const * one = "one",
             two = "two",
             three = "three",
             four = "four";
char const * a[2][2] = {{one, two}, {three, four}};

Добавьте 1 к a, а затем разыменуйте его, и вы получите указатель на символ, ссылающийся на строку «три». Добавьте к этому два, и вы получите указатель на короткую строку «ree». Разыщите его, и вы получите значение char 'r', но только благодаря счастливой случайности вы избежали ошибки защиты памяти.

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