Why does this infinite loop in C not loop infinitely? - c

I am running this below 'C' program which has an infinite loop. I see the CPU reaching almost 99% using the 'top' command. Then suddenly the program stops. I was curious to know what rule in the system is causing it to stop.
I am running this under a docker image alpine:3.1 (+ some modules) and I just simply start its ash (alpine's bash).
Also, I can not find ulimit.
#include <stdio.h>
int main() {
int marks[10],i,n,sum=0;
printf("Enter number of students: ");
scanf("%d",&n);
for(i = 0; i < n; --i) {
//printf("Enter marks of student%d: ",i+1);
int c;
c = 5;
int a;
a = c;
}
return 0;
}

As you mention, you could expect this to produce an infinite loop, at least if n is positive.
The behaviour you're seeing instead is caused by your counter wrapping around: when it reaches INT_MIN, the result of --i is INT_MAX (at least, with GCC's default settings on Intel-style CPUs — this is not defined in C, see below) and i<n fails.
So your program uses 100% of one CPU while it counts down from 0 to INT_MIN, then wraps around to INT_MAX and stops.
You can add
printf("%d\n", i);
just before the return line to see the value of i when the loop exits.
Note that this is undefined behaviour in C... If you compile this with -O2, at least with GCC 5.3, the program never exits the loop, because the optimiser decides that the exit condition will never be reached, and compiles the loop as a tight infinite loop if n is positive (.L4: jmp .L4).

Your loop is not infinite per se. YOu have a termnation condition which can be reached - in principle. The CPU load occurs, just because the loop runs for (likely) >2000000000 iterations before it ends.
First of all, you don't check the result of scanf. If it failed, n is unspecified.
Worse, your code invokes undefined behaviour (UB) due to signed integer over-/underflow. You decrement i past the minimum representable int value INT_MIN. Note that C does not have arbitrary length integers like Python.
Undefined behaviour means anything can happen, all bets are off. In your case, it might wrap to the max. positive int INT_MAX, thus the comparison fails and the loop is exited. But this is nothing you can rely on!
From the text and rest of the code, you likely don't want to decrement i, but increment: i++, so it counts from 0 to n - 1. Note that you also should check n for a valid range and ask the use to enter a valid value again. For the example, a negative upper limit is useless (but it does also no harm).

Related

I don't get what is happening inside the while loop . Would you please explain?

#include <stdio.h>
int main() {
int i = 10;
while (i++ != 0);
printf("%d", i);
}
Output: 1
I do not understand the while loop.
i is incremented until the int variable saturates (32767 when int is 16 bits long) and then starts counting up from -32.768 to zero. When i becomes zero, the while-loop stops. Because of the post increment i++, i will be one when the while loop is finished. So you get one as a result printed to your output.
Edit: Long explanation:
When executing the while condition the first time, it will be checked if 10 is not equal to 0. After this check, i is incremented to 11, next check is 11 not equal to zero, and i becomes 12. This continues until you reach the highest possible value for int. When this maximum value is incremented, you get the lowest possible value, but this is also not equal zero. So the condition is still true, but now you have a negative number, and a negative number incremented will get zero. So after some time, i becomes zero and the while condition is false and since i is incremented after this check, you will get an output of one.
The code has undefined behavior:
the while loop has an empty body consisting of a null statement ;.
hence the loop iterates doing nothing more than the side effect in the test i++ != 0.
i starts at 10 so the first test is true, i is incremented to 11 and the loop continues with the test again... i is incremented all the way to INT_MAX, which is a least 32767, but more commonly 2147483647 and incrementing i with this value has a undefined behavior because it causes a signed arithmetic overflow. What happens then is undefined, the program could stop, run an infinite loop, turn the lights off... anything is possible.
on your system, i will probably just get the value INT_MIN an no bad side effect will happen.
the loop continues, incrementing i through all the negative values...
until i finally becomes 0, making the test evaluate to false and break the loop after the side effect of ++ happens for the last time, leaving i with the value 1.
the last statement printf("%d",i); prints 1, without a newline, which may prevent the output from reaching the terminal on some legacy systems.
So depending on the system, the program can:
print 1 and exit immediately
print 1 after pause and exit
run forever and print nothing*
crash with some kind of error
cause a more of less important side effect, such as summoning aliens from Sirius to invade planet Earth...
(*) gcc -O2 compiles the posted code to a single jmp instruction on Godbolt's Compiler Explorer.

