-1

EDIT: this is not my code, that's a part of a exam.

This is the output:

60 -3 //from -5 to -3
59 0 //from -3 to 0
58 3
57 6
2

I can't figure out why in the first loop, the 'i' variable was increased two times and in the second loop three times.

int a=61,i=-5;

for(int *p=&i;(a++,(*p)++)?(++(*p),(a--)-1):((*p)+=3,a-1);(*p)++){
    --a;
    printf("%d %d \n",a,*p);
    if(*p>3){
        a=(!(--a)&&a++)?3:2;
        break;
    }
    else continue;
}  

printf("%d\n",a);  
klutt
  • 30,332
  • 17
  • 55
  • 95
edoardo
  • 35
  • 7
  • you have plenty UBs here. BTW one of most frequesnt duplicates – 0___________ Nov 11 '19 at 17:30
  • 2
    That code is practically undecipherable. Make your goal to write code that not only the compiler can understand, but that is understandable by humans as well. Think what will happen if you get this code to work, but as asked to change it in two years -- will you know how to do it? Code like this would never pass a code review in a professional setting, in any team I've ever been a part of, or led. – TomServo Nov 11 '19 at 17:31
  • 2
    Was this code given to you as part of an assignment, exam, or interview? Or did you just randomly type it in? – Steve Summit Nov 11 '19 at 17:33
  • 2
    My opinion is that code like this has no value for anything: certainly not practical, not even pedagogical. I don't believe it's worth your time trying to figure out what it does, and it's certainly not worth mine. If you want to understand how pointers, `for` loops, and the `++` operator all work, there are far better ways. – Steve Summit Nov 11 '19 at 17:35
  • 2
    There's no UB here. The use of the comma operator, ternary operator, and logical AND introduce sufficient sequence points. – dbush Nov 11 '19 at 17:37
  • @dbush no UBs ? only one example `(!(--a)&&a++)` – 0___________ Nov 11 '19 at 17:50
  • 1
    @P__J__ Nope. The evaluation of the left operand of `&&` is sequenced before the right operand. Otherwise you coudn't do `if (p != NULL && p->data > 4)` – dbush Nov 11 '19 at 17:51
  • 2
    I would say it's quite impressive to have such a code without UBs :) Still, the author should be banned from touching keyboards – Eugene Sh. Nov 11 '19 at 17:55
  • @SteveSummit yez, this code is a part of an exam, i have more excercises like this, whith even more ubfuscated code. – edoardo Nov 11 '19 at 17:58
  • 3
    If this code is part of a class, drop that class immediately and go look for a legitimate one. – Lee Daniel Crocker Nov 11 '19 at 18:01
  • @SteveSummit: Clearly it is pedagogical, because it just taught P__J__ not to jump to conclusions. That is a valuable lesson all programmers should learn. – Eric Postpischil Nov 11 '19 at 18:07
  • 2
    @TomServo: Whether the code would pass review in a professional setting is irrelevant, as the code is in an exam, not a professional setting. Regardless of whether code should or should not be written this way, learning to analyze cryptic code is an essential skill for programmers. One **must** be able to analyze what code actually expresses **regardless** of one’s visual impressions or proclivities to jump to conclusions. If a person can only understand what code does if it looks pretty and is simple, they are of little use for programming. It may be distasteful, but it is necessary. – Eric Postpischil Nov 11 '19 at 18:10
  • 1
    Eww. If I had to bet my job on understanding such ugly code, I'd go read the assembly output. – Michael Dorgan Nov 11 '19 at 18:19
  • @TomServo: I have more experience than that, and it did not take anywhere near that amount of time to learn to adapt to new information. The code is from an exam, so arguing about how hard it is to update or debug is irrelevant. It is false that no one should ever write code that someone else cannot easily read: A time when someone should write code that someone else cannot easily read is when crafting exercises in reading and analyzing code. They are **intended** and **needed** to be hard to cause the brain to exercise. You stretch on hard things so that routine things are easier. – Eric Postpischil Nov 11 '19 at 18:23
  • @EricPostpischil As I said, you're entitled to your opinion. We can disagree and go on with our lives now, I think. – TomServo Nov 11 '19 at 18:25
  • 1
    I'm reminded of [this question](https://stackoverflow.com/questions/58660861/k-expression-in-c), where in a similar comment thread on the propriety of asking contrived questions like this, I wrote: "Suppose you're a driver training instructor. Suppose you ask your student to drive through heavy downtown traffic while beating him about the head with a baseball bat. Arguably, the student will learn a potentially-useful skill. But I call this abuse. And I stand by my opinion that the code in this question is abusive, with negative pedagogical value." – Steve Summit Nov 11 '19 at 19:00
  • 1
    There's also the question of whether students ought to be getting help from Stack Overflow on exam questions. It's pretty clear that this student simply could not answer this question without help from experts here. (Note too that plenty of the experts here can't or won't answer the question at all.) I suspect this student is not alone. I'd say if most of the students can't answer most of the questions on the exam, something is wrong somewhere. – Steve Summit Nov 11 '19 at 19:03

3 Answers3

1

The only sensible thing to do with code like this (except from saying words that are not allowed here to the author) is to rewrite it. The else continue can be removed. It accomplishes nothing.

Then we go for the beast (a++,(*p)++)?(++(*p),(a--)-1):((*p)+=3,a-1). This will be executed in the beginning of each loop, so we can rewrite it like this:

