Function returns nothing back, not even ByRef

Ok, time to ask the question:

Having surfed almost every corner of this and other forums for solutions, I'm stuck with my lack of knowledge.

My char function below generates the right answer but holds on to the answer, not willing to share it with the outer world, not even when I use a ByRef var in the header.

Reading the war on Strings, strings, char and const chars it must be something to do with the character of chars. although I think I treated the subject correct (no strings, Strings or other jelly) and thought of them as an array of ASCII and the solution works.

I want to create a float to char conversion called Float2Char and wish to control the whole process instead of hooking up libraries to confiscate memory so precious on the Uno.

Working on an original Uno and a few surrogate MEGA's all works well until this point.

I'm building a link between a robotic Total Station (3D coordinate spewing device) to a PC by RS232 to a LoRa (via an Arduino) to talk to a setting out hovercraft to give it corrections every second to alter the direction and the distance until it is on the required point to set out.

The robotic supplies OS BNG coordinates every second which are too large to fit in an Arduino float so I'm calculating the differences between design and actual location to calculate heading and distance for the hovercraft (has another Arduino on board). what comes out of the LoRa needs to be calculated to steer the servo and the rudders. char to float and back for presentation on an OLED.

Float2Char is needed to be able to calculate char => float values and back on board of the hovercraft.

All works well inside the function but nothing comes out. I fear a char related issue. even ByRef fails to let go of the value.

Could anyone shine a light on the below please? Much appreciated.

I'm migrating from VB6/VB.net to C/C++ here, burning the midnight oil to get it under my belt.

Thanks.

As a newbie I'm not able to upload something ..... so see below, copied straight from my Arduino IDE:


//=================== Float2Char.ino ============
//
// Global VAR definitions
//
char Float2CharResult; // holds the return of the function
char FloatResult; // ByRef to transport the answer from the function
float OrgVal; // holds the given value
int AmountOfDecs; // hold the amount of required decimals to project in the char
   
// ------------------------------------------------- Float2Char() ---------------------------------------
char Float2Char(float F2C_value, int Decimals, char &F2C_Result)
{
  bool F2C_Sign=false; // True = negative value, False = Positive value, assume positive to start with
  char F2C_Dot = '.'; // Decimal dot

  byte DotPos=0; // Counter how many digits before the dot
  
  long val_int; // OS is too large for int, need a long
  float val_frac; // Store the fraction of the value
  long work_val_int; // container to shift the values from the digits to the right, needs long for OS coords to contain
  float work_val_double; // for float values to shift left
  int CharAmount; // total amount of characters to reserve
  
  if (F2C_value<0) F2C_Sign=true; // if negative reserve an extra place for the minus
  F2C_value=abs(F2C_value); // unsign the value

  val_int=F2C_value; // Integer part of the value
  val_frac=F2C_value-val_int; // Decdima part of the value

  work_val_int=val_int; // 
  
//  Serial.print("work_val_int = ");
//  Serial.println(work_val_int);
  
  while (work_val_int>0) // till there is nothing left
  {
        work_val_int=work_val_int/10; // Shift the digits to the right
        DotPos++; // increase the digits counter
  }

  CharAmount=DotPos+1+Decimals; // Amount of characters to reserve in the char
  if (F2C_Sign) CharAmount++; // plus one if negative
  
  Serial.print("CharAmount: ");
  Serial.println(CharAmount);
  
//-----------------------------------------------------
  char ReturnValue[CharAmount+1]; // reserve the exact space required + 1 for terminator
  int RVIndex = 0; // start at the 1st location
//-----------------------------------------------------

  if (F2C_Sign) // 
  {
    ReturnValue[RVIndex]='-'; // place minus if negative
    RVIndex++; // move the index
  }

//  
// Digits of the value before the dot
//
  
// pop the int value back into the work_val_double

  work_val_double=val_int;

// shift to the right until all is left is a fraction

  for (int i=1;i<=DotPos;i++) work_val_double=work_val_double/10;

// shift to the left to distinguish characters to store in char array
  
  for (byte i=1;i<=DotPos;i++)
  {
    work_val_double=work_val_double*10;//0.214318 => 2.14318
    val_int=work_val_double; // val_int = 2
    work_val_double=work_val_double-val_int; //work_val = 0.14318
    ReturnValue[RVIndex]=val_int+48; // ASCII value to store
    RVIndex++; // Up the Index
  }

// Digit section done, all good
  
  ReturnValue[RVIndex]=F2C_Dot; // Load the dot
  RVIndex++; // up the index

  work_val_double=val_frac; // as many original decs as poss
  
// Decimal places after the dot

// Same principle: move to left to distinguish individual characters to store in the char array
  
  for (byte i=1;i<=Decimals;i++)
  {
    work_val_double=work_val_double*10;//0.412872811 => 4.12872811
    val_int=work_val_double; // val_int = 4
    work_val_double=work_val_double-val_int; //work_val = 0.14318, for the next round
    ReturnValue[RVIndex]=val_int+48; // Store as ASCII
    RVIndex++; // Up the index
  }
//
  ReturnValue[RVIndex]='\0';// Terminator at the last index

//
//  F2C_Result=F2C_Result+ReturnValue; // Does not work => Error cannot bind const char to char
//  strcat(F2C_Result,Returnvalue); // does not work => Error: cannot bind const char to char
 
  Serial.print("Return inside: >");
  Serial.print(ReturnValue);
  Serial.println("<"); // All good here !!
  
  return ReturnValue; // Function fails to return the char although perfect (no hiding, astray characters) inside the function
 
}
// ------------------------------------------------- Float2Char() End ---------------------------------------

