Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Output of C program with complex pointer arithmetic

I am preparing for quiz on programming involving guessing C code outputs.

After long try, I am still struggling to understand the output of the below code:

#include <stdio.h>

char *c[] = {"GeksQuiz", "MCQ", "TEST", "QUIZ"};
char **cp[] = {c+3, c+2, c+1, c};
char ***cpp = cp;

int main()
{
    printf("%s ", **++cpp);
    printf("%s ", *--*++cpp+3);
    printf("%s ", *cpp[-2]+3);
    printf("%s ", cpp[-1][-1]+1);
    return 0;
}

Output

TEST sQuiz Z CQ

Can anyone please help me to understand why is this output?

like image 426
Mahesha999 Avatar asked May 17 '15 21:05

Mahesha999


2 Answers

It will be helpful to create the following temporary variables to understand some of the expressions.

char s1[] = "GeksQuiz";
char s2[] = "MCQ";
char s3[] = "TEST";
char s4[] = "QUIZ";

char *c[] = {s1, s2, s3, s4};
char **cp[] = {c+3, c+2, c+1, c};
char ***cpp = cp;

First printf

printf("%s ", **++cpp);

**++cpp has the side effect of cpp = cpp+1, and evaluates to

**(cpp+1), which is the same as

*(cp[1]), which is the same as

*(c+2), which is the same as:

c[2], which is the same as:

s3, which evaluates to "TEST".(initially it was s2 corrected)

At the end of that statement, cpp is the same as cp+1.

Second printf

printf("%s ", *--*++cpp+3);

*--*++cpp+3 is the same as

*(--(*(++cpp))) + 3, has side effect of cpp = cpp+1, and evaluates to:

*(--(*(cpp+1))) + 3, which is the same as

*(--(*(cp+2))) + 3, which is the same as

*(--(cp[2])) + 3

*(--(cp[2])) + 3 has the side effect of cp[2] = cp[2]-1 = c+1-1 = c, and evaluates to:

*(cp[2]-1) + 3, which is the same as

*(c+1-1) + 3, which is the same as

*(c) + 3, which is the same as

c[0] + 3, which is the same as

s1 + 3, which evaluates to "sQuiz"

At the end of that statement, cpp is the same as cp+2.

Third printf

printf("%s ", *cpp[-2]+3);

*cpp[-2]+3 is the same as

*(cpp[-2])+3, which is the same as

*(cp)+3 because of the previous ++ operations on cpp., which is the same as

c[3]+3, which is the same as

s4+3, which evaluates to "Z".(corrected initially it was s3+3)

Fourth printf

printf("%s ", cpp[-1][-1]+1);

cpp[-1][-1]+1 is the same as

*(cpp-1)[-1]+1, which is the same as

*(*(cpp-1) -1) + 1, which is the same as

*(*(cp+1) -1) + 1, which is the same as

*(cp[1] -1) + 1, which is the same as

*(c+2-1) + 1, which is the same as

*(c+1) + 1, which is the same as

c[1] + 1, which is the same as

s2 + 1, which evaluates to "CQ".

like image 78
R Sahu Avatar answered Oct 19 '22 13:10

R Sahu


It helps to draw a picture of the pointers, but that's difficult to do on StackOverflow:

cpp: cp
cp: c+3, c+2, c+1,  c
c:   s0,  s1,  s2,  s3
s0: 'G', 'e', 'K', 's', 'Q', 'u', 'i', 'z', '\0'
s1: 'M', 'C', 'Q', '\0'
s2: 'T', 'E', 'S', 'T', '\0'
s3: 'Q', 'U', 'I', 'Z', '\0'

This is a table showing each of the arrays, plus the static strings (which I've given names to make them easier to talk about).

So now lets look at what the statements do:

printf("%s ", **++cpp);

Increment cpp (changing to point at cp[1]), then deref twice -- the first gets c+2, the second s2 which is then printed: TEST

printf("%s ", *--*++cpp+3);

Increment cpp (now points at cp[2]), dereferece and decrement that (changing cp[2] to now point at c[0]), and dereference again (getting s0). Finally add 3 (s0+3) and print: sQuiz

printf("%s ", *cpp[-2]+3);

Get the value back 2 slots from cp[2] (which is cp[0] == c+3) and then dereference it to get s3. Then add 3, and print: Z

printf("%s ", cpp[-1][-1]+1);

Get the value back 1 slot from cp[2] (which is cp[1] == c+2), then get the value back one slot from that (which is c[1] == s1), then add 1 and print: CQ


The important things to remember are:

  • every * or [] is a dereference which refers to the value pointed at by a pointer
  • increment/decrement operators operate on the last dereferenced thing (or on the variable directly if nothing has been dereferenced yet.
  • unary operators are higher precedence than binary, so (barring parenthesis), all unary postfix operators happen first, then unary prefix, and only then binary.
  • [] is really a unary postfix operator, not an infix binary operator (even though it has two operands), as the second operand is within the brackets.
like image 28
Chris Dodd Avatar answered Oct 19 '22 11:10

Chris Dodd