2

I am writing the following code for an embedded system, where I am putting up strings and integers into a buffer. There are several sprintfs in the code similar to the one below (with more than one strings and integers).

sprintf(txbuf, "info=>t:%d-%d-%d$%d:%d:%d", ui8Day,ui8Month, ui8Year, ui8Hour, ui8Minute, ui8Second);

PS: The $ , - and : are just for data seperation.

However, sprintf has a large memory footprint and I am attempting to reduce the code size. I also tried using strcat and strcpy functions but they also ended up increasing the code size.

Annie
  • 21
  • 5
  • 1
    If you do not want to include the standard libraries, then you can define your own `mystrcpy` and `mystrcat` functions. – Rishikesh Raje Mar 18 '20 at 06:05
  • 1
  • 2
    Some compiler systems for embedded systems have a simpler `sprintf()` implementation that is limited in its capabilities. For example it cannot format `float`, or does not accept widths. This implementation is commonly really small. You did not mention your target nor your compiler. – the busybee Mar 18 '20 at 06:47
  • 2
    Oh, and `strcpy()` and `strcat()` are quite small already. By how many bytes do they increase your code size? Are you sure you don't mean `itoa()` or similar? – the busybee Mar 18 '20 at 06:48
  • It's unlikely that `strcpy` and `strcat` increase the size of your code significantly. These two functions are basically one liners. You need to tell us more. – Jabberwocky Mar 18 '20 at 07:48
  • You should include your versions using `strcpy()` and `strcat()` because it is not clear how you used them to achieve the equivalent effect. Whatever you used to convert the integers to decimal strings will contribute the largest part of any solution not `strcat` or `strcpy`. Your title suggest the question is about concatenating strings, but it is about formatting integers in strings - that is a different matter and intrinsically significantly more code. Implementing your own base 10 specific `itoa10()` is likely smaller than the standard `itoa()` with support for base 2 to 26. – Clifford Mar 18 '20 at 19:59

2 Answers2

1

Here is a minimalist implementation of sprintf you can use as a replacement that only supports %d, %u, %x, %s, %c and %%. You can easily modify it to remove unused conversion specifiers:

#include <limits.h>
#include <stdarg.h>
#include <stdio.h>

int small_sprintf(char *dest, const char *fmt, ...) {
    char buf[20];
    va_list arg;
    char *p, *s;
    int n;
    unsigned int u;

    va_start(arg, fmt);

    for (p = dest; (*p++ = *fmt++) != '\0';) {
        if (fmt[-1] == '%' && *fmt != '\0') {
            p--;
            s = buf + sizeof(buf);
            *--s = '\0';
            switch (*fmt++) {
            case 'u':
                u = va_arg(arg, unsigned);
                goto conv10;
            case 'd':
                u = n = va_arg(arg, int);
                if (n < 0) {
                    *p++ = '-';
                    u = -u;
                }
            conv10:
                do { *--s = '0' + (u % 10); } while ((u /= 10) != 0);
                break;
            case 'x':
                u = va_arg(arg, unsigned);
                do { *--s = "0123456789abcdef"[u & 15]; } while ((u >>= 4) != 0);
                break;
            case 's':
                s = va_arg(arg, char *);
                break;
            case 'c':
                *--s = (char)va_arg(arg, int);
                break;
            default:
                *--s = fmt[-1];
                break;
            }
            while ((*p = *s++) != '\0')
                p++;
        }
    }
    va_end(arg);
    return p - 1 - dest;
}

int main() {
    char buf[128];
    int n = small_sprintf(buf, "Hello %s%c %d %d%% INT_MIN=%d, INT_MAX=%x", "world", '!', 0, 100, INT_MIN, INT_MAX);
    puts(buf);
    while (n-- > 0)
        putchar('-');
    putchar('\n');
    return 0;
}

Note however that your example should probably use %02d or %.2d to produce 01 instead of 1 for minutes and seconds. For this purpose, you could add another conversion specifier %D with this code:

        case 'D': // convert an unsigned integer as 2 decimal digits
            d = va_arg(arg, unsigned);
            *--s = '0' + u % 10;
            *--s = '0' + u / 10 % 10;
            break;
chqrlie
  • 131,814
  • 10
  • 121
  • 189
-1

you may use snprintf() instead of sprintf() because snprintf() stop memory overflow.

zaman
  • 5
  • 3