简体   繁体   中英

Arduino/C++ GlobalVars vs Local variables

I'm working on an Arduino project right now. What it does is basically this:

  1. Reads serial data from PC
  2. Parse received data
  3. Draw on TFT screen using this data.

My current approach heavily utilizes global vars. Without them, I'd have to use some nested functions that passes variables 3 levels deep. That got me thinking on which approach is better performance-wise. Here are the two examples.

First the local:

void setup() {
  Serial.begin(9600);
  Serial.println(firstFunction(10));
}

int firstFunction(int val)
{
  return secondFunction(val+1);
}

int secondFunction(val)
{
  return thirdFunction(val+1);
}

int thirdFunction(val)
{
  return val + 1;
}

Global:

int x; //global var
void setup() {
  Serial.begin(9600);
  firstFunction(10);
  Serial.println(x);
}
void firstFunction(int val)
{
  x = val;
  x += 1;
  secondFunction();
}
void secondFunction()
{
  x++;
  thirdFunction();
}
void thirdFunction(val)
{
  x++;
}

On PC side of things, using globals are generally frowned upon. But my understanding is that's mostly for styling and scalability reasons.

What you guys think?

In my opinion it is better to style to go for the local approach, also on the generally smaller Arduino programs. Once you have a lot globals you can them in a struct and pass the address of the struct to the functions.

Especially useful in the setup function, the memory is not occupied anymore once setup is done. In your version x will still take up 4 bytes during the lifetime of the program. With just 2k memory you can reach the limit fast.

I would do something like this:

typedef struct {
  int a, b;
} entity_t;

void foo(entity_t *e) { foo2(e); foo3(e); /* do stuff */ }

void setup()
{
  entity_t e = { 1, 2};

  foo(&e);

  Serial.println(e.a);
  // automatic memory of e is released
}

Depending on how complex your project is, either approach can be feasible.

One consideration which speaks against passing variables as parameters through a stack of function calls is the very limited amount of memory you have on your microchip. Every local variable and every function parameter is put on your stack, and in your case you have basically the same variable multiple times on the stack, wasting your memory. Depending on the amount of RAM on your µC this can get hairy once you start working with strings and other larger structures you want to manipulate at runtime.

If you notice that suddenly your µC starts misbehaving - crashes, hangs, produces garbage output, etc. - it can be that you are underflowing your stack into your heap or the other way round. This is less likely to become a problem if you try to avoid unnecessary copies of variables like this in the first place.

Of course, depending on how your program works, the opposite can be true: If you have to have some state you need to keep track of only in a certain part of your program, and you don't call many nested functions there, it is probably better to just keep it in local variables passed through, since then the variable will be gone again once you return from the outer function.

Basically, the fundamental difference is that global variables are always there and occupy space through the whole time your program runs, while local variables do that only as long as the function which created them returns - but function arguments are like local variables of the function, so passing a variable through to another function creates a second copy. So, it has always two sides.

However, with increasing complexity, I'd definitely use classes - split by their responsibilities - with member or static variables and corresponding methods instead of just a bunch of global variables.

Then you still have things nicely tied together and not just all loosely floating around in global space, but you aren't wasting memory.

I don't think adding a reference adds any overhead; remember the compiler isn't tied to implementing it as pointer. If everything is in the same compilation unit, it can probably be optimized away. Almost certainly if the function is so small it can get inlined.

A different possibility would be to make those functions and variables members of the same class, if the design allows it.

But at the end of the day, encapsulation mainly exists so that complex projects can be maintained over month or decades by shifting teams of programmers. If it's a small code that only you will ever see, it's probably not necessary to get excessively tied to good practices.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM