Fluffy Stuff

A tmp place to rest

Difference between i++ and ++i

Date Tags c

I'm starting to read through Hacking: The Art of Exploitation on my four hours daily commute to work in order to get myself more comfortable working with C. I resolve a puzzle I have about C, as shown in the title.

  • i++ means increment the value of i by 1 after evaluating the arithmetic operation.
int a, b;
a = 5;
b = a++ * 6;

b will contain 30 and a will contain 6, since the shorthand of b = a++ * 6; is the equivalent to the following statements:

b = a * 6;
a = a + 1;
  • ++i means increment the value of i by 1 before evaluating the arithmetic operation.
int a, b;
a = 5;
b = ++a * 6;

b will contain 36 and a wil contain 6, since the shorthand of b = ++a * 6; is the equivalent to the following statement:

a = a + 1;
b = a * 6;

In fact, the two principles mentioned above apply more than "evaluating the arithmetic operation". For example, in the stack push operation:

/* Push an element on the Stack
 */
void
push (ET elem, Stack S)
{
  if (isFull(S))
  {
    resizeStack(S);
  }
  S->Array[++S->TopOfStack] = elem;
}

we can do S->Array[++S->TopOfStack] = elem;, which is equivalent with the following, a nice short verison:

S->Array[S->TopOfStack] = elem;
S->TopOfStack = S->TopOfStack + 1;

Another example is stack topAndPop:

/* Check the top element and pop it out of Stack
 */
ET
topAndPop(Stack S)
{
  return S->Array[S->TopOfStack--];
}

In this case, we essentially do:

ET a = S->Array[S->TopOfStack];
S->TopOfStack = S->TopOfStack - 1;
return a;

Look how clean I can make my code is if I can understand the difference between ++i and i++.

--- 01/19/2017 UPDATE ---

++i and i++ is really a powerful technique to shorten the C code. However, it can be error-prune if we are not careful enough.

Let's take a look at the following code snippet, which is adapted from the program on K & R p.117.

main(int argc, char* argv[])
{
  while (--argc > 0 && (*++argv)[0] == '-')
    while (c = *++argv[0])
      switch (c)
      {
        case 'x':
          printf ("user invokes the program with -x option\n");
          break;
        case 'n':
          printf ("user invokes the program with -n option\n");
          break;
        default:
          printf("illegal option %c\n", c);
          argc = 0;
          break;
      }
   if (argc != 1)
     printf("Usage: find -x pattern\n");
}

This program itself is straightforward. Let's take a look at (*++argv)[0] to see what it means. Since argv is a pointer to the beginning of the array of argument strings, incrementing it by 1 (i.e. ++argv) makes it point at the original argv[1] instead of argv[0]. Then we dereference it to get the value of the argument string that we are currently looking at (i.e *++argv). Now, we get its first character by adding [0]. So, we have (*++argv)[0]. For example, we run our program with a.out -x -n pattern. Then our argv looks like {"-x", "-n", "pattern"}. Then argv[0] is "-x", argv[1] is "-n", and so on.

The reason we need parenthesis in (*++argv)[0] can be seen in the next line c = *++argv[0]. Since [] binds tighter than * and ++, then *++argv[0] is equivalent with c = *++(argv[0]). argv[0] points to the first char of the argument string that argv pointing at. Then we increment and dereference argv[0] to get the next character in the argument string. For instance, suppose argv pointing at -x. Then argv[0] pointing at - and we increment and dereference argv[0] to get x and assign to c.

We can see that the level of precedence of operators is crtical in this case. This can be seen from table on p.53. in K & R:

Let's see another example from K & R p. 105.

void strcpy(char *s, char *t)
{
  while ((*s++ = *t++) != '\0')
    ;
}

In this case, the value of *t++ is the character that t pointed to before t was incremented; the postfix ++ doesn't change t until after this character has been fetched. This makes sense if we consider it from precedence of the operators' view. * and ++ have the same precedence in our table. Thus, we evaluate the expression in ordinary order: from left to right. We first evaluate *s then we increment s.

comments powered by Disqus