9

I saw this question in a test in which we have to tell the output of the following code.

#include<stdio.h>

int main(){
    int k = 0;
    while(+(+k--)!=0)
    k=k++;
    printf("%d\n", k);  
    return 0;
}

The output is -1. I am unsure why this is the answer, though.

What does the expression +(+k--) mean in C?

S.S. Anne
  • 15,171
  • 8
  • 38
  • 76
Ankur Gautam
  • 153
  • 1
  • 10

3 Answers3

12

This code is deeply, perhaps deliberately, confusing. It contains a narrowly-averted instance of the dread undefined behavior. It's hard to know whether the person who constructed this question was being very, very clever or very, very stupid. And the "lesson" this code might purport to teach or quiz you about -- namely, that the unary plus operator doesn't do much -- is not one that's important enough, I would think, to deserve this kind of subversive misdirection.

There are two confusing aspects of the code, the strange condition:

while(+(+k--)!=0)

and the demented statement it controls:

k=k++;

I'm going to cover the second part first.

If you have a variable like k that you want to increment by 1, C gives you not one, not two, not three, but four different ways to do it:

  1. k = k + 1
  2. k += 1
  3. ++k
  4. k++

Despite this bounty (or perhaps because of it), some programmers get confused and cough out contortions like

k = k++;

If you can't figure out what this is supposed to do, don't worry: no one can. This expression contains two different attempts to alter k's value (the k = part, and the k++ part), and because there's no rule in C to say which of the attempted modifications "wins", an expression like this is formally undefined, meaning not only that it has no defined meaning, but that the whole program containing it is suspect.

Now, if you look very carefully, you'll see that in this particular program, the line k = k++ doesn't actually get executed, because (as we're about to see) the controlling condition is initially false, so the loop runs 0 times. So this particular program might not actually be undefined -- but it's still pathologically confusing.

See also these canonical SO answers to all questions concerning Undefined Behavior of this sort.

But you didn't ask about the k=k++ part. You asked about the first confusing part, the +(+k--)!=0 condition. This looks strange, because it is strange. No one would ever write such code in a real program. So there's not much reason to learn how to understand it. (Yes, it's true, exploring the boundaries of a system can help you learn about its fine points, but there's a line in my book between imaginative, thought-provoking explorations versus dunderheaded, abusive explorations, and this expression is pretty clearly on the wrong side of that line.)

Anyway, let's examine +(+k--)!=0. (And after doing so, let's forget all about it.) Any expression like this has to be understood from the inside out. I presume you know what

k--

does. It takes k's current value and "returns" it to the rest of the expression, and it more or less simultaneously decrements k, that is, it stores the quantity k-1 back into k.

But then what does the + do? This is unary plus, not binary plus. It's just like unary minus. You know that binary minus does subtraction: the expression

a - b

subtracts b from a. And you know that unary minus negates things: the expression

-a

gives you the negative of a. What unary + does is... basically nothing. +a gives you a's value, after changing positive values to positive and negative values to negative. So the expression

+k--

gives you whatever k-- gave you, that is, k's old value.

But we're not done, because we have

+(+k--)

This just takes whatever +k-- gave you, and applies unary + to it again. So it gives you whatever +k-- gave you, which was whatever k-- gave you, which was k's old value.

So in the end, the condition

while(+(+k--)!=0)

does exactly the same thing as the much more ordinary condition

while(k-- != 0)

