Josh Sanford
Josh Sanford

Reputation: 672

How to dereference and autoincrement a cast pointer in C?

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

Answers (3)

Marcelo Pacheco
Marcelo Pacheco

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

chqrlie
chqrlie

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

M.M
M.M

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

Related Questions