I recently added some more variables to the stm32 project and noticed some global variables were overwritten after entering a function that declared a huge local variable.
Then I read the C compiler uses stack to alllocate local variables, so there's no way to check them at compile time.
Is that correct? So it only worked until now because I was lucky and the memory was not full?
A simple example, this compiles perfectly in a stm32F103 with 20KB Ram, although I'm allocating 64KB.
Of course, it crashes. I guess the only way for this to work is to use malloc and check a valid pointer?
void test(void){
volatile uint8_t dat[65536];
for(uint32_t t=0;t<sizeof(dat);t++){
dat[t]=GPIOA->IDR;
}
for(uint32_t t=0;t<sizeof(dat);t++){
GPIOB->ODR=dat[t];
}
}
Yes, local variables are allocated on the stack. That's standard. (Now depending on optimization levels, some local variables may be put in registers, but obviously if they can fit in them. A 64KB array will of course be allocated on the stack.)
To answer your question, first you need to know if you really need this array to be dynamically allocated. In small embedded stuff, it's pretty common that statically allocated arrays will work just fine. In particular, if your function is not meant to be re-entrant, you don't need a dynamic allocation. Now the rationale for dynamic allocation would be of course if you need this particular array only temporarily during the program's execution, and would like to be able to use the corresponding RAM for other dynamic allocations the rest of the time.
Using malloc() in embedded settings is often questioned, too. So you need to consider if the benefits really overweigh the risks.
Another point I would like to stress out regarding checking if the allocation succeeded: for small embedded targets, it's very common that implementations do not handle malloc() failure (in case of not enough RAM available) at all. So you may call malloc() with a large size, the allocation would be impossible, but it will still return a pointer. Often to the beginning of a free block, but the size of which will be much smaller than what you asked for. So you're basically screwed.
So, if you really settle on using malloc(), do check on your particular application that calling it with a size that you know won't fit in RAM effectively returns NULL. This is often not guaranteed on base setups for many MCUs. For this to properly work, you often have to write a custom linker script + implement the _sbrk() "system" function. But check it - if you're lucky and failed malloc() returns NULL in your case, then no need to bother; I'm just pointing out it rarely works "out of the box" with the vendor-provided linker scripts and libraries.
We had a whole thread about estimating stack usage. Since this is something not trivial (or sometimes impossible depending on code flow) to statically estimate (meaning: at compile and link time), it's pretty usual that no check, and thus no warning, will be issued by the linker if you use up too much stack.