Подтвердить что ты не робот

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(), который размещен в общедоступном домене, уже будет достаточным, и я мог бы сделать это сам.

Спасибо большое!

4b9b3361

Ответ 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);
}