Ошибка сегментации строки с помощью 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;
}
Много проблем:
strcpy(result+i+strlen(strings),format+i-memo+2);
копирует формат для результата. Вам нужно скопировать строку, переданную в качестве аргумента переменной, в результирующую строку. Futhermoreresult+i+strlen(strings)
не имеет никакого смысла: зачем ты это написал?- Был
i=0
каждый раз, когда вы узнаете спецификатор формата%
: Зачем? это было основной причиной ошибки сегментации. Цикл перезапускается каждый раз%
найден, и запрашивается новый переменный аргумент, но другие не требуются. - у вас должно быть 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
возвращаемая вами строка