Memory allocation - arrays in C

Go To Last Post
8 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This new-fangled C stuff has me completely befuddled. Consider the following illustrative code:

int main(void)
{
    uint8_t red[6];
    uint8_t green[6];
    uint8_t blue[6];
    
    for (uint8_t i = 0; i < 6; i++)
    {
        red[i] = 0;
        green[i] = 0;
        blue[i] = 0;
    }
    
    while (1)
    {
        for (uint8_t i = 0; i < 6; i++)
        {
            red[i]++;
            green[i]++;
            blue[i]++;
        }
        
        // Do stuff with the values so the compiler doesn't optimize anything out
    }
}

I would have expected the compiler to use a register for i (which it does), and to allocate 18 bytes in the data segment for the three arrays (red, green, blue). When I compile this in the context of a complete program, it shows 0 bytes used in the data segment. When I run it in the simulator, I can see the array values changing in RAM, so I know it's doing what it's supposed to do. What concerns me is that there is no allocation shown in the data segment and that the values in the array are precipitously close to the end of RAM, while I'd expect them to be right at the beginning. The last byte of blue[] is only about 4 bytes from the end of RAM, and this makes me nervous about what's going to happen as the stack grows from the end of RAM. If I include stdlib.h and use malloc(), I can do this:

uint8_t *red = malloc(sizeof(uint8_t)*6);

That shows up as 6 bytes in the data segment, but the malloc() code uses about 600 bytes of program memory, and I want to run this on a Tiny13A, so...

 

1. Why doesn't the fixed-dimension array show up as being part of the data segment?

 

2. Why are the array values so close to the end of RAM instead of at the beginning?

 

3. Is the compiler smart enough to leave enough stack space for the maximum possible stack usage of a given program, or am I right to worry about my array getting clobbered by the stack?

 

4. Is it wrong to use an array in this context?

 

5. Is it mandatory to use malloc() in order to properly handle memory allocation in this scenario?

Last Edited: Fri. Jun 5, 2015 - 09:08 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

it shows 0 bytes used in the data segment.

Sorry but why are you surprised? Your variables are not .data or even .bss section variables. They are stack frame automatics and are not allocated by the linker. 

 

Suggest you get a good book on C programming if you don't understand this distinction. 

 

1 like I say they are local automatics, not globals

 

2 the end of RAM is where the stack is located, it's exactly where you'd expect them to be located. 

 

3 on entry to the function with autos the compiler does an SBIW on SP to make the allocation then sets Y (the frame pointer) to the base of the reserved (but not initialised) area

 

4 auto arrays are fine but don't get into the habit of making large local allocations unless you are ready to check for SP hitting .bss

 

5 on a limited RAM micro like AVR it's actually very unwise to use the heap if for no other reason than fragmentation. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Strictly speaking, there are occasions when the compiler could legitimately put automatic variables in a memory space for globals.

main's sutos are one possibility.

The compiler would have to know that main is will not be called recursively.

Such knowledge could come from a compiler option, a pragma, or from reading the whole program.

In the known absence of recursion, the compiler could put all variables in a memory space for globals.

I do not know of any compiler that does.

 

All that said, the norm is as stated:

local variables go on the stack and do not show up in most listings of data size requirements.

Nor should they.

Large arrays on the stack can be bad news.

Iluvatar is the better part of Valar.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
    volatile uint8_t red[6];
    volatile uint8_t green[6];
    volatile uint8_t blue[6];
    
int main(void)
{
    
    
    for (uint8_t i = 0; i < 6; i++)
    {
        red[i] = 0;
        green[i] = 0;
        blue[i] = 0;
    }
    
    while (1)
    {
        for (uint8_t i = 0; i < 6; i++)
        {
            red[i]++;
            green[i]++;
            blue[i]++;
        }
        
        // Do stuff with the values so the compiler doesn't optimize anything out
    }
}

declaring the vars outside a function makes them global, so the compiler allocates them memory. The volatile attribute is to ensure the compiler doesn't optimise them out as it might think you're not doing anything useful in your example.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Declaring them static inside main() would also mean that the compiler allocates them memory.

 

Kartman wrote:
The volatile attribute is to ensure the compiler doesn't optimise them out as it might think you're not doing anything useful in your example.

You missed this comment, then:

                // Do stuff with the values so the compiler doesn't optimize anything out

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Andy, your presumption was incorrect.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sorry to be a pedant but it's not the compiler that allocates memory for globals and statics, it's the linker. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My knowledge of C is limited to "making it work" without really considering what's going on internally, and my AVR projects have all been ASM until now. Thanks for the replies, they helped me to understand what's going on with the compiler.