C语言 sprintf()与自动内存分配?

3j86kqsm  于 2022-12-22  发布在  其他
关注(0)|答案(8)|浏览(234)

我正在寻找一个类似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;
}

但是,当调用带有空指针的sprintf()时,这会立即导致segfault。
那么有什么想法、解决方案或提示吗?一个类似sprintf()的解析器的小实现放在公共领域就已经足够了,然后我就可以自己完成了。
多谢了!

ct2axkht

ct2axkht1#

下面是from Stack Overflow的原始答案。正如其他人提到的,您需要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;
}
3qpi33ja

3qpi33ja2#

GNU和BSD的asprintfvasprintf就是为你设计的。它会为你计算出如何分配内存,并在任何内存分配错误时返回null。
asprintf在分配字符串方面做了正确的事情--它首先测量大小,然后尝试使用malloc进行分配。如果失败,它将返回null。除非您自己的内存分配系统不允许使用malloc,否则asprintf是完成这项工作的最佳工具。
代码如下所示:

#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main()
{
    char*   ret;
    char*   a = "Hello";
    char*   b = "World";
    int     c = 123;

    int err = asprintf(&ret, "%s %d %s!", a, c, b );
    if (err == -1) {
        fprintf(stderr, "Error in asprintf\n");
        return 1;
    }

    printf("ret = >%s<\n", ret);
    free(ret);

    return 0;
}
wqlqzqxt

wqlqzqxt3#

如果您可以使用GNU/BSD扩展,那么这个问题已经得到了解答,您可以使用asprintf()(和vasprintf()构建 Package 器函数)并完成任务。
但是根据手册页,snprintf()vsnprintf()是由POSIX强制要求的,后者可以用来构建您自己的asprintf()vasprintf()的简单版本。

int
vasprintf(char **strp, const char *fmt, va_list ap)
{
    va_list ap1;
    int len;
    char *buffer;
    int res;

    va_copy(ap1, ap);
    len = vsnprintf(NULL, 0, fmt, ap1);

    if (len < 0)
        return len;

    va_end(ap1);
    buffer = malloc(len + 1);

    if (!buffer)
        return -1;

    res = vsnprintf(buffer, len + 1, fmt, ap);

    if (res < 0)
        free(buffer);
    else
        *strp = buffer;

    return res;
}

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

您可以使用一些预处理器魔法,仅在不支持您的函数版本的系统上使用它们。

qjp7pelc

qjp7pelc4#

1.如果可能的话,使用snprintf--它提供了一种简单的方法来测量将要产生的数据大小,以便分配空间。
1.如果您真的不能这样做,另一种可能性是使用fprintf打印到一个临时文件,以获得大小,分配内存,然后使用sprintf。

tpxzln5u

tpxzln5u5#

GLib库提供了一个g_strdup_printf函数,如果可以选择与GLib链接,该函数可以完全满足您的需要。
类似于标准的C sprintf()函数,但更安全,因为它计算所需的最大空间并分配内存来保存结果。当不再需要返回的字符串时,应使用g_free()释放该字符串。

rnmwe5a2

rnmwe5a26#

POSIX. 1(又称IEEE 1003. 1 -2008)提供了开放内存流:

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)可能就是您想要的。

sf6xfgos

sf6xfgos7#

/*  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);
}
kuuvgm7e

kuuvgm7e8#

我的https://stackoverflow.com/a/10388547/666907版本(v3)-更通用一点[注意我是C++新手,使用风险自担:P]:

#include <cstdarg>

char* myFormat(const char* const format...) {
  // `vsnprintf()` changes `va_list`'s state, so using it after that is UB.
  // We need the args twice, so it is safer to just get two copies.
  va_list args1;
  va_list args2;
  va_start(args1, format);
  va_start(args2, format);

  size_t needed = 1 + vsnprintf(nullptr, 0, format, args1);

  // they say to cast in C++, so I cast…
  // https://stackoverflow.com/a/5099675/666907
  char* buffer = (char*) malloc(needed);

  vsnprintf(buffer, needed, format, args2);

  va_end(args1);
  va_end(args2);

  return buffer;
}

char* formatted = myFormat("Foo %s: %d", "bar", 456);
Serial.println(formatted); // Foo bar: 456

free(formatted); // remember to free or u will have a memory leak!

相关问题