Преобразование int в char* в C, когда использование sprintf слишком дорого

Мне нужно конвертировать int значение для char строка, я использовал следующую функцию, где счет имеет тип int,

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

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

Кажется, это работает, за исключением того, что этот фрагмент кода является частью гораздо большей системы, в которой я кодирую видеоигру на микроконтроллере (MSP430) с ограниченным объемом доступной памяти. Я могу обновить счет за один раз до того, как моя игра зависнет и вылетит.

По какой-то причине, когда я добавляю sprintf() функция (или другие подобные функции, такие как snprintf, или же asprintf), он съедает много доступной памяти на MCU.

С sprintf функции он компилирует в 11407 байт. Если я прокомментирую эту строку, мой код скомпилируется в 4714 байт

Я не слишком знаком с преобразованием int в char Струны, однако, я не думаю, что он должен занимать столько памяти. Любое руководство будет высоко ценится.

4 ответа

Вот моя быстрая реализация int в string функция:

// 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;
}

Использование:

char my_buf[16];

char * s = i2s( my_buf, sizeof my_buf, 10850 ); // 10850 is the int value

Пять символов - это не много - чтобы покрыть каждое значение целого, вам нужно как минимум 10 цифр + знак + завершающий символ 0, т. Е. 12. Тогда я бы создал массив со степенью двойки, то есть 16:

char str[16];

Тогда вы можете просто закодировать с обратной стороны:

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);

Изменить (глядя на другие ответы (и комментарии), опубликованные во время написания моего):

Выше решение тоже не удастся для INT_MIN!

Вы могли бы поймать это отдельно (оценка == INT_MIN) - или просто полагаться на то, что это значение никогда не будет достигнуто в любом случае (потому что это не разумно в вашей игре).

Дополнительно: мой вывод выровнен по левому краю! Если вы хотите выровнять по правому краю, вы можете просто поместить дополнительные пробелы в цикл for:

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

Имейте в виду, что вы не можете сравнивать произвольные указатели с <, >, <=, >=! Здесь это возможно, потому что оба указателя указывают на один и тот же массив!

Там уже опубликовано общее решение. В вашем случае, когда известны верхний и нижний пределы, вы можете использовать следующий код:

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);
}

Если вы хотите набрать ноль, вам не нужно быстро переходить к первой ненулевой цифре:

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);
}

Вы можете печатать целые числа вручную, например

#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");
}

ВЫХОД на терминале Linux.

1234
 234
  34
   4
Другие вопросы по тегам