How does C99 work on the C90 for declaration of variables in the middle of the code?

9

In C90 and C89 we have that the loop for must have its variable declared earlier at the beginning of the function scope, eg:

void main (void)
{
    int i;
    for(i = 0; i < 10; i++)
    {
            /* Qualquer coisa */
    }
}

Now for C99 we can do this:

void main (void)
{
    for(int i = 0; i < 10; i++)
    {
            /* Qualquer coisa */
    }
}

This was an example with for , but this occurs with any variable declaration. I read that this update came to improve performance by declaring the variable only when it is used, so the program boots up taking up less memory.

How does this statement work? Does the program itself make a kind of malloc during execution?

    
asked by anonymous 26.01.2015 / 12:11

2 answers

9

Not quite, you have several wrong assumptions in your question.

What has not changed

This is an issue that has not changed program execution at all. This change is purely a new way for the compiler to work, but it does not generate different code because of this.

There is no improvement in performance and there is less memory consumption. Nor is there any worsening or increased memory. There is no malloc implicit placed by the compiler. There would be no reason to.

Variable declaration and the use of malloc are orthogonal things. One does not depend on the other. Only the programmer can invoke malloc , the compiler never does this.

Performance enhancements can be obtained by the compiler independent of it if initial ANSI, C89, C90, C99, C11 or any other standard of the language. Language pattern and compiler version are different things. The compiler is free to do what he wants as long as he follows the language specification. One of these versions of the specification listed.

The fact that you declare a variable before the start of the function or closer to its initial use does not change the program at all. It may change slightly if the assignment is done in addition to the declaration.

What changes

Statement is just a reservation of space for the variable. Note that I said to the variable and not to the object of it.

Stack X heap

Many variables have their object merging with their own storage location. Case of all primary types such as int , char , float , even data sequence as structures and arrays allocated in stack .

Types that use pointers will store the pointer at the location of the variable and will likely allocate the main object to another area, possibly in heap through malloc (explicitly).

This placeholder in stack is done at the beginning of the function execution. In any version of the specification it is so.

With C99 this has changed a bit and now some stack reservations may occur during the execution of the function, they can be made at the beginning of the scope block. These reservations are made at stack frames . Note that this reservation is not an allocation. The stack is already all in memory. Compiler optimizations may change this stream a bit if it decides it is advantageous.

Theassignmentismadethemomentitappearsinthecode.Iftheassignmentisnowinthemiddleofthefunction,itisclearthattheassignmentwilloccuratthattime.Soiftheassignmentdependsonsomethingdonebefore,therewillbenoproblem.Forthisreason,itisverycommontodaytodeclareandassignavaluetothevariableatthesametime.Beforecompilerswereabletodothis(andsomewerealreadycapableevenbeforethedefaultexistssincestandardsusuallymakewhatcompilersalreadydo)itwascommonfortheprogrammertodeclarethevariableandonlyassignitatatimewhenithadeverythingasheneeded.

Sotherewasnofundamentalchangeinlanguage.Itwasanotherchangeinthewayofcompiling.Thishasonlyhadtheadvantageofbettercodeorganizationsinceitisalwaysbettertodeclarethevariableclosesttoitsuse,andifpossible,inasmallerscope.

Andwiththischangeitwaspossibletotakebetteradvantageofmemory,althoughthishaslittlerelevance.

InyourexamplethememoryreservationismadeinC89atthebeginningofthebeginningofthefunctionexecution.IntheC99example,itisdoneonlyatthebeginningoftheforblock.Thishastheadvantagethatitwillbe"freed" at the very end of for instead of the end of the function. This space in the stack can be reused by the function just after the end of for and the variable name can also be reused although this is seldom an interesting thing to do. The advantage of reusing the stack space is rarely relevant.

Conclude

This is a feature introduced to make life easier for the programmer. This is important. With the C99 form the programmer manages the code better, understands better what he is doing, keeps in a smaller region what he really needs to use. Internally, in essence, nothing has changed.

    
26.01.2015 / 12:39
5

Previously to C89 it was expected that the programmer already defined at the beginning of the function all the variables that he would use. In this case they were immediately allocated at the opening of the function. Notice that I used the word "allocated" here, but it does not do much justice to what really happens. A brief explanation of how STACK works:

At the beginning of the program a large block of memory is allocated to be the STACK, you can consider that this is done via malloc . At the beginning of this block a pointer named fim_da_stack is created and it indicates that everything after it is empty. When any function is called, it knows that memory shortly after fim_da_stack is available, so if you want to allocate 20 bytes, first make fim_da_stack += 20 to indicate for future functions that memory is no longer available, and then just use the memory. At the time of return is done fim_da_stack -= 20 to "free" the memory. The important thing here is that getting more memory is very cheap, very fast.

Notice what happens:

int sum(int* list, int size) {
  int result = 0;
  int i;
  // aqui estamos usando 8 bytes
  for (i = 0; i < size; i++) {
    result += list[i];
    // aqui ainda estamos usando 8 bytes
  }
  // aqui continuamos usando 8 bytes
  return result;
}

But if you do this:

int sum(int* list, int size) {
  int result = 0;
  // aqui estamos usando 4 bytes
  for (int i = 0; i < size; i++) {
    result += list[i];
    // aqui estamos usando 8 bytes
  }
  // aqui voltamos usando 4 bytes
  return result;
}

The difference is that the loop variable is created by entering the loop and destroyed on exit from the loop, this allows the variable to survive only as long as it is needed and not much more than that. It's a good general rule to follow: create the variables as late as possible and stop using them as early as possible. This makes it easy to see the program because you programmer has fewer variables to keep in mind while thinking about the code.

And as a general rule do not worry so much about speed in these cases, the compiler is pretty smart and can do things like allocate the variables in advance or use the same memory for two different variables that are not used at the same time. The two examples above, for example, are considered equivalent for the compiler.

    
26.01.2015 / 12:40