skip to Main Content

Why does the code below count from 0 to 9?
Does C store the variable i in test always at the same address? And when calling C initializes the variable i with its previous value?
Or is that just by random chance ?

I used Ubuntu with gcc.


#include <stdio.h>

void test(){
    int i; 
    printf("%dn", i);
    i++;
}


int main(int argc, char const *argv[])
{   
    for (int i = 0; i < 10; i++){
        test();
    }
    

    return 0;
}

/*

output: 

0
1
2
3
4
5
6
7
8
9

*/

I also created an array in between function calls to see if the memory might be overritten but that did not change anything.

3

Answers


  1. Actually the code has undefined behavior due to using the uninitialized variable i within the function test

    void test(){
        int i; 
        printf("%dn", i);
        i++;
    }
    

    But it occurred such a way that the memory where the variable i with automatic storage duration is allocated was not rewritten by other functions.

    Login or Signup to reply.
  2. It’s not legal to read an uninitialized variable. This is undefined behaviour, which means there’s no predicting what behaviour you’ll get next time.

    There’s a couple of explanations why you might get the behaviour you see. The most likely is that your environment uses a stack, the automatic variable i is found on the stack, the stack frame of every call to the function happens to be at the same place, and nothing overwrites that part of the stack between calls to test. In other words, you got very lucky.

    Again, you cannot count on getting the behaviour you observed, even from the same program.

    The correct way to achieve the shown effect is to use a variable in static or extern storage.

    void test( void ) {
        static int i = 0;
        printf( "%dn", i );
        i++;
    }
    

    or

    int i = 0;   // Short for `static int i = 0;`
    
    void test( void ) {
        printf( "%dn", i );
        i++;
    }
    

    or

    extern int i = 0;
    
    void test( void ) {
        printf( "%dn", i );
        i++;
    }
    
    Login or Signup to reply.
  3. Here is what I get

    21967
    21968
    21969
    21970
    21971
    21972
    21973
    21974
    21975
    21976
    

    So answer is, yes it is by random chance. Not pure random. There is a bias that makes this behavior quite probable. But not sure. So by chance in the sense that you are not supposed to count on any such behavior.

    The fact that your variable i in test was the same as i in main that is pure chance (totally unrelated to variable name being the same. It is not the same variable. And the names disappear at runtime. In fact, they disappear at the second stage of the classic 3 stages compilation). Well, biased also, because your i of main starts from 0, and, more often than not, an unitialized variable is 0 (not a rule. It just happens to be frequent). But you see, in my case, it was not so.
    If your i of main was iterating from 129391 to 129401, then probability to see the output of your code iterating precisely from 129391 to 129401 would have been near 0.

    The fact that the displays, in both cases (yours, starting from 0, and mine, starting from 21967) iterates 1 by 1, is also undefined (you can’t count on it). But yet, that one is almost a sure thing. At least with gcc and without too much optimization.

    Simply because the i of test is a variable in the stack. And the stack has no reason not to be at the same place at each run of test. Nothing else uses the stack in between. And there is no reason to reinitialize it (that would be a waste of CPU). So, each subsequent call of test finds its variable i where it let it.

    But, again,

    1. you can’t count on it. For example, if an heavy optimization realizes that i is uninitialized, it could decide to not even allocate memory for it. Or even to unroll loop in main.

    2. That is because nothing else touches the stack. Just try this

    #include <stdio.h>
    
    void test(){
        int i; 
        printf("%dn", i);
        i++;
    }
    
    void other(int x){
    }
    
    int main(int argc, char const *argv[])
    {   
        for (int i = 0; i < 10; i++){
            test();
            other(12);
        }
        
    
        return 0;
    }
    

    And now my output is

    22025
    12
    12
    12
    12
    12
    12
    12
    12
    12
    

    First time, I get the random value that was at this place in the stack before anyone use it. And that place also happens to be where argument of other is pushed. So it is reset to 12 each times.

    And that, also, you can’t count on it. There is no rule saying that this is how stack is supposed to be used.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search