Character Array Issues

Good evening all,

I am working on some code where I need to make an unsigned int into a string with a given number of digits in it for display on an LCD display. The following is the function I am using for this:

char * uintToString(unsigned int value, int numChars){

  char digitsChar[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
  char retVal[numChars + 1];
  int index = numChars - 1;
  
  // Initialize for given number of characters
  for (int i = 0; i < numChars; i++){
    retVal[i] = '0';
  }
  retVal[numChars] = '\0';

  // Convert the unsigned in to a string
  while (value >= 10){
    retVal[index--] = digitsChar[value % 10];
    value = value / 10;
  }
  retVal[index] = digitsChar[value];
  return retVal;
  
}

So, if I pass value = 1065 and numChars = 4, on return I get 0065. If I put in a Serial.print as shown here (or a delay, or almost any other statement) and change nothing else, I get 1065 as expected.

  // Convert the unsigned int to a string
  while (value >= 10){
    retVal[index--] = digitsChar[value % 10];
    value = value / 10;
  }
  retVal[index] = digitsChar[value];
  Serial.print(retVal);  // Can also replace with delay(10)
  return retVal;

Could someone please help me with figuring out why I get this behavior? I am totally stumped here.

Thanks in advance,
Rob Hix

retval is local to that function and it isn't static. So it goes out of scope as soon as return is called. Whatever code you are returning that number to is getting a pointer that doesn't point to anything. OK, so it just so happened that some of the digits were still sitting in memory at that location and hadn't been overwritten. But if you change the code then you change that behavior. Either way you're in undefined behavior territory, so anything is liable to happen. If this code made little demons fly out of your nose every time you ran it then you would only be able to say it was working properly.

Moral of the story? Don't return pointers to local variables. Pass a pointer to this function and let the function put the converted string in the pointer that belongs to the caller and gets passed in.

Or make retval static.

By the way, there's a much easier way to convert single digits to and from ascii.

char digitsChar[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};

retVal[index--] = digitsChar[value % 10];

Could just be:

retVal[index--] = (value % 10) + '0';

If that isn't immediately obvious to you, go spend a little time looking at an ascii table and you'll see why it works. '0' == 48

Also by the way, the C++ function sprintf would do this for you.

Wow, my C/C++ is a little rustier than I thought. It has been 8-10 years since I touched C, so obviously there are some things I have forgotten.

That explanation makes total sense, thanks for the help and guidance.

Rob Hix

I need to make an unsigned int into a string with a given number of digits in it for display on an LCD display.

Why the need for conversion ? Can you not just print the unsigned int ?

Because he wants the leading zeros and print won't do that.

sprintf() is very powerful and will work, but it’s an H-bomb-to-kill-an-ant and uses more memory than you need because of unused features of the function. You might use something like:

void setup() {
  Serial.begin(9600);
  Serial.print("Enter an unsigned int: ");
}

void loop() {
  char input[6];
  int charsRead;
  int numChars = 5;     // We'll assume you want a field of 5 digits

  if (Serial.available() > 0) {
    charsRead = Serial.readBytesUntil('\n', input, sizeof(input) - 1);
    input[charsRead] = '\0';    // Make it a string...

    uintToString(input, numChars);     //Now pad the input

    Serial.print("Input = ");
    Serial.println(input);
    Serial.print("Enter an unsigned int: ");  
  }
}

/*****
 * Purpose: to pad number with leading 0's for desired field width
 * 
 * Parameter list:
 *  char *s         input digit characters
 *  int fieldWidth  the desired width of field
 *  
 * Return value:
 *  void
 *  
 * CAUTION: assumes s is big enough to hold digit chars, pad, and null
 * 
 */
void uintToString(char *s, int fieldWidth)
{
  char temp[6];
  int len = strlen(s);
  int i;
  if (len == fieldWidth) {      // Already desired width
    return;
  }
  len = fieldWidth - len;       // How many '0'

  temp[0]  = '\0';              // Since we use strcat()
  for (i = 0; i < len; i++) {
    strcat(temp, "0");          // Add leading '0'
  }
  strcat(temp, s);              // add original string on...
  strcpy(s, temp);              // copy all back to original string
}