2
#include<stdio.h>
#include<conio.h>
void main()
{
    int i=1;
    for(;i;)i++;
    printf("%d",i);
    getch();
}

I always get the output as zero no matter what value i initially has.

GAURANG VYAS
  • 689
  • 5
  • 16
Roko
  • 129
  • 1
  • 10
  • 5
    I'm no C expert, but I would expect that loop to silently continue for all positive values of `i` until it overflows `int` and eventually becomes `0`. Once the loop exits on a value of `0` for `i` (which would be "falsey") then nothing further modifies `i`, so it remains `0`. – David Jun 26 '17 at 16:11
  • Are you asking how integer overflow/underflow works? That's what's happening, but is that what you're asking? (fyi, I took liberty to fix the indentation in your source code, which was both deceptive, and incorrect of any rational C programmer). – WhozCraig Jun 26 '17 at 16:12
  • 3
    The signed integer wraparound on overflow **is not guaranteed to happen**. The code is broken. – Antti Haapala -- Слава Україні Jun 26 '17 at 16:33
  • [`void main()` is wrong in C](https://stackoverflow.com/q/204476/995714). And signed overflow is undefined behavior. Change it to unsiged and it'll always return zero – phuclv Jun 26 '17 at 16:46
  • I got answer Thanks but remember program is running successfully. – Roko Jun 26 '17 at 16:50
  • 3
    @Roko "running successful" doesn't mean a correct program, because in C, you can have **undefined behavior**. A program with UB doesn't have a defined meaning, so it can break in most interesting ways (built for another platform, using another compiler, getting different input, running in a different timezone, endless possibilities!) –  Jun 26 '17 at 16:56

5 Answers5

5

TL;DR: your program prints 0 because if the loop ever exits, it must be because i is zero; and starting with i = 1 this can happen for example by signed integer overflow causing a wraparound. But the C standard doesn't actually require such wraparound, and thus the code invokes undefined behaviour. An optimizing compiler might and probably will make the resulting program to do funny things.


The for loop could be exchanged with the following while loop - they're the same.

while (i) {
    i++;
}

This means: "while i is non-zero, increment i by one". After the loop exists, the value of i must be zero, and that is what is printed.

However, the starting value of i is 1. That it happens to print 0 is just a fluke, because your program invokes undefined behaviour: Each iteration you increment it by 1. By the time it becomes INT_MAX, and you add 1 to it, the behaviour is undefined because INT_MAX + 1 cannot be represented in an int.

Then anything can happen, including the counter wrapping around to INT_MIN, which seemingly happens with your unoptimized program. It is not required however. The compiler could generate code that would make the loop hang there forever for example. This isn't just hypothetical, but it is easy to produce with GCC 6.3.0:

% gcc wraparound.c -O2
wraparound.c: In function ‘main’:
wraparound.c:6:14: warning: iteration 2147483646 invokes undefined behavior 
           [-Waggressive-loop-optimizations]
     for(;i;)i++;
             ~^~
wraparound.c:6:5: note: within this loop
     for(;i;)i++;
     ^~~

The resulting program never ends, neither does it ever print anything.

The disassembly shows that the only instruction in the main is:

0000000000000530 <main>:
 530:   eb fe                   jmp    530 <main>

That is, the code runs an infinite loop, i.e. while (1) {}.


If - for some reason - it does wrap around to INT_MIN and the execution continues, then eventually the negative INT_MIN will be incremented the necessary 2 billion times so that it becomes 0, and then the terminating condition i evaluates to 0, which is a falsy value, and the loop is terminated. And since i now holds the value 0, that's what is printed.

The integer overflow is also used as the sole example in the C standard 3.4.3 Undefined behavior:

Undefined behavior

  1. behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements

  2. NOTE Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).

  3. EXAMPLE An example of undefined behavior is the behavior on integer overflow.


Signed integer overflow is not defined by the C standard, but unsigned integer math is guaranteed to wrap. Your code becomes perfectly defined if you change int i = 1; to unsigned int i = 1;. Though, you might need to wait for a while if unsigned ints are 64-bits wide and you didn't optimize.

This program

#include <stdio.h>
int main(void)
{
    unsigned int i = 1;
    for(;i;)i++;
    printf("%d\n",i);
}

when compiled with my GCC with -O2 unrolls the loop entirely and just prints 0 right away:

% time ./a.out
0
./a.out  0.00s user 0.00s system 0% cpu 0.003 total

i.e. the compiler deduced the result and hardcoded it.

