Странное поведение с пустыми указателями

Я хочу создать функцию, которая получает пустой указатель. Этот указатель указывает на произвольные пользовательские данные. Это сейчас не актуально. Важнее дескриптор, описывающий эти пользовательские данные. Он находится под этими данными. Я хочу получить его адрес, есть два разных решения:

struct data_desc {
     size_t size;
     data_type_t type;
     /* And so on... */
};

/* Operate on the descriptor */
void operate_on_data(void *ptr)
{
    struct data_desc *desc;
    /* Now I want to get the desc /*

    /* This is the first approach, simply fails */
    desc = ((struct data_desc *)ptr) - sizeof(struct data_desc);
    /* This is the second, it works...*/
    desc = (struct data_desc *)ptr;
    desc--; 
    /* Do something with desc */
}

Как видите, я использую два разных подхода для получения адреса дескриптора. Я думал, что первое более естественно, но оно просто не работает. Я использовал больше круглых скобок, чтобы избежать проблем с приоритетом.

Я знаю, что эти методы небезопасны. Первый вариант не работает, почему? В чем причина такого поведения?

Заранее спасибо!


person Ákos Kovács    schedule 23.04.2012    source источник
comment
Не могли бы вы прояснить некоторые вещи? На что указывает ptr? тип или размер, или что?   -  person Jesus Ramos    schedule 23.04.2012
comment
Это будет распределитель памяти, ptr передается пользователю, так что это могут быть любые пользовательские данные. Чтобы организовать эти фрагменты, дескриптор хранит некоторую информацию об этом, размере и указателях предыдущего и второго фрагментов. Когда приходит функция free(), она получает только PTR, поэтому она должна каким-то образом получить информацию. Самый простой способ сделать это — прикрепить его к пользовательским данным.   -  person Ákos Kovács    schedule 23.04.2012


Ответы (3)


Проблема в арифметике указателей. Когда вы берете указатель и вычитаете 1, вы действительно вычитаете (1*sizeof(struct)). В первом уравнении с "-sizeof" вы действительно вычитаете (sizeof(struct) * sizeof(struct)) байта из указателя. Есть смысл?

person thatshowthe    schedule 23.04.2012

Когда вы определяете data_desc как указатель на struct data_desc, компилятор знает, что нужно добавить или вычесть sizeof(struct data_desc) при увеличении или уменьшении указателя на единицу. Другими словами, оно переводит «один» в «единая структура».

Код обычно пишут следующим образом:

struct data_desc *desc = (struct data_desc *) ptr;

Затем вы можете манипулировать им следующим образом:

size_t sz0 = desc->size;  // size of 0th element
size_t sz1 = desc[1].size;  // size of 1st element
size_t sz2 = (desc + 2)->size; // size of 2nd element (slightly awkward)
desc++;  // Increment to next structure

Вернемся к исходному коду: вы могли написать desc = ((struct data_desc *)ptr) - 1;, но большинство программистов предпочли бы инициализировать desc, а затем использовать его напрямую.

person Adam Liss    schedule 23.04.2012
comment
Ты прав. Первый случай также сделать это. Вычитание из ptr похоже на уменьшение desc на единицу. Я думаю, что проблема заключается в размерах шрифта. ИМХО, он должен работать с указателем char, потому что он имеет ширину всего один байт, sizeof (struct desc_data) также находится в байтах. - person Ákos Kovács; 23.04.2012
comment
Я не совсем уверен, что вы имеете в виду. Если вы говорите, что можете определить char *desc, а затем использовать исходный расчет, вы правы... но тогда вы не можете разыменовать desc, потому что он указывает на структуру char, а не на структуру data_desc. - person Adam Liss; 23.04.2012

desc = ((struct data_desc *)ptr) - sizeof(struct data_desc);

Вы преобразовали ptr в struct data_desc *. Теперь все арифметические действия, которые компилятор выполняет с этим указателем, выполняются порциями по размеру типа, на который он указывает.

Итак, если вы вычтете sizeof(struct data_desc) (скажем, struct data_desc имеет размер 8 байт), ptr будет указывать на место, которое может поместиться 8 struct data_desc между собой и ptr.

Предполагая, что каждый loc ниже может содержать один struct data_desc

-------------------------------------------------------------
|     |     |     |     |     |     |     |     |     |     |
| loc | loc | loc | loc | loc | loc | loc | loc | loc | ptr | 
|  9  |  8  |  7  |  6  |  5  |  4  |  3  |  2  |  1  |     |
-------------------------------------------------------------
      ^                                               ^
      |                                               |
     desc                                            ptr 
     location                                        location
     after
     subtraction           
person Praetorian    schedule 23.04.2012