1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.

C: Dynamic string using variable number of arguments

Discussion in 'C' started by negru, Mar 14, 2015.

  1. negru

    negru New Member

    Joined:
    Feb 13, 2015
    Messages:
    4
    Likes Received:
    0
    Trophy Points:
    0
    I need to create dynamic string by given format(%d,%s,%f,%lf,%c) using variable number of arguments in function. This code gives me an error(main.exe has stopped working):

    Code:
    #include<stdio.h>
    #include<stdarg.h>
    #include<string.h>
    #include<stdlib.h>
    
    char *form(char *format,...);
    char *form(char *format,...)
    {
        char *res=(char *)calloc(1,1),val_int[12],*pos_int,*val_str,*pos_str,*val_float,*pos_float,*val_lf,*pos_lf,*val_char,*pos_char;
        va_list args;
        va_start(args,format);
        do
        {
          pos_int=strstr(format,"%d");pos_str=strstr(format,"%s");pos_float=strstr(format,"%f");pos_lf=strstr(format,"%lf");pos_char=strstr(format,"%c");
          if(pos_int && (!pos_str || pos_int < pos_str) && (!pos_float || pos_int < pos_float) &&
             (!pos_lf || pos_int < pos_lf) && (!pos_char || pos_int < pos_char))
          {
            itoa(va_arg(args,int),val_int,10);
            res=(char *)realloc(res,strlen(res)+(pos_int-format)+strlen(val_int)+1);
            strncat(res,format,pos_int-format);
            strcat(res,val_int);
            format=pos_int+2;
          }
          else if(pos_str && (!pos_int || pos_str < pos_int) && (!pos_float || pos_str < pos_float) &&
                  (!pos_lf || pos_str < pos_lf) && (!pos_char || pos_str < pos_char))
          {
             val_str=va_arg(args,char *);
             res=(char *)realloc(res,strlen(res)+(pos_str-format)+strlen(val_str)+1);
             strncat(res,format,pos_str-format);
             strcat(res,val_str);
             format=pos_str+2;
          }
    
          else if(pos_float && (!pos_int || pos_float < pos_int) && (!pos_str || pos_float < pos_str) &&
                  (!pos_lf || pos_float < pos_lf) && (!pos_char || pos_float < pos_char))
          {
             fcvt(va_arg(args,double),val_float,6,0);
             res=(char *)realloc(res,strlen(res)+(pos_float-format)+strlen(val_float)+1);
             strncat(res,format,pos_float-format);
             strcat(res,val_float);
             format=pos_float+2;
          }
    
          else if(pos_lf && (!pos_int || pos_lf < pos_int) && (!pos_str || pos_lf < pos_str) &&
                  (!pos_float || pos_lf < pos_float) && (!pos_char || pos_lf < pos_char))
          {
              ecvt(va_arg(args,double),val_lf,6,0);
              res=(char *)realloc(res,strlen(res)+(pos_lf-format)+strlen(val_lf)+1);
              strncat(res,format,pos_lf-format);
              strcat(res,val_lf);
              format=pos_lf+2;
          }
          else if(pos_char && (!pos_int || pos_char < pos_int) && (!pos_str || pos_char < pos_str) &&
                  (!pos_float || pos_char < pos_float) && (!pos_lf || pos_char < pos_lf))
          {
              val_char=va_arg(args,char *);
              res=(char *)realloc(res,strlen(res)+(pos_char-format)+strlen(val_char)+1);
              strncat(res,format,pos_char-format);
              strcat(res,val_char);
              format=pos_char+2;
          }
        }
        while(pos_int || pos_str || pos_float || pos_lf || pos_char);
        va_end(args);
        res=(char *)realloc(res,strlen(res)+strlen(format)+1);
        strcat(res,format);
        return res;
    }
    
    int main()
    {
        char *s;
        s=form("John is %d years old and has an %c in %s. He is going to school for %lf years.",9,'A',"maths",1.5);
        printf("%s",s);
        free(s);
        return 0;
    }
    I assume the error is in functions(itoa,fcvt,ecvt). Thanks for replies.
     
  2. hobbyist

    hobbyist New Member

    Joined:
    Jan 7, 2012
    Messages:
    141
    Likes Received:
    0
    Trophy Points:
    0
    I'm not familiar with va_args stuff, but I tried something like below, which *seemed* to work for your test string. I didn't post the entire code; it could probably be better optimized, but maybe it can offer an idea.

    HTH

    Code:
    char *form(char *format, ...)
    {
        size_t i = 0, j, len = strlen(format) * 2 + 1; 
        // realloc gives me bad vibes. :P
        char *ret = malloc(len), tmp[33];
        va_list args;
        va_start(args, format); 
    
        if(ret == NULL) return NULL;
    
        memset(ret, 0, len);
    
        while(*fmt)
        {
            if(i < len)
                *(ret + i) = *fmt;
            else 
                break;
    
            if(*fmt == '%')
            {
                switch(*(fmt + 1))
                {
                    case 'd' : sprintf(tmp, "%d", va_args(args, int));
                                 ++fmt;
                               
                                 if(i < len + strlen(tmp))
                                 {
                                     for(j = 0; tmp[j] != '\0'; ++j)
                                         *(ret + i++) = tmp[j];
                                 }
     
                                 --i; // remove extra space
                     break;
    
                     // similar for %c, %f, %s, etc
                    
                     case 'l' :  if(*(fmt + 2) == 'f')
                                  {
                                       sprintf(tmp, "%0.1lf", va_args(args, double));
                                       fmt += 2;
    
                                       if(i < len + strlen(tmp))
                                       {
                                            for(j = 0; tmp[j] != '\0'; ++j)
                                                *(ret + i++) = tmp[j];
                                        }
    
                                        --i;
                                    } // test for ld can be added here  
                     break;  
                 }
             }
    
             ++fmt;
             ++i;
        }
    
        va_end(args); 
        *(ret + i) = 0; 
    
        return ret;
    }
     

Share This Page