Andrei Ardelean Andrei Ardelean - 16 days ago 4
C Question

Why is this program crashing?

This function should return a string that contains string representations of the second and subsequent arguments, each to two decimal places and separated by commas. The first argument is a count of the number of arguments that follow. I did some research and found out about

va_list
and tried to work with it. Unfortunately the program is crashing and I have no idea why. I'm posting this hoping that someone could maybe spot an obvious mistake or something like that.

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

char *to_string(int count,...)
{
int i,x,k=0;
float tmp;
va_list valist;
va_start(valist, count+1);
char comma=',';
char buffer[count*6];
for(i=0;i<count;i++)
{
tmp=va_arg(valist, double)*100;
x=tmp/100.0;
k+=4;
snprintf(buffer + k, "%C",comma);
k++;
snprintf(buffer + 1, "%.2f", x);
}
va_end(valist);
printf("%s", buffer);
return buffer;
}

int main()
{
to_string(2,3.14876,6.123243);
}

Answer

As noted in the comments, you need to read the manual pages for va_start() and snprintf() in particular, and also pay attention to memory management (you cannot safely return a pointer to a local (non-static) variable).

Here's a revamped piece of code that cleans up the issues identified and that doesn't crash (for me on my Mac running macOS Sierra 10.12.1 and GCC 6.2.0).

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

static char *to_string(char *buffer, size_t buflen, int count, ...)
{
    va_list valist;
    va_start(valist, count);
    char *data = buffer;
    for (int i = 0; i < count; i++)
    {
        int x = va_arg(valist, double) * 100;
        double tmp = x / 100.0 + 0.005;
        int n = snprintf(data, buflen, ",%.2f", tmp);
        if (n > (int)(buflen - 1))
        {
            fprintf(stderr, "%s: buffer not big enough\n", __func__);
            break;
        }
        data += n;
        buflen -= n;
    }
    va_end(valist);
    printf("%s: [%s]\n", __func__, buffer);
    return buffer;
}

int main(void)
{
    char buffer[80];
    printf("main: [%s]\n", to_string(buffer, sizeof(buffer), 2, 3.14876, 6.123243));
    return 0;
}

Example output:

to_string: [,3.15,6.12]
main: [,3.15,6.12]

Note that there's the leading comma which you probably don't want. That can be fixed like this:

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

static char *to_string(char *buffer, size_t buflen, int count, ...)
{
    va_list valist;
    va_start(valist, count);
    char *data = buffer;
    const char *pad = "";
    for (int i = 0; i < count; i++)
    {
        int x = va_arg(valist, double) * 100;
        double tmp = x / 100.0 + 0.005;
        int n = snprintf(data, buflen, "%s%.2f", pad, tmp);
        if (n > (int)(buflen - 1))
        {
            fprintf(stderr, "%s: buffer not big enough\n", __func__);
            break;
        }
        pad = ",";
        data += n;
        buflen -= n;
    }
    va_end(valist);
    printf("%s: [%s]\n", __func__, buffer);
    return buffer;
}

int main(void)
{
    char buffer[80];
    printf("main: [%s]\n", to_string(buffer, sizeof(buffer), 2, 3.14876, 6.123243));
    printf("main: [%s]\n", to_string(buffer, sizeof(buffer), 20,
           6.67, 0.04, 8.81, 8.49, 3.50, 1.20, 4.28, 0.67, 1.93, 5.63,
           5.30, 8.43, 1.99, 4.62, 5.54, 7.21, 9.43, 2.02, 4.77, 0.29));
    return 0;
}

Example output:

to_string: buffer not big enough
to_string: [3.15,6.12]
main: [3.15,6.12]
to_string: [6.67,0.04,8.82,8.50,3.50,1.20,4.29,0.68,1.93,5.63,5.30,8.44,1.99,4.62,5.54,7.21]
main: [6.67,0.04,8.82,8.50,3.50,1.20,4.29,0.68,1.93,5.63,5.30,8.44,1.99,4.62,5.54,7.21]
Comments