Ошибка сегментации строки с помощью va_arg

Я делаю функцию, похожую на printf, она должна принимать строку и аргументы, такие как:

form("Integer %d, String %s", 54, "STRING");

и сделать строку "Integer 54, String STRING", я использую stdarg.h библиотека, потому что моя функция должна иметь переменное количество аргументов в зависимости от строки.

Проблема в том, что я получаю Segmentation fault, Я понял, что это происходит только тогда, когда я strlen или же strcpy со строкой (символ *), которую я передал va_arg, Вот мой код:

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

char* form(char *format, ...)
{
    va_list ap;char sign;int br=0,lasti,memo=0;char* help;
    int ints; float floats; double doubles; char chars; char* strings;
    va_start(ap,format);
    char* result=(char*)calloc(100,strlen(format));
    strcpy(result,format);
    for(int i=0; result[i] ;i++)
        if(result[i]=='%')
        {   
            switch (result[i+1])    
            {
                case 'd': {
                    ints=va_arg(ap,int);
                    int b,save=ints,dec=1,j;char *p=result+i;
                    for(b=0;save;b++) {save/=10;dec*=10;}
                    for(dec/=10,j=0; dec ; j++) { p[j]=((ints/dec)%10)+0x30; dec/=10; }
                    strcpy(result+i+b,format+i-memo+2);memo+=b-2;
                } break;

                case 'f': {
                    floats=va_arg(ap,double);

                } break;

                case 'l': {
                    doubles=va_arg(ap,double);

                } break;

                case 'c': {
                    chars=va_arg(ap,int);
                    result[i]=chars;
                } break;

                case 's': {
                    strings=va_arg(ap,char*);
                    strcpy(result+i+strlen(strings),format+i-memo+2);memo+=(strlen(strings)-2);
                } break;

                default: printf("Unknown type.\n"); break;
            }
            i=0;
        }

    return result;
}

int main()
{
    char a[100];
    scanf("%s",a);
    char*s=form("treci %s peti",a);
    printf("%s", s);

    printf("\n");
    free(s);
    return 0;
}

Единственный способ, с помощью которого strlen или strcpy может вызвать ошибку segfault, - это если строка не заканчивается нулем, а моя. Так что здесь не так и как мне это исправить?

РЕДАКТИРОВАТЬ: Добавлен код.

2 ответа

Решение

Проверено только для случая%s

char* form(char *format, ...)
{
    va_list ap;char sign;int br=0,lasti,memo=0;char* help;
    int ints; float floats; double doubles; char chars; char* strings;
    va_start(ap,format);
    char* result=(char*)calloc(100,strlen(format));
//    strcpy(result,format);
    for(int i=0; format[i] ;i++)
    {
        if(format[i]=='%')
        {
            switch (format[i+1])
            {
                case 'd': {
                    ints=va_arg(ap,int);
                    int b,save=ints,dec=1,j;char *p=result+i;
                    for(b=0;save;b++) {save/=10;dec*=10;}
                    for(dec/=10,j=0; dec ; j++) { p[j]=((ints/dec)%10)+0x30; dec/=10; }
                    strcpy(result+i+b,format+i-memo+2);memo+=b-2;
                } break;

                case 'f': {
                    floats=va_arg(ap,double);

                } break;

                case 'l': {
                    doubles=va_arg(ap,double);

                } break;

                case 'c': {
                    chars=va_arg(ap,int);
                    result[i]=chars;
                } break;

                case 's': {
                    strings=va_arg(ap,char*);
                    strcpy(result+memo,strings);
                    memo+=strlen(strings);
                } break;

                default: printf("Unknown type.\n"); break;
            }
            i++;
        }
        else
        {
            result[memo++] = format[i];
        }
    }

    return result;
}

Много проблем:

  1. strcpy(result+i+strlen(strings),format+i-memo+2); копирует формат для результата. Вам нужно скопировать строку, переданную в качестве аргумента переменной, в результирующую строку. Futhermore result+i+strlen(strings) не имеет никакого смысла: зачем ты это написал?
  2. Был i=0 каждый раз, когда вы узнаете спецификатор формата %: Зачем? это было основной причиной ошибки сегментации. Цикл перезапускается каждый раз % найден, и запрашивается новый переменный аргумент, но другие не требуются.
  3. у вас должно быть 2 разных индекса для разбора строки входного формата и индексации строки результата, потому что выходная строка, вероятно, будет иметь разную длину.

С помощью vsnprintf делает это очень просто:

char *form(const char *format, ...)
{
    va_list va;
    va_start(va, format);

    int result = vsnprintf(NUll, 0, format, va);

    // Error checking
    if (result < 0)
        return NULL;

    // Here result is the number of bytes we need to allocate (excluding terminator)
    char *string = malloc(result + 1);

    // Now do the actual formatting
    vsnprintf(string, result + 1, format, va);

    return string;
}

Важно: не забудьте free возвращаемая вами строка

Другие вопросы по тегам