Mr Pang
Mr Pang

Reputation: 1181

When are local variables initialized in C?

Consider below example

void func(int i){
  if(i) {
    int arr[2048] = {0};
    //Doing something related to arr;
  } else {
    //Doing something
  }
}

I have a large array declaration in the if block. The initialization of this array should cost some time. My question is: if i == 0, will this array be initialized at all?

Upvotes: 2

Views: 2530

Answers (3)

Lundin
Lundin

Reputation: 213276

In practice, a variable will be initialized before use, regardless if it is placed in an inner scope or not. This code:

void func1 (int i){
  if(i) {
    int arr[2048] = {0};
    printf("%d", arr[666]);
  } else {
    //Doing something
  }
}

gives exactly the same machine code as this code:

void func2 (int i){
  int arr[2048] = {0};
  if(i) {
    printf("%d", arr[666]);
  } else {
    //Doing something
  }
}

gcc -O3 on x86 gives:

.LC0:
        .string "%d"
func1:
        test    edi, edi
        jne     .L4
        ret
.L4:
        xor     esi, esi
        mov     edi, OFFSET FLAT:.LC0
        xor     eax, eax
        jmp     printf

and

.LC0:
        .string "%d"
func2:
        test    edi, edi
        jne     .L7
        ret
.L7:
        xor     esi, esi
        mov     edi, OFFSET FLAT:.LC0
        xor     eax, eax
        jmp     printf

As you can see they are identical.

It is good design practice to limit the scope of variables as much as possible though, but that has nothing to do with performance.

Upvotes: 1

Frankie_C
Frankie_C

Reputation: 4877

To understand the compiler behavior you should consider that in the C language each variable has a storage class (ISO/IEC 9899:201x §6.2.4 Storage durations of objects) which characterize it behavior and its 'life', that means the existence of such an object (a variable is an object), and the legal access conditions to it. The storage classes are 4: static, thread, automatic, and allocated. The latter use dynamic memory allocation.

In your case the array arr[2048] is an automatic object, which lifetime is defined (in the same paragraph of the standard @ point 6) as:

For such an object that does not have a variable length array type, its lifetime extends from entry into the block with which it is associated until execution of that block ends in any way. (Entering an enclosed block or calling a function suspends, but does not end, execution of the current block.)

If the block is entered recursively, a new instance of the object is created each time.

The initial value of the object is indeterminate.

If an initialization is specified for the object, it is performed each time the declaration or compound literal is reached in the execution of the block; otherwise, the value becomes indeterminate each time the declaration is reached.

This explain that:

  1. The life of the object begins with the beginning of the block where it is defined.
  2. The initial value (array contents in our case) is indefinite: the compiler doesn't initialize the memory area. And when an initialization is specified it is done when the execution reach the block.

The first point is clear, and is already the answer to your question. The code you reference is:

{    //Block init
  int arr[2048] = {0};
  //Doing something related to arr;
}    // block end

If you don't enter the block the life of your object, the array, doesn't begin: the array doesn't exist. Of course under this condition no operations can be performed on the object, even initialization.

Now the point 2 helps to clarify better. The expression:

int arr[2048] = {0};

Isn't functionally interpreted by the compiler as a declaration with object initialization, because of the storage class of the array object. It is, substantially, treated moreover as a declaration plus an assignment.

What's the difference?

A declaration of an initialized variable having storage class different from automatic is implemented with a mechanism transparent to the user code, statically assigning values in the BSS section or with code belonging to the compiler prologues and epilogues, that could happen even if the object is not accessed.

In the other case the initialization code, the assignment, is part of the user code, and for this reason executed following the execution flow logic.

This is the official behavior. Inspecting under the hood you can see that in some cases the space for automatic variables happen to be allocated in advance from the object life beginning, but these behavior are strictly depending on the CPU architecture, and basically when happen don't create functional divergence between the code the language standard (which is the basic property of a compliant compiler).

Upvotes: 3

bruno
bruno

Reputation: 32586

if i == 0, will this array be initialized at all?

because your code is

if(i) {
  int arr[2048] = {0};
  //Doing something related to arr;
} else {
  //Doing something
}

the array does not exist if i==0 so it cannot be initialized, the array only exists in the branch of the if where i != 0

Upvotes: 6

Related Questions