Why does it takes a little longer to display 'Hello' after terminating a For loop before the printing syntax?

#include <stdio.h>
int main(void) {
// single-line for-loop
for (int i = 0; i < 5; i--);
// delays to execute this syntax
printf("Hello\n");
return 0;
}
Why does it take around 10 seconds to print Hello in console after running it?
Notice that the use of a semicolon at the end of the For loop is intentionally given.
I've figured out how and why this happened..
It's because the range of int data type is from -2,147,483,648 to 2,147,483,647. "i" in this for loop is going from 0 to -2,147,483,648 (because of i--;) and after that when the value of "i" becomes 2,147,483,647 (because of limitations of data type's range and data overflow), hence making the condition (i<5;) false and the loop stops. After that the next statement (printf("Hello");) prints "Hello".
The whole process of the loop iterating 2 Billion times takes 10 seconds by my compiler to process and after that it prints the next call (which is printf("Hello");)
int i;
for(i=0;i<5;i--);
We don't know anything about your C implementation.
For more about the C language, see this reference and later the C11 standard n1570. Read also Modern C
My guess would be that you use a recent GCC compiler on a x86-64 computer. I recommend reading the documentation of your optimizing compiler, for GCC it is here. You could also need to read the documentation of your linker, so (on my Linux computer) of binutils.
If indeed that is the case, I recommend enabling all warning and debug info, so compile your code with gcc -Wall -Wextra -g. You are likely to get some warnings.
You are decrementing i. Assume that int are 32 bits. Then on the first loop, i is 0; on the second loop, i becomes -1 .... Your computer probably will loop 231 times. So about two billions loops.
Computers are fast, but not infinitely fast.
My recommendation: learn to use a debugger, such as GDB.
At last, <stdio.h> gives buffered input output. So learn to use fflush(3) and read of course the documentation of printf(3).
for(i=0;i<5;i--);
Well I don't know how you got Hello even after 10 seconds! Take a closer look at the for loop. It actually never ends because the i value is constantly decreasing and hence it will always be less than 5. So I suppose the process was terminated.
Now don't think that just because you added a semi colon after the for loop the for loop won't run. It will run. Looping will take place, its just that there is no code to execute. The semi colon will be considered as a null statement by the compiler. And once the condition will return false the compiler will move on to the next statement.
Please check whether the code is correct or not. I highly doubt that it is ++i and not --i
Since the decrement (i.e. i--) will be executed till infinity for i < 5 never meets, as soon as the iterating integer i reaches the minimum value that an integer could hold (i.e. -2,147,483,648) get overflowed and it instantly quits the loop at negative 2147483648-th iteration.
If you write something like this:
int i;
long long j = 0; // intentionally using to hold lesser than the value
// and integer could hold for debugging test
for(i = 0; i < 5; i--)
j = i;
printf("%d\n", j);
Then you'll get to know practically in which iteration the loop is quitting and the reason behind getting a few time to print Hello after that loop.

Function optimized to infinite loop at 'gcc -O2'

