Reputation: 672
I have a void*
in plain C that I'm using while walking through some unstructured data, and I'd like to cast with dereference and autoincrement as I go. I would like to write the following:
void* ptr = /*something*/;
uint16_t i = *((uint16_t*) ptr)++;
The compiler objects and tells me "lvalue required as increment operand", but I guess I thought that a pointer cast as a pointer would still qualify as an lvalue.
Clearly my intent is for ptr to now point two bytes beyond where it pointed before. I can't remove the parentheses around the cast of ptr because ++
has higher precedence than the cast, so this won't work as I want it to:
int i = *(uint16_t*) ptr++;
I could of course do my own incrementing, like the following, but I was hoping for something elegant and concise:
int i = *(uint16_t) ptr;
ptr += sizeof(uint16_t);
What's a good way to do this?
Upvotes: 0
Views: 579
Reputation: 276
void *ptr;
*(*(uint16_t **)&ptr)++;
Taking the address to the pointer (a void ** type) casting it to uint16_t **, dereference it, now you have a valid lvalue you can increment and dereference normally.
Upvotes: 0
Reputation: 144780
Incrementing a pointer cast to a different type has been disallowed for a long time, probably since C89. The reason is that the conversion from one type of pointer to another type can change the representation, and thus might not refer to the same object.
You must split the expression into 2 statements:
void *ptr = /*something*/;
uint16_t i = *(uint16_t*)ptr;
ptr = (uint16_t*)ptr + 1;
Or if context commands for a single expression, you could use this hack:
uint16_t i = ((uint16_t*)(ptr = (uint16_t*)ptr + 1))[-1];
Or this one if you want to obfuscate even more:
uint16_t i = (-1)[(uint16_t*)(ptr = (uint16_t*)ptr + 1)];
Upvotes: 0
Reputation: 141598
It would be simplest to write:
uint16_t *uptr = ptr;
uint16_t i = *uptr++;
ptr = uptr; // if required
You might be able to restructure the larger code to have a uint16_t *
as much as possible, removing the need for so many conversions.
Upvotes: 1