void setup() {
  Serial.begin(9600);
}

void loop() {

    AmountOfDecs=4; // decs to receive
    FloatResult="";
    OrgVal = 218658.62544458879596326;// OS BNG Easting, I know this does not fit in a float with so many decs, 
                                      // 218658.625 is the best accuracy here which is fine atm, 
                                      // I have alternatives for that to increase the accuracy lateron
    
    Float2CharResult = Float2Char(OrgVal,AmountOfDecs, FloatResult); // call the function and store the result, returns no value ??, not even ByRef.
    
    Serial.print("-----Return from Function: "); // 
    Serial.println(Float2CharResult); // Return value
    Serial.println(FloatResult); // ByRef result
    Serial.println();
    
    delay(5000);
}

// ============================ End of Session ==================

Your function returns a char, not a pointer to a char array.

not exactly - the function is defined as returning a char, but OP try to return a pointer from it. So it won't work anymore...

Your function returns a local variable what has gone out of scope at that time.

Plus what @anon57585045 said.

a7

Reserving an array INSIDE of the function and return it outside is not a good idea in general. After exiting from the function your local array ReturnValue[] will destroyed and all it contents will be lost.
You should reserve array in main program and pass it as pointer to the function. Inside the function you can populate array with the values, so it will be available in the main program ater the exit of the function.

This might work, there are better ways maybe. No changes except remove the declaration of ReturnValue in th function.

char ReturnValue[bigEnoughConstant];  // global - get over it