Context
I was asked the following puzzle by one of my friends:
void fn(void)
{
/* write something after this comment so that the program output is 10 */
/* write something before this comment */
}
int main()
{
int i = 5;
fn();
printf("%d\n", i);
return 0;
}
I know there can be multiple solutions, some involving macro and some assuming something about the implementation and violating C.
One particular solution I was interested in is to make certain assumptions about stack and write following code: (I understand it is undefined behavior, but may work as expected on many implementations)
void fn(void)
{
/* write something after this comment so that the program output is 10 */
int a[1] = {0};
int j = 0;
while(a[j] != 5) ++j; /* Search stack until you find 5 */
a[j] = 10; /* Overwrite it with 10 */
/* write something before this comment */
}
Problem
This program worked fine in MSVC and gcc without optimization. But when I compiled it with gcc -O2 flag or tried on ideone, it loops infinitely in function fn.
My Observation
When I compiled the file with gcc -S vs gcc -S -O2 and compared, it clearly shows gcc kept an infinite loop in function fn.
Question
I understand because the code invokes undefined behavior, one can not call it a bug. But why and how does compiler analyze the behavior and leave an infinite loop at O2?
Many people commented to know the behavior if some of the variables are changed to volatile. The result as expected is:
If i or j is changed to volatile, program behavior remains same.
If array a is made volatile, program does not suffer infinite loop.
Moreover if I apply the following patch
- int a[1] = {0};
+ int aa[1] = {0};
+ int *a = aa;
The program behavior remains same (infinite loop)
If I compile the code with gcc -O2 -fdump-tree-optimized, I get the following intermediate file:
;; Function fn (fn) (executed once)
Removing basic block 3
fn ()
{
<bb 2>:
<bb 3>:
goto <bb 3>;
}
;; Function main (main) (executed once)
main ()
{
<bb 2>:
fn ();
}
Invalid sum of incoming frequencies 0, should be 10000
This verifies the assertions made after the answers below.
This is undefined behavior so the compiler can really do anything at all, we can find a similar example in GCC pre-4.8 Breaks Broken SPEC 2006 Benchmarks, where gcc takes a loop with undefined behavior and optimizes it to:
L2:
jmp .L2
The article says (emphasis mine):
Of course this is an infinite loop. Since SATD() unconditionally
executes undefined behavior (it’s a type 3 function), any
translation (or none at all) is perfectly acceptable behavior for a
correct C compiler. The undefined behavior is accessing d[16] just
before exiting the loop. In C99 it is legal to create a pointer to
an element one position past the end of the array, but that pointer
must not be dereferenced. Similarly, the array cell one element past
the end of the array must not be accessed.
which if we examine your program with godbolt we see:
fn:
.L2:
jmp .L2
The logic being used by the optimizer probably goes something like this:
All the elements of a are initialized to zero
a is never modified before or within the loop
So a[j] != 5 is always true -> infinite loop
Because of the infinite, the a[j] = 10; is unreachable and so that can be optimized away, so can a and j since they are no longer needed to determine the loop condition.
which is similar to the case in the article which given:
int d[16];
analyzes the following loop:
for (dd=d[k=0]; k<16; dd=d[++k])
like this:
upon seeing d[++k], is permitted to assume that the incremented value
of k is within the array bounds, since otherwise undefined behavior
occurs. For the code here, GCC can infer that k is in the range 0..15.
A bit later, when GCC sees k<16, it says to itself: “Aha– that
expression is always true, so we have an infinite loop.”
Perhaps an interesting secondary point, is whether an infinite loop is considered observable behavior(w.r.t. to the as-if rule) or not, which effects whether an infinite loop can also be optimized away. We can see from C Compilers Disprove Fermat’s Last Theorem that before C11 there was at least some room for interpretation:
Many knowledgeable people (including me) read this as saying that the
termination behavior of a program must not be changed. Obviously some
compiler writers disagree, or else don’t believe that it matters. The
fact that reasonable people disagree on the interpretation would seem
to indicate that the C standard is flawed.
C11 adds clarification to section 6.8.5 Iteration statements and is covered in more detail in this answer.
In the optimized version, the compiler has decided a few things:
The array a doesn't change before that test.
The array a doesn't contain a 5.
Therefore, we can rewrite the code as:
void fn(void) {
int a[1] = {0};
int j = 0;
while(true) ++j;
a[j] = 10;
}
Now, we can make further decisions:
All the code after the while loop is dead code (unreachable).
j is written but never read. So we can get rid of it.
a is never read.
At this point, your code has been reduced to:
void fn(void) {
int a[1] = {0};
while(true);
}
And we can make the note that a is now never read, so let's get rid of it as well:
void fn(void) {
while(true);
}
Now, the unoptimized code:
In unoptimized generated code, the array will remain in memory. And you'll literally walk it at runtime. And it's possible that there will be a 5 thats readable after it once you walk past the end of the array.
Which is why the unoptimized version sometimes doesn't crash and burn.
If the loop does get optimized out into an infinite loop, it could be due to static code analyzis seeing that your array is
not volatile
contains only 0
never gets written to
and thus it is not possible for it to contain the number 5. Which means an infinite loop.
Even if it didn't do this, your approach could fail easily. For example, it's possible that some compiler would optimize your code without making your loop infinite, but would stuff the contents of i into a register, making it unavailable from the stack.
As a side note, I bet what your friend actually expected was this:
void fn(void)
{
/* write something after this comment so that the program output is 10 */
printf("10\n"); /* Output 10 */
while(1); /* Endless loop, function won't return, i won't be output */
/* write something before this comment */
}
or this (if stdlib.h is included):
void fn(void)
{
/* write something after this comment so that the program output is 10 */
printf("10\n"); /* Output 10 */
exit(0); /* Exit gracefully */
/* write something before this comment */
}

