Индекс массива разыменования указателя - PullRequest
0 голосов
/ 08 мая 2020

Имея это:

#include <stdio.h>
#include <stdlib.h>

struct Test { char c; } foo;

int main (void) {

   struct Test **ar;
   ar=malloc(16);
   *(ar+1) = &foo;
   ar[1]->c = 'c'; //this work
  (*(*ar+1)).c = 'c'; //this does't work

   return 0;
}
        //(**(ar+1)).c='c'; --> first case

Почему вышеупомянутое работает только вариант с вводом в массив, а не разыменование указателя?

struct Test { char c; } foo;

int main (void) {

   struct Test **ar;
   ar=malloc(16);
   *ar=malloc(0);
   *(ar+1) = &foo;
   //(**(ar+1)).c='c';
   (*(*ar+1)).c='c'; // NOW IT WORKS --> second case
   printf("%c\n", (*(*ar+1)).c); //prints 'c'

   return 0;
}

Теперь даже выделено 0 байтов, это не имеет значения, так как я просто нужен адрес, предоставленный ОС, чтобы инициализировать первый элемент

вопрос: как работает арифметика указателя c в обоих случаях? Насколько я понимаю их:

1) сначала Чтобы получить lvalue struct Test, указатель переходит непосредственно с указанного адреса ar на lvalue **ar - sizeof(struct Test**)

2) во втором случае указатель инициализировал первый член ar[0], поэтому он начинается здесь *ar и переходит к lvalue на *ar - sizeof(struct Test*).

Но оба указателя имеют одинаковый размер sizeof(struct Test**) == sizeof(struct Test*), и поэтому не должно быть разницы в арифметических c, иначе мне что-то не хватает?

Ответы [ 3 ]

2 голосов
/ 08 мая 2020
struct Test **ar;
ar=malloc(16);
...
(*(*ar+1)).c = 'c'; //this does't work

Конечно, есть. Как отмечалось в моем комментарии, * имеет более высокий приоритет, чем + C Приоритет оператора . Так что же происходит в (*(*ar+1)).c? Посмотрите на:

(*ar+1)

, что эквивалентно:

(ar[0] + 1)

Поскольку тип для ar - это указатель-указатель- to struct Test, *ar или ar[0] - это тип указатель на struct Test. Затем вы добавляете + 1, что добавляет sizeof (struct Test*) к первому указателю ar, что вам и нужно.

Почему это работает? Приоритет операторов:

   *ar              /* dereference ar** leaving pointer to struct Test */

  (*ar + 1)         /* advance to next pointer - applied before next dereference */

 *(*ar + 1)         /* dereference again leaving struct Test assigned to 2nd pointer */

(*(*ar + 1)).c      /* reference member 'c' of above */

Читаемость критична при игре с несколькими уровнями косвенности. Очень поможет использование индексной нотации. Вместо (*(*ar + 1)).c = 'c'; гораздо проще написать:

(*ar)[1].c = 'c';

Это ясно показывает, что вы сначала разыменовываете ar перед применением смещения 1 и повторным разыменованием ([..] предоставляет разыменование, как это делает '*'), чтобы достичь 2-го из выделенных вами poitners.

1 голос
/ 08 мая 2020

Для начала вам следует правильно указать размер выделяемой памяти

   ar = malloc( 2 * sizeof( struct Test * ) );

Этот оператор

*(ar+1) = &foo;

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

Это то же самое, что

ar[1] = &foo;

Это выражение

*ar

, которое эквивалентно выражению

ar[0]

дает первый элемент выделенного массива указателей. Он не был инициализирован. В результате это выражение

*ar+1

или

ar[0] + 1

вызывает неопределенное поведение (добавление 1 к чему-то, что не было инициализировано и имеет неопределенное значение).

Кажется вы имеете в виду

(**(ar+1)).c = 'c';

То есть выражение

*( ar + 1 )

дает второй элемент выделяемого динамически массива указателей. Разыменовав его, вы получите адрес объекта foo. При разыменовании во второй раз вы получите lvalue самого объекта foo.

Обратите внимание, что выражение

ar[1]

эквивалентно

*( ar + 1 )

и, как вы можете см. из этого допустимого оператора

ar[1]->c = 'c'

приведенное выше выражение дает указатель. Поэтому вам нужно разыменовать его, если вы хотите использовать оператор.

**( ar + 1 )
0 голосов
/ 08 мая 2020

Вы хотите разыменовать, чтобы получить ar[i], сделайте так:

(*(ar+1))->c = 'c';
...