I have recently started to learn C and I am taking a class with C as the subject. I\'m currently playing around with loops and I\'m running into some odd behaviour which I d
There are two things wrong here. The int i is actually an array element, array[10], as seen on the stack. Because you have allowed the indexing to actually make array[10] = 0, the loop index, i, will never exceed 10. Make it for(i=0; i<10; i+=1)
.
i++ is, as K&R would call it, 'bad style'. It is incrementing i by the size of i, not 1. i++ is for pointer math and i+=1 is for algebra. While this depends on the compiler, it is not a good convention for portability.
The bug lies between these pieces of code:
int array[10],i;
for (i = 0; i <=10 ; i++)
array[i]=0;
Since array
only has 10 elements, in the last iteration array[10] = 0;
is a buffer overflow. Buffer overflows are UNDEFINED BEHAVIOR, which means they might format your hard drive or cause demons to fly out of your nose.
It is fairly common for all stack variables to be laid out adjacent to each other. If i
is located where array[10]
writes to, then the UB will reset i
to 0
, thus leading to the unterminated loop.
To fix, change the loop condition to i < 10
.
Unlike Java, C doesn't do array boundary check, i.e, there's no ArrayIndexOutOfBoundsException
, the job of making sure the array index is valid is left to the programmer. Doing this on purpose leads to undefined behavior, anything could happen.
For an array:
int array[10]
indexes are only valid in the range 0
to 9
. However, you are trying to:
for (i = 0; i <=10 ; i++)
access array[10]
here, change the condition to i < 10
the error is in portion array[10] w/c is also address of i (int array[10],i;). when array[10] is set to 0 then the i would be 0 w/c resets the entire loop and causes the infinite loop. there will be infinite loop if array[10] is between 0-10.the correct loop should be for (i = 0; i <10 ; i++) {...} int array[10],i; for (i = 0; i <=10 ; i++) array[i]=0;
Beyond the possibility that memory might be laid out so that an attempt to write to a[10]
actually overwrites i
, it would also be possible that an optimizing compiler might determine that the loop test cannot be reached with a value of i
greater than ten without code having first accessed the non-existent array element a[10]
.
Since an attempt to access that element would be undefined behavior, the compiler would have no obligations with regard to what the program might do after that point. More specifically, since the compiler would have no obligation to generate code to check the loop index in any case where it might be greater than ten, it would have no obligation to generate code to check it at all; it could instead assume that the <=10
test will always yield true. Note that this would be true even if the code would read a[10]
rather than writing it.
On my laptop running Ubuntu 14.04, this code does not break it runs to completion. On my school's computer running CentOS 6.6, it also runs fine. On Windows 8.1, the loop never terminates.
What is more strange is when I edit the conditional of the
for
loop to:i <= 11
, the code only terminates on my laptop running Ubuntu. CentOS and Windows never terminates.
You've just discovered memory stomping. You can read more about it here: What is a “memory stomp”?
When you allocate int array[10],i;
, those variables go into memory (specifically, they're allocated on the stack, which is a block of memory associated with the function). array[]
and i
are probably adjacent to each other in memory. It seems that on Windows 8.1, i
is located at array[10]
. On CentOS, i
is located at array[11]
. And on Ubuntu, it's in neither spot (maybe it's at array[-1]
?).
Try adding these debugging statements to your code. You should notice that on iteration 10 or 11, array[i]
points at i
.
#include <stdio.h>
int main()
{
int array[10],i;
printf ("array: %p, &i: %p\n", array, &i);
printf ("i is offset %d from array\n", &i - array);
for (i = 0; i <=11 ; i++)
{
printf ("%d: Writing 0 to address %p\n", i, &array[i]);
array[i]=0; /*code should never terminate*/
}
return 0;
}