Understanding the Significance of the Stack in Embedded Programming

Follow+Star Public Account, don’t miss out on exciting content

Understanding the Significance of the Stack in Embedded Programming

Author | Li Xiaoyao

Source | Technology Makes Dreams Greater

What is a Variable?

Variables can generally be subdivided as shown in the figure below:

Understanding the Significance of the Stack in Embedded Programming

The focus of this section is to help everyone understand the “stack” in the memory model, temporarily disregarding the case of “static variables” and agreeing on the following:

“Global variables” are simply assumed to be “ordinary global variables”;

“Local variables” are simply assumed to be “ordinary local variables”.

How to Determine Global and Local Variables?

Simply put, a global variable is defined outside of a function, while a local variable is defined inside a function. The following example clearly illustrates how to determine global and local variables:

unsigned char a; // Defined outside the function, so it is a global variable.
void main() // Main function
{
  unsigned char b; // Defined inside the function, so it is a local variable.
  b=a;
  while(1)
  { 
  
  }
}

Memory Model of Global and Local Variables

The memory of a microcontroller includes ROM and RAM. ROM stores the instructions and some unchangeable constant data in the microcontroller program, while RAM stores variable data that can be changed;

In other words, both global and local variables are stored in RAM, but despite both being stored in RAM, there are significant differences in the memory model between global and local variables.

Thus, two different RAM areas are allocated: the RAM area occupied by global variables is called the global data area, while the RAM area occupied by local variables is called the stack.

What are the Essential Differences in Their Memory Models?

The global data area is like your own room at home; it is unique, and the address of a room can only be occupied by you (assuming you are single), and it is permanent (sorry). Therefore, each global variable has a unique corresponding RAM address that cannot be duplicated.

The stack is like an inn; the people staying there change every night, and each person has a limited time to stay. The address of a room may be occupied by different people throughout the year, and it is not unique.

Global variables in the global data area have permanent ownership, while local variables in the stack can only temporarily reside in the inn, with non-unique addresses and limited duration.

The stack is shared among all local variables within the functions of the program. When a function is called, each local variable within that function is allocated to a specific RAM address in the stack. After the function call ends, that local variable becomes invalid.

Thus, the corresponding RAM space in the stack is reclaimed to allow the next called function’s local variables to occupy it.

Using the “Inn” Analogy for Local Variables in the “Stack”.

void function(void); // Declaration of the sub-function

void function(void) // Definition of the sub-function
{
  unsigned char a; // Local variable
  a=1;
} 

void main() // Main function
{
  function(); // Call to the sub-function
}

We see that the microcontroller executes from the main function downwards, first encountering the call to function(), so it jumps to the definition of function() to start execution. At this point, the local variable a begins to be allocated to a certain address in the RAM of the “stack area”, equivalent to being assigned a room in the inn.

After the microcontroller finishes executing the sub-function function(), the local variable a at the address allocated in the RAM of the stack is reclaimed, and the local variable a disappears. The reclaimed RAM address may be reassigned to other local variables of functions that are called later.

At this point, it is as if you left the inn, and you have no further connection with the room you occupied; the room you stayed in will be reassigned to other guests by the innkeeper.

The scope of global variables is permanent and unrestricted, while the scope of local variables is limited to the internal range of the function they are in. The global data area is a permanent private house, while the stack is a temporary inn.

Summary

  1. Each time a new global variable is defined, it means an additional RAM memory cost. However, each time a local variable is defined, as long as the total number of local variables defined within the function does not exceed the stack area of the microcontroller, there is no additional RAM memory cost, because local variables temporarily borrow from the stack, and after use, they return it. The stack is a public area that can be reused and can serve several different local variables within functions.

  2. Every time the microcontroller enters a function, local variables are initialized, while global variables are not initialized; global variables retain the last modified value.

Common Questions

Who Allocates the Global Data Area and Stack Area, and How is it Allocated?

It is automatically allocated by the C compiler. As for how it is allocated, who gets more or less, the C compiler has a default ratio for allocation, which we generally do not need to worry about.

The Stack Area is Temporarily Borrowed; when a sub-function is called, its internal local variables are temporarily allocated to a certain address in the “stack” area. So, who is in charge of the allocation of the “stack area”?