1

Based on your for loop, "i" is the terminating condition. In C, any non-zero value is considered true when used in a boolean expression. Therefore, the only exit condition for the loop you made is the situation in which i finally becomes zero, which as others have noted is likely due a wraparound that has occurred when the value of i experienced an overflow. Again, as others have noted, this particular behavior is compiler specific and an overflow does not always result in a wraparound, since it is essentially undefined behavior.

h0r53
  • 3,034
  • 2
  • 16
  • 25
  • 1
    It is not due to overflow. It is due to *wraparound*. Overflow is what happens when the result of a calculation is too big to be represented in an `int`. Wraparound is one possible outcome of the overflow, but it is not required to be. – Antti Haapala -- Слава Україні Jun 26 '17 at 17:03
  • That's a good point. I generically called this overflow, which as you've stated doesn't always result in a wraparound. I'll update my answer. – h0r53 Jun 26 '17 at 17:05
0

You have i as condition in the for(;i;) so it gets converted into a boolean and tested:

  • i positive result into true
  • i=0 result into false

so the loop will run until it reaches the max possible value and overflow and become 0, when it happens the for will end and you print the value of i (that is 0).

Changing the initialization of i in the beginning doesn't matter because every non-zero value will be converted into true so the for will start anyway and run as I said previously unless you initialize it as 0.

I don't get what you want to accomplish with that code but i hope my answer helps

Andrea
  • 75
  • 6
0

The loop

for(;i;)
  i++;

basically says, "while i is not zero, increment i". Integer overflow1 happens and i eventually wraps back around to 0, at which point the loop exits. That's the value you print.


  1. The behavior on signed integer overflow is undefined, so it's not strictly guaranteed that i would wrap back around to zero or that the loop would terminate; however, on most real-world systems it eventually will return to 0.
John Bode
  • 119,563
  • 19
  • 122
  • 198
0

Your loop code:

   int i=1;
     for(;i;)i++;
      printf("%d",i);

Initializes i to 1, then the for loop keeps incrementing i until it is zero, then exits and prints the value of i which must be zero.

Why?

A for has four part of its syntax for(init;test;increment) body;:

  1. init: this is typically used to initialize some counter like i, but in your case this is blank, so it does nothing. Fortunately, you initialized i to in the previous statement, but it is better style to do it within the for loop.
  2. test: this is used to exit the loop by testing some expression to see if it is zero. When it evaluates to zero (or false if it is a boolean expression), then loop exits. In your case, you are testing i, so the loop will keep incrementing i until it is zero, then the for loop will exit. That is why you only see the print once i becomes zero.
  3. increment: this is where one typically places the i++ that you have instead placed as the for body. body
  4. body: In you case, the body is i++. You may think the printf is part of the body, it is the next statement after the for loop. To include it in the loop, you would need to add braces (unlike Python that uses indenting to parse syntax).

    int i=1; for(;i;) { i++; printf("%d",i); }

With the printf included in the loop as above, you will see that your loop will keep incrementing i until it hits MAX_INT, then thanks to 2s-complement, it will flip to -INT_MAX (INT_MIN actually), and keeps incrementing that until it is zero. As others have noted, overflow of a counter is technically undefined behavior, but wrap-around is what I have seen on every architecture that I have used with C, but being UB, your loop should not depend on this wrap-around working in a well defined way. Besides, it makes for difficult code to understand and maintain!

As written, your loop increments i about 4 billion times (depending on the size of int on your machine), but to the credit of the C compiler, this happens remarkable fast. After adding the printf into your loop, it will take considerably longer!

The correct way to write your loop would be:

#include<stdio.h>
#include<conio.h>
void main()
{
    int i;
    for(i=1; i!=0; i++)
    {
       printf("%d",i);
       getch();
    }
}
ScottK
  • 1,526
  • 1
  • 16
  • 23
  • Thanks for that i got it – Roko Jun 26 '17 at 16:51
  • The wraparound thingie is not about the architecture, the architecture doesn't mean one bit here. Every single good compiler does it, including MSVC++ here: https://www.viva64.com/en/b/0374/ – Antti Haapala -- Слава Україні Jun 26 '17 at 23:05
  • @AnttiHaapala: Agreed. I was paraphrasing your excellent answer (which I upvoted!) which quoted the C standard as int overflow being undefined behavior. I think of it as also being architecture dependent because the number of bits defined for int is architecture-dependent, so the wrap-around point may vary based on architecture. – ScottK Jun 26 '17 at 23:51