C 创建一个my_printf,将数据发送到sprintf和普通printf?

C 创建一个my_printf,将数据发送到sprintf和普通printf?,c,linux,gcc,printf,C,Linux,Gcc,Printf,我正在玩printf和这个想法 编写一个my_printf(…)调用普通printf 和一个sprintf,它将结果发送到一个特殊函数。 (我考虑的是sprintf,因为它在大多数平台上的行为与printf一样) 我的想法是编写一个小型宏来实现这一点: #define my_printf(X, Y...) do{ printf(X, ## Y); \ char* data = malloc(strlen(X)*sizeof(char)); \ sprintf(data, X, #

我正在玩printf和这个想法 编写一个my_printf(…)调用普通printf 和一个sprintf,它将结果发送到一个特殊函数。 (我考虑的是sprintf,因为它在大多数平台上的行为与printf一样)

我的想法是编写一个小型宏来实现这一点:

#define my_printf(X, Y...) do{ printf(X, ## Y); \ char* data = malloc(strlen(X)*sizeof(char)); \ sprintf(data, X, ## Y); \ other_print(data);\ free(data);}while(0) #定义my_printf(X,Y…)do{printf(X,Y)\ char*data=malloc(strlen(X)*sizeof(char))\ sprintf(数据,X,##Y)\ 其他打印(数据)\ 空闲(数据);}while(0) 但是由于sprintf可以将字符串扩展到比X大得多的大小, 这种方法几乎直接中断

加上一个数字,malloc似乎是解决问题的错误方法, 从那时起,我将把问题转移到未来,有一天我想打印一个大的表达式

有人对如何解决这个问题有更好的想法吗? 或者我如何知道sprintf的结果会有多大

谢谢 约翰


更新:我忘了printf返回它打印的字符数, 因为我已经在宏中调用printf了,所以添加它非常简单 保存数字的整数

#define buf_printf(X, Y...) do{ int len = printf(X, ## Y); \ char* data = malloc((len+1)*sizeof(char)); \ sprintf(data, X, ## Y); \ other_print(data);\ free(data);}while(0) #定义buf_printf(X,Y…)do{int len=printf(X,Y)\ char*data=malloc((len+1)*sizeof(char))\ sprintf(数据,X,##Y)\ 其他打印(数据)\ 空闲(数据);}while(0) 更新:我在考虑这个问题,也许可以使用一个普通函数 这看起来很像ephemient所说的是个好主意。 这里的关键似乎是不同printf函数的v版本 (vprintf、vsprintf和vsnprintf)。谢谢你指出这一点

再次感谢
约翰

最好的方法是使用varargs。创建一个与printf()具有相同原型的函数,并使用varargs函数将数据传递给sprintf以填充所需的缓冲区,在返回之前还将该缓冲区传递给printf(“%s”)

许多早期的实现对最低级别的printf()调用有4K限制,但我会选择更多。你可能需要设定一个上限并坚持下去


我们在日志系统中使用的一个技巧是使用printf()将数据写入/dev/null句柄。由于printf()返回写入的字符数,因此我们使用它来分配缓冲区。但是这不是很有效,因为它需要调用printf()type函数两次。

使用snprintf计算大小。从手册页:

“如果由于此限制而截断了输出,则返回值为字符数(不包括尾随'\0'),如果有足够的可用空间,将写入最终字符串”

snprintf是C99的标准配置。如果您只有C89编译器,那么请查看文档:预标准版本可能不会返回您想要的值。同样根据手册页,如果输出被截断,2.1版之前的glibc用于返回-1,而不是所需的大小


顺便说一句,sizeof(char)在每个C实现中都被定义为1:-)

如果您总是在一个带有
glibc
的系统上运行(即Linux和任何其他带有GNU用户区的操作系统),
asprintf
就像
sprintf
一样,但可以自动处理分配本身

int my_printf(const char *fmt, ...) {
    char *buf = NULL;
    int len;
    va_list ap;

    va_start(ap, &fmt);
    len = vasprintf(&buf, fmt, ap);
    va_end(ap);

    if (len < 0) {
        /* error: allocation failed */
        return len;
    }

    puts(buf);
    other_print(buf);

    free(buf);
    return len;
}

正如一个接一个的所说,一些旧系统没有兼容的
snprintf
。无法赢得所有的支持…

因为您使用的是Linux,我建议您使用
asprintf()
-它是为您分配字符串的GNU扩展。 多亏了C99可变宏,你不必再去惹瓦拉格了

因此,您的宏看起来像:

#define MY_PRINT(...) do { \
                          char *data; \
                          asprintf(&data, __VA_ARGS__); \
                          printf("%s", data); \
                          other_print(data); \
                          free(data); \
                      } while (0)
注意!这是C99和GNU专用代码


编辑:现在它将只计算一次宏参数,因此使用类似(“%d”,i++)的东西调用宏将正常工作。

同意,因为它避免了讨厌的宏,但我认为这无助于提问者解决分配多大缓冲区的问题。是的,我最初误读了这个问题,假设,与sprintf()类似,myprintf()的调用者已经有了足够大的缓冲区。“由于printf()返回写入的字符数,因此我们使用它来分配缓冲区”,这是关键:-)但是使用vararg似乎是更好的解决方案。这可能是特定于平台的,但sprintf(NULL,fmt,…)将返回malloc所需的字符数。或者,将sprintf()转换为一个静态缓冲区,如果它填满了整个缓冲区,则只使用/then/双sprintf技巧。你将摊销到1。平均值上的printfs使用varargs尝试vsnprintf()。建议:微软C++(2003)有一些非常不符合标准的C99标准打破了关于[V] SNPROTFF()应该返回的想法。SIEZOF(CHAR)= 1总是有趣的。然而,我总是把它放在那里,这样在其他情况下我就不会忘记它(这很重要)。此外,先将snprintf放入缓冲区,然后再打印缓冲区。这样字符串只格式化一次。@David-我想你是指sprintf(),然后是put()。有趣的是,GCC确实会尽可能地将printf(“%s”,foo)优化为put()。请使用varargs尝试vsnprintf()。请注意:微软C++(2003)有一些非常不一致的C99标准打破了关于[V] SNPrTNFF()应该返回的想法。与其说“只有一个C89编译器”,我应该说“只有一个不兼容C99的编译器”。有些编译器不符合已知的规范,特别是如果意外编译C为C++;打字错误:s/aspintf/asprintf/。我个人不喜欢宏处理这个问题的方式,因为它会导致对参数进行多次计算,但看起来确实简单多了。感谢您的评论,更正了输入错误,更改了宏以避免多个副作用。抱歉,最后一个错误<与
printf(“%s”,data)
put(data)
相比,code>printf(data)是不好的:如果
data
包含
%
#define MY_PRINT(...) do { \
                          char *data; \
                          asprintf(&data, __VA_ARGS__); \
                          printf("%s", data); \
                          other_print(data); \
                          free(data); \
                      } while (0)