When the microcontroller is powered on and starts running the program, the compiler is no longer in effect. The allocation of the “stack area” for local variables within functions is indeed done by the C compiler, but this is before the microcontroller is powered on.

The C compiler has already planned the allocation of all local variables within functions, specifying which local variable of a function should be allocated to which address in the “stack area” once that function is called. The C compiler completes all these “post-mortem” arrangements before it ends its life.

When the microcontroller is powered on and starts working, although the C compiler is no longer present, the microcontroller strictly follows the instructions left by the C compiler to start working and allocating the “stack area”. Therefore, the “temporary allocation” of the “stack area” is not truly “temporary” in the strict sense.

If the total number of local variables defined within a function exceeds the RAM size of the “stack” area, is the consequence serious?

This situation is referred to as stack overflow. The program may exhibit inexplicable anomalies, and the consequences can be particularly severe.

To avoid this situation, generally, when writing programs, large arrays should not be defined as local variables, and the number of local variables should not be too many or too large, especially to avoid defining large arrays as local variables.

Large arrays should be defined as global variables or defined as static local variables.

Some C compilers may kindly remind you when encountering a “stack overflow” situation, preventing you from compiling, but some C compilers may not give you any warning. Therefore, everyone should be cautious about “stack overflow” when writing functions in projects.

Priority of Global and Local Variables

As mentioned earlier, the scope of global variables is permanent and unrestricted, while the scope of local variables is limited to the internal range of the function they are in.

Now, the question arises: if a local variable and a global variable have the same name, which variable is executed within the function?

This question involves priority.

Note: When faced with a local variable and a global variable with the same name, the variable executed within the function is the local variable, meaning that local variables take precedence over global variables within the function.

Let’s Look at Some Examples

First Example

unsigned char a=5; // Here, the first a is a global variable

void main() // Main function
{
  unsigned char a=2; // Here, the second a is a local variable, which has the same name as the first global variable a
  print(a); // Send a to the serial assistant software on the computer for observation
  while(1)
  { 
  
  }
}

The correct answer is 2. The local variable within the function takes precedence over the global variable.

Although the two a’s have the same name, their memory models are different. The first global variable a is allocated in the global data area, which has a unique address, while the second local variable a is allocated in the temporary stack area, residing within the main function.

Second Example

void function(void); // Function declaration
unsigned char a=5; // Here, the first a is a global variable

void function(void) // Function definition
{
  unsigned char a=3; // Here, the second a is a local variable.
} 

void main() // Main function
{
  unsigned char a=2; // Here, the third a is also a local variable.
  function(); // Call to the sub-function
  print(a); // Send a to the serial assistant software on the computer for observation.
  while(1)
  {
  }
}

The correct answer is 2. This is because the function function() is called and completed before executing print(a), meaning the local variable (the second local variable a) within the function no longer exists when executing <code>print(a), so the a in <code>print(a) refers to the third local variable a (defined within the main function).

Third Example

void function(void); // Function declaration
unsigned char a=5; // Here, the first a is a global variable

void function(void) // Function definition
{
  unsigned char a=3; // Here, the second a is a local variable
} 

void main() // Main function
{
  function(); // Call to the sub-function
  print(a); // Send a to the serial assistant software on the computer for observation
  while(1)
  {
  }
}

The correct answer is 5. This is because the function function() is called and completed before executing print(a), meaning the local variable (the second local variable) within the function no longer exists.

At the same time, since there is no local variable a defined within the main function, the a in print(a) must refer to the first global variable a (defined outside the main function).

In Conclusion

After reading this article, I believe everyone has gained a basic understanding of the stack. In embedded programming, we must always be cautious to avoid stack overflow; if there are any errors, please feel free to point them out. Until next time, goodbye.

———— END ————

Understanding the Significance of the Stack in Embedded Programming

● Column “Embedded Tools”

● Column “Embedded Development”

● Column “Keil Tutorial”

● Selected Tutorials from the Embedded Column

Follow the public account and reply “Join Group” to join the technical exchange group according to the rules, reply “1024” to see more content.

Understanding the Significance of the Stack in Embedded Programming

Understanding the Significance of the Stack in Embedded Programming

Click “Read Original” to see more shares.

Leave a Comment