web-dev-qa-db-ja.com

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;
}

ただし、これにより、NULLポインタを指定したsprintf()が呼び出されるとすぐにsegfaultが発生します。

アイデア、解決策、ヒントはありますか?パブリックドメインに配置されたsprintf()のようなパーサーの小さな実装はすでに十分であり、それから私はそれを自分で行うことができます。

どうもありがとう!

36
the-shamen

これが元の答え Stack Overflowから です。他の人が述べたように、snprintfではなくsprintfが必要です。 snprintfの2番目の引数がzeroであることを確認してください。これにより、snprintfが最初の引数であるNULL文字列に書き込むことができなくなります。

2番目の引数は、出力バッファーに書き込むための十分なスペースがないことを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;
}
38
Tarun

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;
}
25
Mike Axiak

GNU/BSDの拡張機能を使用できる場合は、質問はすでに回答されています。 asprintf()(およびvasprintf()を使用してラッパー関数を作成)し、実行することができます。

しかしsnprintf()vsnprintf()は、マンページによればPOSIXによって義務付けられており、後者は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;
}

いくつかのプリプロセッサのマジックを実行して、関数のバージョンを、それらをサポートしないシステムでのみ使用できます。

13
Pavel Šimerda
  1. 可能であれば、 snprintf -を使用すると、生成されるデータのサイズを簡単に測定できるため、スペースを割り当てることができます。
  2. 本当にそれができない場合、別の可能性は、fprintfで一時ファイルに出力してサイズを取得し、メモリを割り当てることです、次にsprintfを使用します。 snprintf間違いなく推奨される方法です。
10
Jerry Coffin

GLib ライブラリーは _g_strdup_printf_ 関数を提供します。GLibへのリンクがオプションの場合、この関数は正確に機能します。ドキュメントから:

標準のC sprintf()関数と似ていますが、必要な最大スペースを計算し、結果を保持するためのメモリを割り当てるため、より安全です。返された文字列は、不要になったときにg_free()で解放する必要があります。

5

POSIX.1(別名IEEE 1003.1-2008)はopen_memstreamを提供します:

char *ptr;
size_t size;
FILE *f = open_memstream(&ptr, &size);
fprintf(f, "lots of stuff here\n");
fclose(f);
write(1, ptr, size); /* for example */
free(ptr);

open_memstream(3)は少なくともLinuxとmacOSで利用可能で、数年前から存在しています。 open_memstream(3)の逆はfmemopen(3)であり、これによりバッファの内容を読み取り可能にします。

単一のsprintf(3)が必要な場合は、広く実装されているが非標準のasprintf(3)が必要な場合があります。

0
John Haxby
/*  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);
}