0

I need to convert an int value to a char string, I used the following function where score is of type int.

void updateScore(){
   char str[5] = "0";
   sprintf(str, "%d", score);
   drawString(str);
}

void drawString5x7(char *string){
   while (*string) 
      drawChar5x7(*string++);
}

This seems to work, except this snippet of code is part of a much larger system where I am coding a video game on a microcontroller (MSP430) with limited memory space available. I am able to update the score one time before my game freezes and crashes.

For some reason, whenever I add the sprintf() function (or other similar functions like snprintf, or asprintf), it eats up a lot of the available memory on the MCU.

With the sprintf functions it compiles at 11407 bytes. If I comment that single line out, my code compiles at 4714 bytes

I'm not too familiar with converting int to char strings, however, I don't think it's supposed to consume that much memory. Any guidance will be much appreciated.

Toby
  • 9,696
  • 16
  • 68
  • 132
Omar Khalik
  • 13
  • 1
  • 1
  • 9
  • 6
    `sprintf` is a complex function. Some compilers allow you to specify the level of support the function has. For example, you could disable float specifiers if you never use them, and function would be a lot smaller. – user694733 Apr 26 '17 at 07:34
  • ...and: are you sure your score is 4 digits long as max? – LPs Apr 26 '17 at 07:39
  • @user694733, I'm not sure my compiler allows me to do that... I'm running a relatively weak one – Omar Khalik Apr 26 '17 at 07:39
  • @LPs The game I'm making is essentially Flappy Bird. I figure nobody will score passed that – Omar Khalik Apr 26 '17 at 07:40
  • If you need to show only integers, you can do it manually easily. – LPs Apr 26 '17 at 07:40
  • @OmarKhalik What compiler do you use? You should check the manual to be sure, if you don't want to write your own. – user694733 Apr 26 '17 at 07:46

4 Answers4

1

Here is my quick implementation of int to string function:

// buf - string buffer, e.g. 16 bytes
// len - size of buf
// value - integer value
char * i2s( char * buf, int len, int value )
{
    char m = 0;

    if ( value < 0 )
    {
        // if ( value == -2147483648 ) return "-2147483648"; // fix for INT_MIN
        value = -value;
        m = '-'; // minus for negative values
    }
    buf[len - 1] = 0; // 0 for end of string
    for ( int i = len - 2; i > 0; i-- ) // loop from right (last digits) to left
    {
        buf[i] = '0' + ( value % 10 );
        value /= 10;
        if ( value == 0 )
            break;
    }
    if ( m ) buf[--i] = m;
    return buf + i;
}

Usage:

char my_buf[16];

char * s = i2s( my_buf, sizeof my_buf, 10850 ); // 10850 is the int value
i486
  • 6,491
  • 4
  • 24
  • 41
  • 1
    Note: This won't work for `INT_MIN` (assuming 2's complement). – user694733 Apr 26 '17 at 08:01
  • 1
    I agree. When `value` is `INT_MIN` (i.e. `-2147483648` for 32-bit `int`), `value = -value` will remain unchanged (negative) and the result is garbage. Added a simple fix. – i486 Apr 26 '17 at 08:08
0

There already is a general solution posted. In your case, where the upper and lower limits are known, you can use the following code:

void updateScore() {
   char str[5] = "0";     /* Can hold up to 4 digits. */
   int i, div = 1000; /* 10^(length of str - 2). */
   int sc = score;
   /* Fast-forward to the first non-zero digit */
   while (div > sc) {
     div /= 10;
   }
   for (i = 0; div; i++) {
     str[i] = ´0´ + sc / div;
     sc %= div;
     div /= 10;
   }
   drawString(str);
}

If you want to 0-pad the score you don't need to fast-forward to the first non-zero digit:

void updateScore() {
   char str[5] = "0";     /* Can hold up to 4 digits. */
   int i, div = 1000; /* 10^(length of str - 2). */
   int sc = score;
   for (i = 0; div; i++) {
     str[i] = ´0´ + sc / div;
     sc %= div;
     div /= 10;
   }
   drawString(str);
}
Community
  • 1
  • 1
Klas Lindbäck
  • 33,105
  • 5
  • 57
  • 82
0

You can print integers manually, like

#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>

void drawChar ( char c )
{
    printf("%c", c);
}

void drawScore(uint16_t score)
{
    bool startFound = false;
    uint16_t devider = 1000;

    while (devider != 0)
    {
        uint8_t digit = (score / devider);

        if (( digit == 0) && (startFound == false))
        {
            // draw space
            drawChar(0x20);
        }
        else
        {
            // first digit of score found
            startFound = true;

            // draw space
            drawChar(digit+'0');
        }

        // move to the next digit
        score -= digit * devider;
        devider /= 10;
    }
}

int main(void)
{
    drawScore(1234);
    printf("\n");
    drawScore(234);
    printf("\n");
    drawScore(34);
    printf("\n");
    drawScore(4);
    printf("\n");
}

OUTPUT on a linux terminal.

1234
 234
  34
   4
LPs
  • 16,045
  • 8
  • 30
  • 61
0

Five characters is a not much - to cover each value of an int, you need at least 10 digits + sign + terminating 0 character, i. e. 12. I'd create an array of size of power of two then, so 16:

char str[16];

Then you can encode from the backwards simply:

str[sizeof(str)] = 0;
char* pos = str + (sizeof(str) - 1);
int n = score < 0 ? -score : score;
do // do while handles n == 0 correctly!
{
    // possible, if all digits are placed one after another
    // in ascending order, such as in ASCII (and those based on):
    *--pos = '0' + n % 10;
    // alternative, portable over all encodings:
    //*--pos = "0123456789"[n % 10];
    n /= 10;
}
while(n);
if(score < 0)
    *--pos = '-';

drawString(pos);

Edit (looking at the other answers (and comments) posted while writing mine):

Above solution, too, will fail for INT_MIN!

You could catch this separately (score == INT_MIN) – or simply rely on this value never being reached anyway (because not reasonable in your game).

Additionally: my output is left-aligned! If you want right-aligned output, you'd could simply place additional spaces in a for loop:

for(char* align = str + 4; pos > align;)
    *--pos = ' ';

Be aware that you cannot compare arbitrary pointers with <, >, <=, >=! Here, it is possible, because both pointers point to the same array!

Aconcagua
  • 24,880
  • 4
  • 34
  • 59