8

If I define an array in if statement then does memory gets allocated during compile time eg.

if(1)
{
    int a[1000];
} 
else
{
    float b[1000];
}

Then a memory of 2 * 1000 for ints + 4 * 1000 for floats get allocated?

Grijesh Chauhan
  • 57,103
  • 20
  • 141
  • 208
user2738599
  • 81
  • 1
  • 3
  • 3
    Why not look at the code your compiler generates to find out? – Carl Norum Sep 02 '13 at 04:49
  • 4
    @CarlNorum maybe because not everybody know assembler? – Kolyunya Sep 02 '13 at 05:17
  • In this case that's not really a good answer. You're going to have to learn if you want to know what's going on. The behaviour here can't be determined by guessing based on a language standard, since the language doesn't specify it well enough. At the very least, breaking out the ABI and/or procedure call standard is going to be necessary. – Carl Norum Sep 02 '13 at 14:40

5 Answers5

5

It is reserved on the stack at run-time (assuming a non-trivial condition - in your case, the compiler would just exclude the else part). That means it only exists inside the scope block (between the {}).

paddy
  • 60,864
  • 6
  • 61
  • 103
  • 1
    Would it be **run-time** or **compile-time**? Static Memory Allocation is done at compile time. – 0xF1 Sep 02 '13 at 04:55
  • 3
    @nishant: the memory needed is calculated at compile-time, but it gets actually allocated at runtime, at the beginning of the function. – DCoder Sep 02 '13 at 05:02
  • @DCoder : Thanks, for resolving my confusion. Also I have corrected my answer. – 0xF1 Sep 02 '13 at 05:06
  • 5
    @nishant It is run-time, although the actual size of the memory is set at compile-time. All that needs to happen when you enter a scope block is to offset the stack pointer by however much memory is required. The compiler creates the instructions to do that, and to remember the start address of the array until it goes out of scope. But it doesn't know where the stack pointer will be until run-time, so you don't get the memory until you enter the scope block. Maybe a compiler will optimize the memory to go outside the block, but at worst it would not create both arrays - only the largest. – paddy Sep 02 '13 at 05:07
  • @paddy : Thanks for the details. I got your point, it's not possible for compiler, until runtime, to know whether the memory allocation is required or not. – 0xF1 Sep 02 '13 at 05:09
1

In your example, only the memory for the ints gets allocated on the stack (1000 * sizeof(int)).

As you can guess, this is happening at run time. The generated code has instructions to allocate the space on the stack when the corresponding block of code is entered.

Keep in mind that this is happening because of the semantics of the language. The block structure introduces a new scope, and any automatic variables allocated in that scope have a lifetime that lasts as long as the scope does. In C, this is implemented by allocating it on the stack, which collapses as the scope disappears.

Just to drive home the point, note that the allocation would be different had the variables been of different nature.

if(1)
 {
  static int a[1000];
 } 
else
 {
  static float b[1000];
 }

In this case, space is allocated for both the ints and the floats. The lifetime of these variables is the program. But the visibility is within the block scope they are allocated in.

Ziffusion
  • 8,779
  • 4
  • 29
  • 57
  • Some ABIs require that the stack pointer is only decremented once per function invocation to assist exception unwinding code. – Carl Norum Sep 02 '13 at 14:43
  • Good point. But those are implementation specifics. For matters like these, one would go by the semantics of the language, don't you think? – Ziffusion Sep 02 '13 at 17:56
  • Well, you can't answer a question that has implementation-defined behaviour by talking about what the standard says. – Carl Norum Sep 02 '13 at 18:03
  • Why not? In many cases, what matters is what you *should* count on i.e. what the language promises. Because that is the least common denominator. In this case, what you should count on is the assumption that the lifetime of the variable is the lifetime of the block scope, as it is guaranteed to be at least that in all implementations of the language. – Ziffusion Sep 02 '13 at 18:16
  • And none of that has anything to do with the implementation detail the OP is asking about - namely when/if the allocation happens and how big it will be. When it comes to semantics, you're absolutely right. – Carl Norum Sep 02 '13 at 18:22
