How does C99 work in relation to C90 for declaring variables in the middle of the code?

Asked

Viewed 2,240 times

11

At C90 and C89 we have to loop for must have its variable declared earlier at the beginning of the function scope, example:

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

For C99, we can do it this way:

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

This was an example with the for, but this occurs with any variable declaration. I read that this update came to improve performance, declaring the variable only when used, so the program initializes occupying less memory.

How does this statement work ? The program itself makes a sort of malloc during the run ?

2 answers

11


Not quite so, has several wrong assumptions in the question.

What hasn’t changed

This is an issue that has not changed the implementation of the programme 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 no less memory consumption. So little is there worsening or memory increase. There is no malloc() implicit placed by the compiler. Nor would there be any reason to have.

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

Performance improvements can be obtained by the compiler independent of it being initial ANSI, C89, C90, C99, C11, C18 or any other language standard. Standard language and compiler version are different things. The compiler is free to do whatever he wants as long as he follows the language specification (one of the ones listed above).

The fact that you declare a variable earlier at the start of the function or closer to its initial use does not change the program at all. It may change if in addition to the statement the attribution is also made, but then the attribution changes, in some cases.

What changes

Declaration is only a reserve of space for the variable. Note that I said for the variable and not for her object.

Stack X heap

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

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

This space reserved in stack is done right at the start of the function execution. In every C compiler it is like this, no matter what version of the specification it is compatible, because the specification doesn’t say anything about it, but it is something that in practice is like this.

Note that in C99 the reservation is still made at the beginning of the function execution (this is implementation detail, but all compilers act like this). With C99 only slightly changed the syntax allowing the statements can be made outside the beginning of the scope block, but it’s only syntactic change. These reservations are made on stack frames. Note that this reservation is not an allocation. O stack is already all in memory. Compiler optimizations can change this flow a little if he decides it is advantageous.

Stack Frame

The assignment is made at the moment it appears in the code. If now the assignment is in the middle of the function, it is clear that the assignment will occur at that time. So if the attribution depends on something done before there will be no problem. Therefore today it is very common to declare and assign a value to the variable at the same time. Before compilers were able to do this (and some were able even before the standard existed since standards usually make official what compilers already do) it was common for the programmer to declare the variable and only assign at the moment ahead when he had everything as he needed to do this, thus saving a memory access

So there was no fundamental change in language. It was another change in the way of compiling, bringing only the advantage of better organization of the code since it is always better to declare the variable closest to its use, and if possible, in a smaller scope.

And with this change it was possible to take better advantage of memory, although this has little relevance.

In your example the memory reserve is made in C89 right at the beginning of the start of the execution of the function. In the example of C99 it is done only at the beginning of the for. This brings the advantage that she will be "released" at the very end of for instead of the end of the function. This stack can be reused by the function just after the end of the for and the variable name can also be reused although this is rarely something interesting to do. The advantage of reusing the space of the stack rarely is anything relevant.

Completion

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

6

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

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 is created a pointer called fim_da_stack and it indicates that everything after it is empty. When any function is called, it knows that memory right after fim_da_stack is available, so if you want to allocate 20 bytes, just do it first fim_da_stack += 20 to indicate to future functions that that memory is no longer available, and then just use memory. At the time of the return is made fim_da_stack -= 20 to "free" the memory. The important thing here is that getting more memory is much cheap, very fast.

Watch 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 so:

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 when entering the loop and destroyed when exiting the loop, this allows the variable to survive only as long as it is needed and not at all. It’s a good general rule to follow: create variables as late as possible and stop using them as soon as possible. This makes it easy to view the program because you programmer has fewer variables to keep in mind while thinking about the code.

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

Browser other questions tagged

You are not signed in. Login or sign up in order to post.