char *Float2Char(float F2C_value, int Decimals, char &F2C_Result)
{

a7

Have you seen the dtostrf function?

char F2C_Result[10];
dtostrf(-12.345, 0, 5, F2C_Result);  // `F2C_value` = -12.345, `Decimals` = 5.

Serial.println(F2C_Result);          // Prints "-12.34500".

Yeah more like it's a bad idea period.

And the worst part is it might appear to work, as the contents are not necessarily destroyed, and the pointer might well seem to be to a valid correct solution.

Until the day it isn't.

Gotta check - seems like this could might have generated a compiler warning.

I think declaring the variable in the function to be static would fix the problem, though it would mean permanent, big-enough memory use.

a7

Just in case anyone doubted it, the problems of @arjen1962 would all have been made clear if compiler warnings were set to all. And read. And heeded.

Wrong type returned:

warning: invalid conversion from 'char*' to 'char' [-fpermissive]
 return localArray;

and

Variable going out of scope:

warning: address of local variable 'localArray' returned [-Wreturn-local-addr]
 char localArray[64];

Making the local character array static fixes that one, but then you must choose and live with a static size.

a7

Thanks you all, much appreciated.

Makes total sense that an internal declared var is loosing its content outside the function/procedure.

Apologies for not analyzing and attaching the error message as it leads the way. Wasting our precious time.

Cracking on with a solution and will post it when thoroughly tested.

Issue resolved: Topic can be closed.

I have chosen for a global defined char array [25, enough for my project] to be filled by the Proc.

Changed the Function to a Proc. error check: no errors caused, result exists outside the Proc.

Thanks to the above contributors, see the end result below:

//
// Global VAR definitions
//
char Float2CharResult[25]; // holds the return of the function Float2Char, 25 places for a 22 char width OLED is sufficient
//
// 
//
// ------------------------------------------------- Float2Char() ---------------------------------------
void Float2Char(float F2C_value, int Decimals)
{
//
// Local VAR definitions
//  
  bool F2C_Sign=false; // True = negative value, False = Positive value, assume positive to start with
  char F2C_Dot = '.'; // Decimal dot

  byte DotPos=0; // Counter how many digits before the dot
  
  long val_int; // OS is too large for int, need a long
  float val_frac; // Store the fraction of the value
  long work_val_int; // container to shift the values from the digits to the right, needs long for OS coords to contain
  float work_val_double; // for float values to shift left
  int CharAmount; // total amount of characters to reserve
//
// Start Routine
//
  if (F2C_value<0) F2C_Sign=true; // if negative reserve an extra place for the minus
  F2C_value=abs(F2C_value); // unsign the value

  val_int=F2C_value; // Integer part of the value
  val_frac=F2C_value-val_int; // Decdima part of the value

  work_val_int=val_int; // 
  
//  Serial.print("work_val_int = ");
//  Serial.println(work_val_int);
  
  while (work_val_int>0) // till there is nothing left
  {
        work_val_int=work_val_int/10; // Shift the digits to the right
        DotPos++; // increase the digits counter
  }

  CharAmount=DotPos+1+Decimals; // Amount of characters to reserve in the char
  if (F2C_Sign) CharAmount++; // plus one if negative
  
//  Serial.print("CharAmount: ");
//  Serial.println(CharAmount);
  
//-----------------------------------------------------
//  char ReturnValue[CharAmount+1]; // reserve the exact space required + 1 for terminator, Local def/decl, does not exists outside the routine !!
  int RVIndex = 0; // start at the 1st location
//-----------------------------------------------------

  if (F2C_Sign) // 
  {
    Float2CharResult[RVIndex]='-'; // place minus if negative
    RVIndex++; // move the index
  }

//  
// Digits of the value before the dot
//
  
// pop the int value back into the work_val_double

  work_val_double=val_int;

// shift to the right until all is left is a fraction

  for (int i=1;i<=DotPos;i++) work_val_double=work_val_double/10;

// shift to the left to distinguish characters to store in char array
  
  for (byte i=1;i<=DotPos;i++)
  {
    work_val_double=work_val_double*10;//0.214318 => 2.14318
    val_int=work_val_double; // val_int = 2
    work_val_double=work_val_double-val_int; //work_val = 0.14318
    Float2CharResult[RVIndex]=val_int+48; // ASCII value to store
    RVIndex++; // Up the Index
  }

// Digit section done, all good
  
  Float2CharResult[RVIndex]=F2C_Dot; // Load the dot
  RVIndex++; // up the index

  work_val_double=val_frac; // as many original decs as poss
  
// Decimal places after the dot

// Same principle: move to left to distinguish individual characters to store in the char array
  
  for (byte i=1;i<=Decimals;i++)
  {
    work_val_double=work_val_double*10;//0.412872811 => 4.12872811
    val_int=work_val_double; // val_int = 4
    work_val_double=work_val_double-val_int; //work_val = 0.14318, for the next round
    Float2CharResult[RVIndex]=val_int+48; // Store as ASCII
    RVIndex++; // Up the index
  }
//
  Float2CharResult[RVIndex]='\0';// Terminator at the last index, ready to leave the routine

//  Serial.print("Return inside: >");
//  Serial.print(Float2CharResult);
//  Serial.println("<"); // All good here !!
  
//  return *ReturnValue; // Function fails to return the char although perfect (no hiding, astray characters) inside the function
//                          , was locally declared and therefore non existing outside the routine !!
 
}
// ------------------------------------------------- Float2Char() End ---------------------------------------


void setup() {
  Serial.begin(9600);
}

void loop() {

    int AmountOfDecs=4; // decs to receive
 
    float OrgVal = 218658.62544458879596326;// OS BNG Easting, I know this does not fit in a float with so many decs, 
                                           // 218658.625 is the best accuracy here which is fine atm, 
                                          // I have alternatives for that to increase the accuracy lateron
    
    Float2Char(OrgVal,AmountOfDecs); // call the proc
    
    Serial.print("-----Return from Function: "); // 
    Serial.println(Float2CharResult); // Return value is correct and exists outside the Proc
    Serial.println();
    
    delay(5000);
}

Have you seen the dtostrf function?

Thanks, Yes but requires to attach a library to function which. atm, I chose to minimize libraries due to the consumption of memory and my wish to invent the wheel again out of curiosity..

Issue resolved, topic can be closed imv.

chosen to use a global var char array[25] to be filled by the Proc.

Changed function to Proc. Error checked, no issues.

thanks to the above contributions: code below:

//
// Global VAR definitions
//
char Float2CharResult[25]; // holds the return of the function Float2Char, 25 places for a 22 char width OLED is sufficient
//
// 
//
// ------------------------------------------------- Float2Char() ---------------------------------------
void Float2Char(float F2C_value, int Decimals)
{
//
// Local VAR definitions
//  
  bool F2C_Sign=false; // True = negative value, False = Positive value, assume positive to start with
  char F2C_Dot = '.'; // Decimal dot

  byte DotPos=0; // Counter how many digits before the dot
  
  long val_int; // OS is too large for int, need a long
  float val_frac; // Store the fraction of the value
  long work_val_int; // container to shift the values from the digits to the right, needs long for OS coords to contain
  float work_val_double; // for float values to shift left
  int CharAmount; // total amount of characters to reserve
//
// Start Routine
//
  if (F2C_value<0) F2C_Sign=true; // if negative reserve an extra place for the minus
  F2C_value=abs(F2C_value); // unsign the value

  val_int=F2C_value; // Integer part of the value
  val_frac=F2C_value-val_int; // Decdima part of the value

  work_val_int=val_int; // 
  
//  Serial.print("work_val_int = ");
//  Serial.println(work_val_int);
  
  while (work_val_int>0) // till there is nothing left
  {
        work_val_int=work_val_int/10; // Shift the digits to the right
        DotPos++; // increase the digits counter
  }

  CharAmount=DotPos+1+Decimals; // Amount of characters to reserve in the char
  if (F2C_Sign) CharAmount++; // plus one if negative
  
//  Serial.print("CharAmount: ");
//  Serial.println(CharAmount);
  
//-----------------------------------------------------
//  char ReturnValue[CharAmount+1]; // reserve the exact space required + 1 for terminator, Local def/decl, does not exists outside the routine !!
  int RVIndex = 0; // start at the 1st location
//-----------------------------------------------------

  if (F2C_Sign) // 
  {
    Float2CharResult[RVIndex]='-'; // place minus if negative
    RVIndex++; // move the index
  }

//  
// Digits of the value before the dot
//
  
// pop the int value back into the work_val_double

  work_val_double=val_int;

// shift to the right until all is left is a fraction

  for (int i=1;i<=DotPos;i++) work_val_double=work_val_double/10;

// shift to the left to distinguish characters to store in char array
  
  for (byte i=1;i<=DotPos;i++)
  {
    work_val_double=work_val_double*10;//0.214318 => 2.14318
    val_int=work_val_double; // val_int = 2
    work_val_double=work_val_double-val_int; //work_val = 0.14318
    Float2CharResult[RVIndex]=val_int+48; // ASCII value to store
    RVIndex++; // Up the Index
  }

// Digit section done, all good
  
  Float2CharResult[RVIndex]=F2C_Dot; // Load the dot
  RVIndex++; // up the index

  work_val_double=val_frac; // as many original decs as poss
  
// Decimal places after the dot

// Same principle: move to left to distinguish individual characters to store in the char array
  
  for (byte i=1;i<=Decimals;i++)
  {
    work_val_double=work_val_double*10;//0.412872811 => 4.12872811
    val_int=work_val_double; // val_int = 4
    work_val_double=work_val_double-val_int; //work_val = 0.14318, for the next round
    Float2CharResult[RVIndex]=val_int+48; // Store as ASCII
    RVIndex++; // Up the index
  }
//
  Float2CharResult[RVIndex]='\0';// Terminator at the last index, ready to leave the routine

//  Serial.print("Return inside: >");
//  Serial.print(Float2CharResult);
//  Serial.println("<"); // All good here !!
  
//  return *ReturnValue; // Function fails to return the char although perfect (no hiding, astray characters) inside the function
//                          , was locally declared and therefore non existing outside the routine !!
 
}
// ------------------------------------------------- Float2Char() End ---------------------------------------


void setup() {
  Serial.begin(9600);
}

void loop() {

    int AmountOfDecs=4; // decs to receive
 
    float OrgVal = 218658.62544458879596326;// OS BNG Easting, I know this does not fit in a float with so many decs, 
                                           // 218658.625 is the best accuracy here which is fine atm, 
                                          // I have alternatives for that to increase the accuracy lateron
    
    Float2Char(OrgVal,AmountOfDecs); // call the proc
    
    Serial.print("-----Return from Function: "); // 
    Serial.println(Float2CharResult); // Return value is correct and exists outside the Proc
    Serial.println();
    
    delay(5000);
}

Fortunately, only the functions that are used make it to the final binary, so there is no need to minimise library use (unless the implemented functions are particularly bad with respect to memory usage).

I can relate to that.

Fortunately, only the functions that are used make it to the final binary, so there is no need to minimise library use (unless the implemented functions are particularly bad with respect to memory usage).

I was wondering that, does the same goes with vars, const and the lot?

Instead of analyzing complex libraries for memory usage (BYTEs instead of INTs in small for/next loops, overly large defines char arrays, etc) I tend to create my own central Box of Tricks library as far as I can with slashed down memory usage.

Of course, dedicated libs for specific hardware usage I surrender to, to trust efficient memory usage.

Thanks for mentioning re filtering prior to generate the final binary.

If the library is properly written, then this should be the case.

Here is an example:

This sketch works fine until we uncomment line 8 or 9.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.