would have done. (It also does the same thing as the even more complicated-looking condition while(+(+(+(+k--)))!=0) would have done. And those parentheses aren't really necessary; it also does the same thing as while(+ +k--!=0) would have done.)

Even figuring out what the "normal" condition

while(k-- != 0)

does is kind of tricky. There are sort of two things going on in this loop: As the loop runs potentially multiple times, we're going to:

  1. keep doing k--, to make k smaller and smaller, but also
  2. keep doing the body of the loop, whatever that does.

But we do the k-- part right away, before (or in the process of) deciding whether to take another trip through the loop. And remember that k-- "returns" the old value of k, before decrementing it. In this program, the initial value of k is 0. So k-- is going to "return" the old value 0, then update k to -1. But then the rest of the condition is != 0 -- and it's not true that 0 != 0. That is, 0 is equal to 0, so we won't make any trips through the loop, so we won't try to execute the problematic statement k=k++ at all.

In other words, in this particular loop, although I said that "there are sort of two things going on", it turns out that thing 1 happens one time, but thing 2 happens zero times.

At any rate, I hope it's now adequately clear why this poor excuse for a program ends up printing -1 as the final value of k. Normally, I don't like to answer quiz questions like this -- it feels like cheating -- but in this case, since I fundamentally disagree with the whole point of the exercise, I don't mind.

Steve Summit
  • 45,437
  • 7
  • 70
  • 103
  • 1
    Contrived examples are typical in any foundational class. How else would one write a succinct question on [operator precedence](https://en.cppreference.com/w/cpp/language/operator_precedence) on an exam? I teach math, and if I had a nickel for every time a student asked "When am I gonna do this in *real* life?"... – Him Nov 01 '19 at 14:59
  • 4
    @Scott There's contrived, and then there's *contrived*. 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 01 '19 at 15:05
  • 1
    "I stand by my opinion" this is fair, but the keyword here is "opinion". A StackOverflow answer is maybe not the optimal place to express one's opinions. :) – Him Nov 01 '19 at 17:08
  • 1
    For the record, I have edited this answer pretty significantly since it was accepted and voted on. It still says basically the same things, though. – Steve Summit Nov 01 '19 at 18:13
11

At first glance it looks like this code invokes undefined behavior however that is not the case.

First let's format the code correctly:

#include<stdio.h>

int main(){
    int k = 0;
    while(+(+k--)!=0)
        k=k++;
    printf("%d\n", k);  
    return 0;
}

So now we can see that the statement k=k++; is inside of the loop.

Now let's trace the program:

When the loop condition is first evaluated, k has the value 0. The expression k-- has the current value of k, which is 0, and k is decremented as a side effect. So after this statement the value of k is -1.

The leading + on this expression has no effect on the value, so +k-- evaluated to 0 and similarly +(+k--) evaluates to 0.

Then the != operator is evaluated. Since 0!=0 is false, the body of the loop is not entered. Had the body been entered, you would invoke undefined behavior because k=k++ both reads and writes k without a sequence point. But the loop is not entered, so no UB.

Finally the value of k is printed which is -1.

dbush
  • 205,898
  • 23
  • 218
  • 273
  • 1
    It's an open, existential, philosophical, imponderable question whether undefined behavior that's not executed isn't undefined. Is it not undefined? I don't think so. – Steve Summit Nov 01 '19 at 14:46
  • 2
    @SteveSummit There is absolutely nothing existential, philosophical, imponderable about it whatsoever. `if (x != NULL) *x = 42;` Is this undefined when `x == NULL`? Of course not. Undefined behaviour does not happen in parts of code that do not get executed. The word **behaviour** is a hint. Code that is not executed has no behaviour, undefined or otherwise. – n. m. could be an AI Nov 01 '19 at 14:50
  • 1
    @SteveSummit If undefined behavior that's not executed would invalidate the entire program, no C program would have defined behavior. Because C programs are always just a loop/if condition away from executing UB. Take a simple array iteration for an example: It iterates until the bound check fails. Executing the next loop iteration would be undefined behavior, but because that's never executed, everything's fine. – cmaster - reinstate monica Nov 01 '19 at 14:54
  • 3
    I'm not going to argue about it, because I don't do language lawyer. But I bet if you asked this question and tagged it language-lawyer, you could get a spirited debate going. Note that `k=k++` is qualitatively different than `*x=42`. The latter is well-defined if `x` is a valid pointer, but the former is undefined no matter what. (I concede you may be right, but again, I'm not going to argue abut it, and I'm increasingly concerned we've been masterfully trolled.) – Steve Summit Nov 01 '19 at 14:57
  • 1
    " The latter is well-defined if x is a valid pointer, but the former is undefined no matter what". Only entire programs may have undefined behaviour. This notion is not applicable to code fragments taken in isolation. – n. m. could be an AI Nov 01 '19 at 15:11
  • 1
    And [you did](https://stackoverflow.com/questions/58661493/does-i-i-cause-undefined-behavior-even-if-not-executed)! Thanks. (I look forward to the results.) – Steve Summit Nov 01 '19 at 15:11
3

Here's a version of this that shows operator precedence:

+(+(k--))

The two unary + operators don't do anything, so this expression is exactly equivalent to k--. The person that wrote this most likely was trying to mess with your mind.

S.S. Anne
  • 15,171
  • 8
  • 38
  • 76