Local Variables

From Nutwiki
Revision as of 17:02, 27 October 2016 by Harald (Talk | contribs) (1 revision imported)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Test Environment

Hardware Comments Nut/OS
4.8.3
Bad example
Nut/OS
4.8.3
Good example
Nut/OS
4.8.7
Bad example
Nut/OS
4.8.7
Good example
Ethernut 1.3 H OK
Binaries
Compiler: AVR-GCC 4.3.2
OK
Binaries
Compiler: AVR-GCC 4.3.2
OK
Binaries
Compiler: AVR-GCC 4.3.2
OK
Binaries
Compiler: AVR-GCC 4.3.2
Ethernut 2.1 B OK
Binaries
Compiler: AVR-GCC 4.3.2
OK
Binaries
Compiler: AVR-GCC 4.3.2
OK
Binaries
Compiler: AVR-GCC 4.3.2
OK
Binaries
Compiler: AVR-GCC 4.3.2
Ethernut 3.0 E OK
Binaries
Compiler: ARM-GCC 4.3.3
OK
Binaries
Compiler: ARM-GCC 4.3.3
OK
Binaries
Compiler: ARM-GCC 4.3.3
OK
Binaries
Compiler: ARM-GCC 4.3.3

Description

These 2 examples demonstrate how to properly handle local variables of an arbitrary size.

IMPORTANT: The first source code does crash your board when you run it. This is intentional and done solely for demonstrating the dangers of huge local variables.

The second example does exactly the same thing but with malloc instead of a predeclared array.

Source Code

THIS IS AN INTENTIONALLY BAD EXAMPLE! View the second source code snippet below to see how this is done the right way. <source lang="c">

  1. include <dev/board.h>
  2. include <stdio.h>
  3. include <stdlib.h>
  4. include <string.h>
  5. include <io.h>
  1. include <errno.h>

void badFunction(void) {

   int bad_local_array[4096];
   memset(bad_local_array, 0, sizeof(bad_local_array));

}

int main(void) {

   unsigned long baud = 115200;
   NutRegisterDevice(&DEV_DEBUG, 0, 0);
   freopen(DEV_DEBUG_NAME, "w", stdout);
   _ioctl(_fileno(stdout), UART_SETSPEED, &baud);
   printf("Local variables: A bad example.\nTHIS WILL CRASH YOUR APPLICATION AND IS AN EXAMPLE OF WHAT NOT TO DO!\n\n");
   printf("We will now call the function badFunction. This will likely cause a stack overflow and crash your board.\n");
   badFunction();
   printf("You made it through. Lucky!");
   for (;;);
   return 0;

} </source>

<source lang="c">

  1. include <dev/board.h>
  2. include <stdio.h>
  3. include <stdlib.h>
  4. include <string.h>
  5. include <io.h>
  1. include <errno.h>

void goodFunction(void) {

   int *good_local_array;
   good_local_array = malloc(4096 * sizeof(int));

}

int main(void) {

   unsigned long baud = 115200;
   NutRegisterDevice(&DEV_DEBUG, 0, 0);
   freopen(DEV_DEBUG_NAME, "w", stdout);
   _ioctl(_fileno(stdout), UART_SETSPEED, &baud);
   printf("Local variables: A better example.\n\n");
   printf
       ("We will now call the function goodFunction. Unlike the previous example this will not crash your board because it allocates it's own memory.\n");
   goodFunction();
   printf("See? This time we get to do things after the function call because we did not crash!");
   for (;;);
   return 0;

} </source>

Details

Bad example

<source lang="c"> void badFunction(void) {

   int bad_local_array[4096];
   memset(bad_local_array, 0, sizeof(bad_local_array));

} </source> We declare an array of 4096 ints. This is too large for our stack and causes our program to overwrite it's own stack pointer. This means that the program can't remember where it has to jump after calling the function and subsequently crashes. Notice the final printf after the call to badFunction. It is never called because our program never returns to that point.

You can store small amounts of data in a local variable but for bigger chunks you should always use malloc as demonstrated in the better example.

Good example

<source lang="c"> void goodFunction(void) {

   int *good_local_array;
   good_local_array = malloc(4096 * sizeof(int));

} </source>

This piece of code does exactly the same thing with one major important difference: The memory used is dynamically allocated. This way we only place a 2 byte pointer in the stack memory and the rest is located wherever malloc found as much as RAM for us to use as we need. Another advantage is that malloc will generate errors if it fails so you can check for them before actually accessing the array.

As you can see in the output, this time the printf after the function call works.

Output

Bad example

<source lang="text"> Local variables: A bad example. THIS WILL CRASH YOUR APPLICATION AND IS AN EXAMPLE OF WHAT NOT TO DO!

We will now call the function badFunction. This will likely cause a stack overfl ow and crash your board. </source>

Good example

<source lang="text"> Local variables: A better example.

We will now call the function goodFunction. Unlike the previous example this wil l not crash your board because it allocates it's own memory. See? This time we get to do things after the function call because we did not cr ash! </source>

See also





Template:Languages