Sprintf() с автоматическим распределением памяти?
Я ищу sprintf() - как реализация функции, которая автоматически выделяет требуемую память. Поэтому я хочу сказать
char* my_str = dynamic_sprintf( "Hello %s, this is a %.*s nice %05d string", a, b, c, d );
а my_str извлекает адрес выделенной памяти, который содержит результат этого sprintf().
В другом форуме я читал, что это можно решить следующим образом:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main()
{
char* ret;
char* a = "Hello";
char* b = "World";
int c = 123;
int numbytes;
numbytes = sprintf( (char*)NULL, "%s %d %s!", a, c, b );
printf( "numbytes = %d", numbytes );
ret = (char*)malloc( ( numbytes + 1 ) * sizeof( char ) );
sprintf( ret, "%s %d %s!", a, c, b );
printf( "ret = >%s<\n", ret );
free( ret );
return 0;
}
Но это немедленно приводит к segfault, когда вызывается sprintf() с NULL-указателем.
Итак, любая идея, решение или советы? Небольшую реализацию парсинга sprintf(), который размещен в общедоступном домене, уже будет достаточным, и я мог бы сделать это сам.
Спасибо большое!
Ответы
Ответ 1
Вот оригинальный ответ от Кару. Как уже упоминалось, вам нужно snprintf
, а не sprintf
. Убедитесь, что вторым аргументом snprintf
является zero
. Это предотвратит запись snprintf
в строку NULL
, которая является первым аргументом.
Второй аргумент необходим, потому что он сообщает snprintf
, что недостаточно места для записи в выходной буфер. Когда недостаточно места, snprintf
возвращает количество байтов, которое он записал бы, если бы было достаточно места.
Воспроизведение кода по этой ссылке здесь...
char* get_error_message(char const *msg) {
size_t needed = snprintf(NULL, 0, "%s: %s (%d)", msg, strerror(errno), errno) + 1;
char *buffer = malloc(needed);
sprintf(buffer, "%s: %s (%d)", msg, strerror(errno), errno);
return buffer;
}
Ответ 2
В GNU и BSD есть asprintf и vasprintf, которые предназначены для этого. Он будет определять, как распределить память для вас и вернет null при любой ошибке выделения памяти.
asprintf делает правильную вещь в отношении выделения строк - сначала измеряет размер, затем пытается выделить с помощью malloc. В противном случае он возвращает null. Если у вас нет собственной системы распределения памяти, которая исключает использование malloc, asprintf является лучшим инструментом для задания.
Код будет выглядеть так:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main()
{
char* ret;
char* a = "Hello";
char* b = "World";
int c = 123;
ret = asprintf( "%s %d %s!", a, c, b );
if (ret == NULL) {
fprintf(stderr, "Error in asprintf\n");
return 1;
}
printf( "ret = >%s<\n", ret );
free( ret );
return 0;
}
Ответ 3
Если вы можете жить с расширениями GNU/BSD, вопрос уже дан. Вы можете использовать asprintf()
(и vasprintf()
для создания функций оболочки) и выполнить.
Но snprintf()
и vsnprintf()
заданы POSIX в соответствии с man-страницей, а последние могут использоваться для создания вашей собственной простой версии asprintf()
и vasprintf()
.
int
vasprintf(char **strp, const char *fmt, va_list ap)
{
va_list ap1;
size_t size;
char *buffer;
va_copy(ap1, ap);
size = vsnprintf(NULL, 0, fmt, ap1) + 1;
va_end(ap1);
buffer = calloc(1, size);
if (!buffer)
return -1;
*strp = buffer;
return vsnprintf(buffer, size, fmt, ap);
}
int
asprintf(char **strp, const char *fmt, ...)
{
int error;
va_list ap;
va_start(ap, fmt);
error = vasprintf(strp, fmt, ap);
va_end(ap);
return error;
}
Вы можете использовать магию препроцессора и использовать свои версии функций только в тех системах, которые их не поддерживают.
Ответ 4
- Если возможно, используйте
snprintf
- он дает простой способ измерения размера данных, которые будут созданы, чтобы вы могли выделить пространство.
- Если вы действительно не можете этого сделать, другая возможность заключается во временном файле с
fprintf
, чтобы получить размер, выделить память, а затем использовать sprintf. snprintf
, безусловно, является предпочтительным методом.
Ответ 5
Библиотека GLib предоставляет функцию g_strdup_printf
, которая делает именно то, что вы хотите, если опция связывания с GLib является опцией. Из документации:
Аналогично стандарту C sprintf()
функционировать, но безопаснее, так как рассчитывает максимально необходимое пространство и выделяет память для хранения результат. Возвращаемая строка должна быть освобождается с помощью g_free()
, когда его больше нет необходимо.
Ответ 6
/* casprintf print to allocated or reallocated string
char *aux = NULL;
casprintf(&aux,"first line\n");
casprintf(&aux,"seconde line\n");
printf(aux);
free(aux);
*/
int vcasprintf(char **strp,const char *fmt,va_list ap)
{
int ret;
char *strp1;
char *result;
if (*strp==NULL)
return vasprintf(strp,fmt,ap);
ret=vasprintf(&strp1,fmt,ap); // ret = strlen(strp1) or -1
if (ret == -1 ) return ret;
if (ret==0) {free(strp1);return strlen(*strp);}
size_t len = strlen(*strp);
*strp=realloc(*strp,len + ret +1);
memcpy((*strp)+len,strp1,ret+1);
free(strp1);
return(len+ret);
}
int casprintf(char **strp, const char *fmt, ...)
{
int ret;
va_list ap;
va_start(ap,fmt);
ret =vcasprintf(strp,fmt,ap);
va_end(ap);
return(ret);
}