bool cond = (a++,(*p)++)?(++(*p),(a--)-1):((*p)+=3,a-1);
for(int *p=&i; cond; (*p)++){
    cond = (a++,(*p)++)?(++(*p),(a--)-1):((*p)+=3,a-1);

    --a;
    printf("%d %d \n",a,*p);
    if(*p>3){
    a=(!(--a)&&a++)?3:2;
    break;
    }
}

Now when it's removed from the header, we can start disassemble it. First, exctract a++ change to if statements:

a++;
if((*p)++)
    cond = (++(*p),(a--)-1)
else
    cond = ((*p)+=3,a-1);

It's already looking a lot clearer. Now, lets get rid of those commas:

if((*p)++) {
    ++(*p);
    cond = (a--)-1;
}
else {
    (*p)+=3;
    cond = a-1;
}

Let's continue the separation

if((*p)++) {
    (*p)++; // Does not make a difference here, but it's easier to not mix pre and post
    a--;
    cond = a;
}
else {
    (*p)+=3;
    cond = a-1;
}

That's about as far as we get by just mechanically breaking it down. To get further we would need to think a little bit. So let's continue to the next, which is a=(!(--a)&&a++)?3:2; and we will simplify it too.

--a; // Will be executed no matter what
if(!a) {
    a++; // Will only be executed if !(--a) evaluates to true
    a = 3; // But it does not matter since we are reassigning a
} else {
    a = 2;
}

This will give this code after simplification.

bool cond;

a++;
if((*p)++) {
    (*p)++;
    a--;
    cond = a;
} else {
    (*p)+=3;
    cond = a-1;
}

for(int *p=&i; cond; (*p)++){
    a++;
    if((*p)++) {
        (*p)++;
        a--;
        cond = a;
    } else {
        (*p)+=3;
        cond = a-1;
    }

    --a; 
    printf("%d %d \n",a,*p);
    if(!a) {
        a = 3; 
    } else {
        a = 2;
    }
}

printf("%d\n",a);  

Now it's actually possible to reason about it, and it should be fairly easy to just execute this code on paper step by step.

klutt
  • 30,332
  • 17
  • 55
  • 95
0

The (*p)++ statement at the end of the for loop declaration will not be executed on the first iteration. It gets executed only between two consecutive iterations.

SamYonnou
  • 2,068
  • 1
  • 19
  • 23
  • 2
    @P__J__: There is a sequence point after evaluation of the first operand of `? :`. There is a sequence point after the initial expression of the `for`, the controlling expression of the `for`, and the iteration expression of the `for`. Where do you see any problem with unsequenced evaluation? – Eric Postpischil Nov 11 '19 at 17:39
0

First, int a=61,i=-5; gives us a = 61 and i = −5.

Then the initial clause of the for, int *p=&i, sets p to point to i. From this point on, we may take *p as equivalent to i.

Then the controlling expression of the for, (a++,(*p)++)?(++(*p),(a--)-1):((*p)+=3,a-1), is evaluated. Its highest/outermost operator is ? :. The first operand of that (a++,(*p)++) is evaluated. This sets a to 62 and i to −4. The result, from the comma operator, is the value of i (*p) before the increment, so it is −5.

That −5 is used to select in the ? : operation. Since it is not zero, the operand between ? and : is evaluated. That is (++(*p),(a--)-1). This sets i to −3 and a to 61. The value is a before the increment minus 1, which is 62−1 = 61. Thus the expression is non-zero, indicating the loop should continue.

Program control flows into the body of the for, where --a decrements a to 60.

Then the printf shows us that a is 60 and i is −3.

The test *p>3 is false, since i is −3, so the continue is executed.

This causes the iteration expression of the for to be evaluated. That is (*p)++, so i is set to −2.

Then the controlling expression is evaluated. As before, the first operand of ? :, (a++,(*p)++), is evaluated. This sets a to 61 and i to −1, and the value of the expression is −2.

Again the second operand,(++(*p),(a--)-1), is evaluated. This sets i to 0 and a to 60, and its value is 59.

Inside the body, --a decrements a to 59.

The printf shows us a is 59 and i is 0.

Again *p>3 is false, so the continue is executed.

This takes control to the iteration expression, (*p)++, which sets i to 1.

Then the controlling expression is evaluated, starting with the first operand of ? :. Now (a++,(*p)++) sets a to 60 and i to 2, and the expression value is 1.

The second operand of ? :,(++(*p),(a--)-1), is evaluated, which sets i to 3 and a to 59, and the expression value is 59, so the loop continues.

--a sets a to 58.

The printf shows us a is 58 and i is 3.

Again *p>3 is false, so the continue is executed.

This takes control to the iteration expression, (*p)++, which sets i to 4.

Then the controlling expression is evaluated, starting with the first operand of ? :. Now (a++,(*p)++) sets a to 59 and i to 5, and the expression value is 4.

The second operand of ? :,(++(*p),(a--)-1), is evaluated, which sets i to 6 and a to 58, and the expression value is 58, so the loop continues.

--a sets a to 57.

The printf shows us a is 57 and i is 6.

Now *p>3 is true, so the statements inside the if are executed.

These start with a=(!(--a)&&a++)?3:2;. Here --a sets a to 56 and evaluates to that value. Then ! inverts it logically, producing 0. This causes the && to produce 0 without evaluating its second operand. So this first operand of ? : is 0, which results in the third operand of ? : being evaluated. That operand is 2, so that is the value assigned to a.

The final printf shows us the current value of a, 2.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312