array arithmeti c - индексация типов указателей - PullRequest
1 голос
/ 08 мая 2020

В моем предыдущем вопросе: Индекс массива разыменования указателя Я спросил о разыменовании структуры. (Я вставлю оттуда фрагмент кода для повторения):

#include <stdio.h>
#include <stdlib.h>
struct Test { char c; } foo;

int main (void) {

   struct Test **ar;
   ar=malloc(16);
   *ar=malloc(0); //prerequisite for second case (without getting some address from OS, I cannot go 'through' *ar to (*ar+1). 
   //Does not matter allocation of zero bytes. (only to get some valid address)
   *(ar+1) = &foo;
   //(**(ar+1)).c='c'; //// first case - works
   (*(*ar+1)).c='c'; //// second case - also works, with prerequisite
   printf("%c\n", (*(*ar+1)).c); //prints 'c'

   return 0;
}

Я все еще понимаю между добавлением указателя +1 в первом и втором случае. Хорошо, я делаю во втором - добавляю sizeof(struct Test*) к адресу *ar, что похоже на индексацию массива (так что *ar - это указатель имени массива). Но в первом случае? что делает (**(ar+1))? Как я могу добавить (что?) Какой-то тип указателя sizeof(struct Test**), когда ar не является массивом? *(ar+1) адрес разыменования, который мне не принадлежит, но (* ar + 1) адрес разыменования указателя (sizeof(struct Test*)), который ДЕЙСТВИТЕЛЬНО принадлежит мне (член массива). Так почему же работает первый случай ? (из ссылки я пытаюсь дать свое понимание тип разрешения индексируется [например - в первом случае «шаг / индекс» выполняется sizeof(struct Test**), а во втором - sizeof(struct Test*), но оба имеют одинаковый размер ) - просто посмотрите ссылку.

Ответы [ 2 ]

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

Так почему же работает первый случай?

(**(ar+1)).c='c' вообще не работает в моей системе c.

(**(ar+1)) выполняет арифметику указателя c для типа struct Test ** и (*(*ar+1)) выполняет арифметику c указателя на типе struct Test*.

Это означает, что в первом случае арифметика c выполняется sizeof(struct Test *) bytes, во втором случае sizeof(struct Test) байтами.

Компилятор может добавить отступы внутри вашей структуры, чтобы в итоге получилось 4 байта и c, независимо от размера указателя в вашей системе. Так что по счастливой случайности они могут оказаться по одному и тому же адресу. Размер указателя обычно составляет 2, 4 или 8 байтов, в зависимости от того, используется ли адресная шина 16, 32 или 64.

Не имеет смысла размышлять о том, что делает такой непонятный код, как этот. Явной арифметики указателя c в целом следует избегать, гораздо лучше использовать оператор [] для получения читаемого кода.

Также обратите внимание, что malloc(0) дает «либо возвращается нулевой указатель. , или поведение такое, как если бы размер был неким ненулевым значением, за исключением того, что возвращенный указатель не должен использоваться для доступа к объекту ". Если вы получите нулевой указатель, а затем попытаетесь арифметизировать c, у вас будет неопределенное поведение и может произойти что угодно.

0 голосов
/ 08 мая 2020

Итак, давайте разберем ваш код:

Объявление указателя на указатель на struct Test.

struct Test **ar; 

Выделение места для указатели, если ваша система 64-битная, вы выделяете пространство ровно для двух смежных указателей. ), это никогда не используется, но без оптимизации компилятора вам все равно нужно его инициализировать, выделение 0 байтов может быть не лучшим вариантом, поскольку оно вызывает неопределенное поведение, но поскольку вы никогда ничего не храните там, это не вызывает проблем.

*ar = malloc(0); 

Вы храните foo адрес в указателе номер 2, который, поскольку он работает, заставляет меня думать, что ваша система действительно 64-битная.

*(ar+1) = &foo;

Работает, присваивая 'c' char c одному за структурой foo. То же, что и ar[0][1].c ='c';

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

printf("%c\n", (*(*ar+1)).c); //prints 'c'

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

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

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

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

struct Test
{
  char c;
} foo;

int main(void)
{
  struct Test **ar; //declaring a pointer to pointer to struct Test
  ar = malloc(sizeof(*ar) * 2); //allocationg space for 2 pointers to struct Test.

  //without optimization you still need to allocate space 
  //or otherwise initialize the 1st pointer to avoid UB
  *(ar + 0) = malloc(sizeof(**ar)); //or ar[0] = ... or *ar = ...
  *(ar + 1) = &foo; //or ar[1] = ... storing foo's address in the second pointer

  (*(*ar + 1)).c = 'c'; //works fine, one past the allocated memory
  printf("%c\n", ar[0][1].c); 

  (**(ar + 1)).c = 'b'; //works, actually foo
  printf("%c\n", ar[1][0].c); 

  (*(*(ar + 1) + 1)).c = 'a'; //also works, accessing ou of bounds
  printf("%c\n", ar[1][1].c); 

  printf("%c\n", foo.c); //test print foo

  return 0;
}

Живая демонстрация

Это намного лучше не только с точки зрения удобочитаемости, но также и с точки зрения переносимости, поскольку именно система решает, какой размер указателей.

Теперь посмотрите на этот упрощенный код:

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

struct Test
{
    char c;
} foo;

int main(void)
{
    struct Test** ar;
    ar = malloc(sizeof(*ar) * 2);

    ar[0] = malloc(sizeof(**ar)); //or *ar = ...
    ar[1] = &foo;                 //or *(ar + 1) = ...

    ar[0]->c = 'a'; 
    printf("%c\n", ar[0]->c);

    ar[1]->c = 'c'; 
    printf("%c\n", ar[1]->c);

    printf("%c\n", foo.c); //ok foo has 'c'

    return 0;
}

Посмотрите, как легко используйте двойные указатели, если не усложняете вещи.

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