Reputation: 571
I want to create a function which gets a void pointer. This pointer points to an arbitrary user data. This is not relevant now. Which is more important a descriptor, which describe this user data. It is under this data. I want to get it's address, there are two different solution:
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 */
}
As you can see I use two diffrent approach to get the descriptor's address. I thought the first is more natural, but it's just not works. I used more parentheses to avoid the precedence problems.
I know that these methods are not safe. The first does not work, why? What is the reason behind this behavior?
Thanks in advance!
Upvotes: 1
Views: 165
Reputation: 109119
desc = ((struct data_desc *)ptr) - sizeof(struct data_desc);
You've typecast ptr
to a struct data_desc *
. Now, all arithmetic that the compiler does on this pointer is in chunks the size of the type it points to.
So, if you subtract sizeof(struct data_desc)
(let's say struct data_desc
is 8 bytes in size), ptr
will point to a location that can fit 8 struct data_desc
in between itself and ptr
.
Assuming each loc
below can hold one 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
Upvotes: 1
Reputation: 48290
When you define data_desc
as a pointer to a struct data_desc
, the compiler knows to add or subtract sizeof(struct data_desc)
when you increment or decrement the pointer by one. In other words, it translates "one" into "one structure."
It's common to write code like this:
struct data_desc *desc = (struct data_desc *) ptr;
Then you can manipulate it like this:
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
Back to your original code: you could write desc = ((struct data_desc *)ptr) - 1;
, but most programmers would prefer to initialize desc
and then use it directly.
Upvotes: 1
Reputation: 355
The problem is pointer arithmetic. When you take a pointer and subtract 1, you are really subtracting (1*sizeof(struct))
. The first equation with the "-sizeof", you are really subtracting (sizeof(struct) * sizeof(struct))
bytes from the pointer. Make sense?
Upvotes: 2