Сделать указатель на то, на что указывает другой указатель в C - PullRequest
2 голосов
/ 06 мая 2011

Я не могу описать мою проблему в заголовке, но в основном мне нужна помощь с арифметикой указателей.Допустим, у вас есть структура, подобная следующей.Затем вы получите указатель на длинную строку в памяти.Допустим, указатель * p .поэтому я бы сделал p + = sizeof (void *) , чтобы получить p в месте следующего указателя.Теперь, как мне на самом деле сделать, чтобы точка p указывала на то, на что указывает следующая точка, а не просто указывала, где находится следующий указатель в памяти?

struct freeblock {
  long s; 
  // <--- THE POINTER WOULD BE POINTING HERE
  struct freeblock *prev;
  struct freeblock *next;
} 

Ответы [ 6 ]

2 голосов
/ 06 мая 2011

Это может быть сложным вопросом. Вы действительно не хотите делать арифметику указателей в своих структурах. Причина: Struct Alignment . Что происходит, если у вас есть такая структура:

struct a{
  char x; //address 0x8
  //potentially 3 bytes lost here:
  int y; //address 0x12
}

у не собирается начинать один байт после х. Чтобы сохранить выравнивание, поэтому y начинает, скажем, с адреса, кратного 4, после x будет добавлено несколько байтов заполнения.

Если у вас есть адрес экземпляра структуры 0x08 и вы добавили размер символа, и, получив (0x09), вы не получите начало y 0x12, вы получите некоторые данные мусора, которые являются частью мусора и частью y.

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

freeblock* p = s.prev;
p += sizeof(p);
freeblock next = *p;

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

2 голосов
/ 06 мая 2011

Попробуйте это:

void *p; /* points to prev now */
struct freeblock *p1;

p = ((char *)p)+sizeof(struct freeblock *); /* advance p to next */
p1 = *(struct freeblock **)p; /* take whatever is in next */

p1 указывает на то же место, что и следующее.

Небольшое объяснение - указатели набираются на C. Добавление 1 к указателю означает добавление sizeof (типа) к фактическому адресу, поэтому вам нужно привести указатель к нужному типу или к char *, если вы хотите использовать арифметику вручную, так как sizeof (char) равен 1.

1 голос
/ 07 мая 2011

Предположим, что вам известно только текущее значение p и оно указывает на поле prev экземпляра struct freeblock. Между тем, помните, что адрес «после длинного» может не совпадать с адресом поля prev, например, если sizeof(long)==4 и указатели выровнены по 8 байтов (что верно для некоторых 64-битных платформ ); см. ответ Пола Рубеля для объяснения.

Теперь вам нужно получить значение next в том же экземпляре и разыменовать его. Стандартно-совместимое решение должно включать offsetof() макрос :

void * p;
...
size_t delta = offsetof(struct freeblock,next)-offsetof(struct freeblock,prev);
p = (void*) *(struct freeblock**)((char*)p+delta);

Итак, вы сначала вычисляете разницу между адресами полей prev и next с помощью offsetof(), затем приводите p к указателю на символ, чтобы правильно добавить дельту и получить адрес next, затем приведите его к указателю на указатель на свободный блок, разыменуйте этот адрес, чтобы получить желаемое значение, и, наконец, присвойте значение p с приведением к указателю на пустоту (который можно опустить , но я бы оставил его в рабочем коде).

0 голосов
/ 06 мая 2011

Вы говорите, что «p находится в месте расположения указателя next», и вы хотите «заставить p указать, на что next указывает»:

Я собираюсь предположить, что p это char*:

p = (char*) *((struct freeblock**) p);

Краткое объяснение:

Поскольку p указывает на next, p «действует» как указатель на struct freeblock* или struct freeblock**. Чтобы получить значение того, на что указывает struct freeblock**, вы просто разыменовываете его (в результате получается другой указатель, но неправильного типа). Приведите этот результирующий указатель к правильному типу для p, и все готово.

0 голосов
/ 06 мая 2011

Указатель должен указывать на переменную определенного типа, вы не можете выполнять арифметические действия с void*.

Но если предположить, что это некоторый допустимый тип данных, то p++ или p += 1 выполнят эту работу, обратите внимание, что опасно начинать приводить указатель на разные типы.

struct a{
  long s; 
  int d;// <--- THE p POINTER WOULD BE POINTING HERE
  struct freeblock *prev;
  struct freeblock *next;
} 

р + = 1;// так же, как d + = 1 * sizeof (int);

struct a{
  long s; 
  int d;
  struct freeblock *prev; // <--- now p points here
  struct freeblock *next;
} 
0 голосов
/ 06 мая 2011

Может быть, вы пытаетесь разыменовать p, как это:

 char *p;
 struct freeblock *x;
// you got the value for p here somehow.
 x = *((struct freeblock **)p);
//  now you can access the fields:
 if (x->next != 0)
 { //...
 }

Обратите внимание, что я решил поддержать свое понимание того, что тип х отличается от типа р. Вы можете объявить x и p как void *, но вам нужно привести x к '((struct freeblock *) x)', прежде чем вы сможете добраться до полей.

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

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