Ákos Kovács
Ákos Kovács

Reputation: 571

Strange behaviour with void pointers

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

Answers (3)

Praetorian
Praetorian

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

Adam Liss
Adam Liss

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

thatshowthe
thatshowthe

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

Related Questions