For loop works when I write "i == 0" but not when "i = 0" in C?

I was trying to write a program that lists the factors of a given number. It works when I write this:
int main () {
int num, i;
printf("\nEnter a number: ");
scanf("%d", &num);
for(i == 0; i <= num; ++i) {
if (num % i == 0) {
printf("\n\t%d", i);
}
}
}
but not when the for loop is
for(i = 0; i <= num; ++i)
I'm confused as I thought this was the format of a for loop. Could someone point out my error please?
You should start the loop in this case with i=1 as in for(i = 1; i <= num; ++i), otherwise you are trying to divide a number by 0. Factor can never be 0.
i == 0 is an expression with no side effects, so writing for(i == 0; i <= num; ++i) is the same as writing for(; i <= num; ++i), i.e. it just does nothing in the initialization part. Now since you never initialize i anywhere this means that you invoke undefined behaviour.
If you do i = 0, i is initialized, but you still invoke undefined behaviour: num % i will cause division by zero because i will be 0 at the beginning.
Now it happened that on your system the division by zero caused your program to crash, while the version where you used i uninitialized happened to run without crashing. That's why it may have appeared to you that the version using i==0 "worked".
In C there are two separate operators that look somewhat similar - an assignment statement which uses a single equal sign, and an equality comparison, which uses two equal signs. The two should not be confused (although very often they are).
i == 0 is a comparison with zero, not an assignment of zero. It is a valid expression, though, so the compiler does not complain: for loop allows any kind of expression to be in its header, as long as the expression is formed correctly.
However, your code would not work, because it has undefined behavior: i remains uninitialized throughout the loop, making your program invalid.
The first part of the for loop is defining the starting value of your iterator (i).
Since you are not initializing it, it could have an undefined value, or sometimes just garbage value...
So when you use i==0 you're not assigning the value to i, just checking if it's equal to 0.
The value of i when not defined is usually very random, but it can have the integer max value, so incrementing it once actually makes it turn to the initger minimum value.
So actually the correct for is:
for(i=0; i<something; i++) // or ++i, doesn't mater
EDIT: oh yeah... and sinc the starting value of the for loop is 0, the something % i is equal to something % 0 which means you're making a division by zero.
EDIT 2: i see it was already mentioned...

Trailing zeroes of Factorial, Runtime error in C?

#include<stdio.h>
int main()
{
long ctr[100000],i;
float j;
long d[100000],T,h,o;
scanf("%ld",T);
printf("\n");
for(i=0; i<T; i++)
{
scanf("%ld",d[i]);
printf("\n");
for(h=d[i]; h<=0; h--)
{
j=h%10;
if(j==5)
{
ctr[i]++;
}
}
}
for(o=0; o<=i; o++)
{
printf("%ld\n",o);
}
return 0;
}
It's a program to find the number of trailing zeros of a factorial of a group of "T" numbers, input by the user.
What I have done is to divide each number by 10 and test if the number is 5. Then I decrement the number by 1 until it reaches 0. Each pair of 5*4 contributes one trailing 0.
Is the program consuming too much memory or is there another runtime error? This program is giving a runtime error, can anyone help?
A couple of observations:
long[100000] requires 400,000 bytes of stack assuming long is plain old 32 bits, 800,000 bytes if long is 64-bit. You have two such arrays, which might make your program hit operating system stack size limits.
The scanf() function requires pointers to where to store values it's reading in. You're not giving it pointers. As Paul R said in a comment.
Assuming positive numbers are input, the loop for(h=d[i]; h<=0; h--) will never run its body.
Hmm... This loop looks like the loop is going infinity.
for(h=d[i]; h<=0; h--)
Thus giving you a runtime error... Because the count is going negative, and your loops condition says continue until the value reaches less than or equal to zero, but it seems like, your value would never reach zero.
And for the scanf function, don't forget to use the & sign. Like this:
scanf("%ld", &sampleVariable);
That's the solution for your runtime error. :)
And also maybe you should use High Precision Variables, I think you need a higher precision more than the double datatype because you'll be dealing with huge amount of numbers.
See here for added info: http://www.nongnu.org/hpalib/
The loop is going infinity. Ensure that the count is not going negative or else you'll have a runtime error.

Resources