1

Scope

Variables declared inside the scope of a pair of { } are on the stack. This applies to variables declared at the beginning of a function or in any pair of { } within the function.

int myfunc()
{
   int i = 0;   // On the stack, scoped: myfunc

   printf("%i\n");

   if (1)
   {
       int j = 1;   // On the stack, scope: this if statement

       printf("%i %i\n",i,j);
   }

   printf("%i %i\n",i,j);   // Won't work, no j
}

These days the scope of the variables is limited to the surrounding { }. I recall that some older Microsoft compilers didn't limit the scope, and that in the example above the final printf() would compile.

So Where is it in memory?

The memory of i and j is merely reserved on the stack. This is not the same as memory allocation done with malloc(). That is important, because calling malloc() is very slow in comparison. Also with memory dynamically allocated using malloc() you have to call free().

In effect the compiler knows ahead of time what space is needed for a function's variables and will generate code that refers to memory relative to whatever the stack pointer is when myfunc() is called. So long as the stack is big enough (2MBytes normally, depends on the OS), all is good.

Stack overflow occurs in the situation where myfunc() is called with the stack pointer already close to the end of the stack (i.e. myfunc() is called by a function which in turn had been called by another which it self was called by yet another, etc. Each layer of nested calls to functions moves the stack pointer on a bit more, and is only moved back when functions return).

If the space between the stack pointer and the end of the stack isn't big enough to hold all the variables that are declared in myfunc(), the code for myfunc() will simply try to use locations beyond the end of the stack. That is almost always a bad thing, and exactly how bad and how hard it is to notice that something has gone wrong depends on the operating system. On small embedded micro controllers it can be a nightmare as it usually means some other part of the program's data (eg global variables) get silently overwritten, and it can be very hard to debug. On bigger systems (Linux, Windows) the OS will tell you what's happened, or will merely make the stack bigger.

Runtime Efficiency Considerations

In the example above I'm assigning values to i and j. This does actually take up a small amount of runtime. j is assigned 1 only after evaluation of the if statement and subsequent branch into where j is declared.

Say for example the if statement hadn't evaluated as true; in that case j is never assigned 1. If j was declared at the start of myfunc() then it would always get assigned the value of 1 regardless of whether the if statement was true - a minor waste of time. But consider a less trivial example where a large array is declared an initialised; that would take more execution time.

int myfunc()
{
   int i = 0;           // On the stack, scoped: myfunc
   int k[10000] = {0}   // On the stack, scoped: myfunc. A complete waste of time
                        //  when  the if statement evaluates to false.

   printf("%i\n");

   if (0)
   {
       int j = 1;   // On the stack, scope: this if statement

       // It would be better to move the declaration of k to here
       // so that it is initialised only when the if evaluates to true.

       printf("%i %i %i\n",i,j,k[500]);

   }

   printf("%i %i\n",i,j);   // Won't work, no j
}

Placing the declaration of k at the top of myfunc() means that a loop 10,000 long is executed to initialise k every time myfunc() is called. However it never gets used, so that loop is a complete waste of time.

Of course, in these trivial examples compilers will optimise out the unnecessary code, etc. In real code where the compiler cannot predict ahead of time what the execution flow will be then things are left in place.

Grijesh Chauhan
  • 57,103
  • 20
  • 141
  • 208
bazza
  • 7,580
  • 15
  • 22
0

As DCoder & paddy corrected me, the memory will be calculated at compile time but allocated at run-time in stack memory segment, but with the scope & lifetime of the block in which the array is defined. The size of memory allocated depends on size of int & float in your system. Read this for an overview on C memory map

0xF1
  • 6,046
  • 2
  • 27
  • 50
  • Note that there is no such thing as a "C memory map". Maybe they meant "PC memory map" but forgot the 'P'. – Lundin Sep 02 '13 at 06:35
0

Memory for the array in the if block will be allocated on stack at run time. else part will be optimized (removed) by the compiler. For more on where the variables will be allocated memory, see Segmentation Fault when writing to a string

Community
  • 1
  • 1