Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C pointer arithmetic snippet

I have a program that I'm trying to decode. It is translated to C from another language (whose name is not spoken here), and as I want to understand how it works, I am slowly rewriting the code and simplifying it to use all the nice logical constructs C has to offer.

The following little bit keeps popping up in my code, with varying values of X and Y:

ptr[X]--;
while(ptr[X])
  {
    ptr[X]--;
    ptr += Y;
  }

ptr is of type char *, and I can't really make assumptions about the state of the array at any point because it's pretty deeply embedded in loops and dependent on input and output. I can successfully "simplify" that to:

for(ptr[X]--; ptr[X]; ptr[X]--, ptr += Y);

But that's just awful. Ever so slightly better is:

for(ptr[X]--; ptr[X]; ptr += Y) ptr[X]--;

I want to know if anyone can come up with a better simplification of the above code, I would greatly appreciate it. This occurs in no less than five places, and is impairing my ability to simplify and understand the flow control, so if anyone can provide a more consise/readable version, that would be awesome. If anyone can just offer any sort of fancy insight into that code, that would be awesome too, although I basically understand what it does.

Insight into the code for a specific X and/or Y can also help. Y tends to be between -2 and 2, and X is usually 1, for what its worth.

like image 706
Chris Lutz Avatar asked Dec 13 '22 04:12

Chris Lutz


2 Answers

ptr[X] is equivalent to *(ptr + X), so we can rewrite it as follows:

for((*(ptr + X))--; *(ptr + X); (*(ptr + X))--, ptr += Y);

Now there's a lot of redundancy here, so we can simplify this to:

char *ptr_plus_x = ptr + X;
for((*ptr_plus_x)--; *ptr_plus_x; (*ptr_plus_x)--, ptr_plus_x += Y);

Then we can get rid of ptr_plus_x entirely:

ptr += X;
for((*ptr)--; *ptr; (*ptr)--, ptr += Y);

In English, we visit the memory locations at offsets X, X+Y, X+2Y, X+3Y, ..., decrementing each memory location, until we find a memory location that is 0. But, the test for 0 always occurs after the decrement, so we're really looking for the first memory location in that sequence with a value of 1. Once we find that, we decrement it to 0 and quit.

If Y is 1, then we decrement a string of consecutive memory locations going forwards, up to and including the first 1. If Y is -1, the same thing happens, but searching backwards from offset X. If Y is 0, an infinite loop occurs. If Y is any other value, the search pattern skips various entries.

It's not a very intuitive function, so I can see why you're confused.

like image 194
Adam Rosenfield Avatar answered Dec 26 '22 03:12

Adam Rosenfield


I'll throw in:

ptr[X]--
while (ptr[X]--) ptr+=Y;

first evaluate, then decrement (for while condition, that is)

Edit: OK, i'll hate myself in the morning. Goto's are ok at this level, right?

dec:  ptr[x]--
      while (ptr[X]){
           ptr+=Y;
           goto dec;
      }

(i honestly dont know whether to leave this or not.)

EDIT2: so, how about this one? (tcc didn't complain)

 while (ptr[X]--?ptr[X]--,ptr+=Y:0){} 

EDIT 2 1/2;

  //longshot
  while (ptr[X]--?ptr[X]--,ptr+=Y, ptr[X]:0){} 

If all else fails..

EDIT3: Last one for tonight.

while (ptr[X]--?ptr[X]--,ptr+=Y:0){
      if (!ptr[X]) break;
 }//good luck with this, it has been very amusing.
like image 28
Tom Avatar answered Dec 26 '22 03:12

Tom