Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Array base address is changing when declared inside loop

I declared an array inside for loop and tried printing its base address.

#include<stdio.h>

int main(){
  int n=16;
  for(int i=1;i<=n;i++){
    int a[i];
    int b[16];
    int c[n];
    printf("%p %p %p\n",(void *)a,(void *)b,(void *)c);
  }
  return 0;
}

The output goes as follow:

0x7fffe6191740 0x7fffe6191770 0x7fffe6191700
0x7fffe6191740 0x7fffe6191770 0x7fffe6191700
0x7fffe6191740 0x7fffe6191770 0x7fffe6191700
0x7fffe6191740 0x7fffe6191770 0x7fffe6191700
0x7fffe6191730 0x7fffe6191770 0x7fffe61916f0
0x7fffe6191730 0x7fffe6191770 0x7fffe61916f0
0x7fffe6191730 0x7fffe6191770 0x7fffe61916f0
0x7fffe6191730 0x7fffe6191770 0x7fffe61916f0
0x7fffe6191720 0x7fffe6191770 0x7fffe61916e0
0x7fffe6191720 0x7fffe6191770 0x7fffe61916e0
0x7fffe6191720 0x7fffe6191770 0x7fffe61916e0
0x7fffe6191720 0x7fffe6191770 0x7fffe61916e0
0x7fffe6191710 0x7fffe6191770 0x7fffe61916d0
0x7fffe6191710 0x7fffe6191770 0x7fffe61916d0
0x7fffe6191710 0x7fffe6191770 0x7fffe61916d0
0x7fffe6191710 0x7fffe6191770 0x7fffe61916d0

Why is the base address of array getting changed every time? Is memory allocated for every iteration. If so then why is the address not changing for 4 iterations?

Please explain the differences among a, b and c in declarations, memory allocations and base addresses.

like image 668
Guru Vishnu Vardhan Reddy Avatar asked Jun 21 '19 11:06

Guru Vishnu Vardhan Reddy


2 Answers

These arrays have automatic storage duration, and, conceptually, a new instance of each array is created each time the { … } statement inside the for loop is executed. Since, in the various iterations, you request different sizes for the array a, it is entirely reasonable that the C implementation puts it at a different place in memory, to allow room for its elements. Your C implementation appears to be using blocks of 16 bytes as a unit for how much memory it reserves for the array or for how it aligns it. This is likely a consequence of stack management, as the alignment or block size is likely not needed for the array a itself.

Quite possibly, the allocations of a, b and c are affected by the fact that, in the abstract computer specified by the C standard, the lifetime of b begins as soon as execution of the block begins, but the lifetimes of a and c begin when execution (“control”) reaches the statements that define them. This is because C 2018 6.2.4 says objects with automatic storage duration that do not have variable length begin life upon entry to the associated block (paragraph 6) and such objects that do have variable length begin life at the declaration (paragraph 7). Thus, as the code is written, b begins life first, then a, then c.

This order of allocation affects where c is put but not where b is put. Since b is created first, it is “earlier” on the stack (at a higher address, which means it gets an address not yet affected by a). Since c is created later, it is “later” on the stack (at a lower address, which means it gets an address that is affected by how big a is). This order is not technically required by the C standard, as the C implementation could arrange the locations as it pleases as long as the same results as defined by the C standard are obtained. However, it appears your implementation has followed the C’s abstract computer model faithfully, creating b first, then a, then c.

Additionally, the proper way to print addresses of objects is to use the %p format specification and to convert the addresses to void *:

printf("%p %p %p\n", (void *) a, (void *) b, (void *) c);
like image 90
Eric Postpischil Avatar answered Sep 28 '22 03:09

Eric Postpischil


The size of b is the same on every iteration of the loop. The compiler locates it once, and it stays put.

Both a and c are technically variable length arrays. The size of c doesn't change, but it appears that it is allocated at a lower address than a.

Your array a grows, so to keep the array clear of the array b, the start address has to be lower on the stack. And, because c is located below a on the stack, it moves too as a grows. The compiler appears to allocate the stack in quanta of 16 bytes. When the array runs into the other variables, its start is moved down the stack by another quantum of 16 bytes.

A clever compiler could spot that c is a fixed size during the loop and reorder it so it appears above a in the stack. Then only a would change address. Your compiler does not appear to be doing that.

The stack layout appears to be:

1st 4 cycles:

b 0x…901d0 
a 0x…901a0   gap to b is 0x30
c 0x…90160   gap to a is 0x40

2nd 4 cycles:

b 0x…901d0
a 0x…90190  gap to b is 0x40
c 0x…90150  gap to a is 0x40

3rd 4 cycles:

b 0x…901d0
a 0x…90180   gap to b is 0x50
c 0x…90140   gap to a is 0x40

This behaviour is completely at the discretion of the compiler. I don't have a good explanation of why the gap between a and b is so large when a has 1..4 entries. It could place the variables at different places every time. Technically, the arrays go out of scope when the loop ends; the variables are redefined on each loop cycle. None of them are initialized. Two of them can't be initialized; you cannot provide an initializer for a VLA.

like image 28
Jonathan Leffler Avatar answered Sep 28 '22 01:09

Jonathan Leffler