Variables in functions ... What is best?

I wrote this little function to format floats to be displayed on an LCD at some given position.
because I am using dtostrf() the previous contents get overwritten.
It seems to work well and means that I don’t have to clear the previous value if the new one requires fewer characters.
This is it:-

void LcdPrintValAt(int Row,int Col, float fValue, int iIntegerLen, int iMantissaLen){
  Row--;
  Col--;
  if (iIntegerLen < 0){
    iIntegerLen++;                                                                                  // There neeeds to be space for a '-' sign
  }
  if (iMantissaLen){
    iMantissaLen++;                                                                                 // There neeeds to be space for a '.' before the prescribed number of decimals
  }
  char strBuffer[iIntegerLen + iMantissaLen];
  lcd.setCursor(Col, Row);
  lcd.print(dtostrf(fValue, iIntegerLen + iMantissaLen, iMantissaLen, strBuffer));
}

what I am wondering is if the line > char strBuffer[iIntegerLen + iMantissaLen]; <
would be better defined at some maximum length, 20 say given the length of a display line
and if so should it be static so that it isn’t continually created and destroyed.

Any thoughts?

why, not use that as an input to the function? if tomorrow you get a bigger, or smaller, lcd, you change that input to the function.

It will only exist inside the function.

If that was me, I'd put that display functionality in a class and let the properties of the display be set in the constructor and held as instance variables. That way you don't restrict your display logic to just supporting that one display type, you could even support multiple displays concurrently if you wanted, but you don't need to have the display properties scattered around your code.

I am a tad confused by both those answers ... The fact that I am talking about a function to put text on a display is largely immaterial.

The point was about local variables specifically ... Is there any reason, apart from preserving values between function calls, to do anything specific with them. Will the compiler produce more efficient code if local variables are handled in some specific way, or perhaps avoided unless absolutely necessary.

Just trying to learn.

Dyslexicbloke:
what I am wondering is if the line > char strBuffer[iIntegerLen + iMantissaLen]; <
would be better defined at some maximum length, 20 say given the length of a display line
and if so should it be static so that it isn’t continually created and destroyed.

It doesn’t take a lot to create the variable (an adjustment to the stack pointer). However I would be tempted to have a static variable here of the maximum expected size. That will use that memory all the time (even if you are not in the function) but you need that memory available anyway if the function is going to be called.

If you start doing this a lot (eg. another one for integers, etc.) then the creation inside the function will probably use less memory overall.

My personal preference: I use global variables with no specific names, so I can reuse them whenever it's possible :)

Fair enough, I will stick with the dynamic local for now but the insight is useful. Thanks

guix: My personal preference: I use global variables with no specific names, so I can reuse them whenever it's possible :)

Once you encounter some excruciating errors, you'll think twice about this.

@Dyslexicbloke

Local var memory/registers can be re-used by the compilers optimisations, so two variables that don't have an overlapping usage can sometimes be in the same address ( compilers decision, cannot be forced ).

If you are copying data into the variable, a local initialisation can be more efficient than a global assignment.

Memory pools are another option.

Globally allocate a block/pool, then using placement new operator ( search on google, c++ FAQ ) create your local object in pool, and explicitly call the destructor when finished. This allows initialisations without allocations, plus no de-allocation ( only 1 if pool isn't persistent throughout the applications lifetime ).

pYro_65: two variables that don't have an overlapping usage can sometimes be in the same address ( compilers decision, cannot be forced ).

Well, of course you can always use a union to force the issue.

A union is itself a unique entity commonly known as a type. Multiple typed names shouldn't be mixed in usage to conform to standard.

"A union may be thought of as a structure all of whose members begin at offset 0 and whose size is sufficient to contain any of its members. At most one of the members can be stored in a union at any time."

So:

union myunion { int var_a; int var_b; float var_c; } uval;

will allow (actually, force) vars var_a, var_b and var_c to share the same memory address.

uval.var_a and uval.var_b will be typed as ints, while uval.var_c will be typed as a float.

Sometimes you just gotta tell the compiler who's boss!

I use global variables with no specific names, so I can reuse them whenever it's possible

Ooo, a recipe for hard-to-trace bugs and unmaintainable code. You'd have to be desperate for more RAM to do this.


Rob

pico: "A union may be thought of as a structure all of whose members begin at offset 0 and whose size is sufficient to contain any of its members. At most one of the members can be stored in a union at any time."

So:

union myunion { int var_a; int var_b; float var_c; } uval;

will allow (actually, force) vars var_a, var_b and var_c to share the same memory address.

uval.var_a and uval.var_b will be typed as ints, while uval.var_c will be typed as a float.

Sometimes you just gotta tell the compiler who's boss!

That is not the same as the optimisation I pointed out, what about two variables of different sizes? Your optimisation causes sub-optimal performance.

Also its not forcing anything, the names are aliases to a single type. Your example is in violation of the specifications if you write to one member then read another ( strict aliasing rule ), and becomes unsafe once you start using non-trivial members, not to mention trap representations.

Once you have to use unions for much other than sign extension, minimum type sizing, or handling c code, you could be explicitly adding 'undefined behaviour' to your code.

Globally allocate a block/pool, then using placement new operator

It sound great but I fear its a step too far right now ... I think I will do a bit more learning before I go messing directly with memory Thanks though, I suspect this is exactly the sort of thing I need to be working towards.

Right now, with my very limited skills, a union sounds like a superb way to build code that will break without giving me the slightest hope of finding the cause. Again interesting point to note for much later in my C/C++ journey

Thanks folks.

Dyslexicbloke: what I am wondering is if the line > char strBuffer[iIntegerLen + iMantissaLen]; would be better defined at some maximum length, 20 say given the length of a display line

Yes. Yust make

Dyslexicbloke: and if so should it be static so that it isn't continually created and destroyed.

Definitely not. At the moment it's created in the return stack so it's basically free. If you make it static it uses up a fixed amount of RAM.