2016-06-21 56 views
3

要输出格式化的调试输出,我写了vsfprint的包装。现在,我想为输出缓冲区分配足够的内存,而不是声称一个随机的高缓冲区大小(这是一个小型嵌入式平台(ESP8266))。为此,我遍历可变参数,直到找到NULL。C可变包装

这工作正常,但我不会忘记给每个呼叫添加(char *)NULL参数。所以,我想,让创建另一个包装,只是继电器的所有参数,并增加了一个(char *) NULL参数的函数:

#include <stdarg.h> 
#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> // malloc 

void write_log(const char *format, ...) { 

    char* buffdyn; 
    va_list args; 

    // CALC. MEMORY 
    size_t len; 
    char *p; 

    if(format == NULL) 
    return; 

    len = strlen(format); 

    va_start(args, format); 

    while((p = va_arg(args, char *)) != NULL) 
    len += strlen(p); 

    va_end(args); 
    // END CALC. MEMORY 

    // ALLOCATE MEMORY 
    buffdyn = malloc(len + 1); /* +1 for trailing \0 */ 
    if(buffdyn == NULL) { 
    printf("Not enough memory to process message."); 
    return; 
    } 

    va_start(args, format); 
    //vsnprintf = Write formatted data from variable argument list to sized buffer 
    vsnprintf(buffdyn, len, format, args); 
    va_end(args); 

    printf("%s\r\n",buffdyn); 
    free(buffdyn); 
} 

void write_log_wrapper(const char *format, ...) { 

    va_list arg; 

    va_start(arg, format); 
    write_log(format,arg,(char *)NULL); 
    va_end(arg); 
} 


int main() 
{ 
    const char* sDeviceName = "TEST123"; 
    const char* sFiller1 = "12345678"; 

    write_log_wrapper("Welcome to %s%s", sDeviceName,sFiller1); 
    write_log("Welcome to %s%s", sDeviceName,sFiller1, (char *)NULL); 

    return 0; 
} 

调用write_log()功能直接罚款(如果你不要忘了NULL参数)。调用write_log_wrapper()函数将只显示第一个参数,然后向输出添加“(nu”(垃圾?)。

我在做什么错?这是一个很好的方法来处理我的目标摆在首位呢?

感谢。

+2

如果您打算将它写入'stdout',为什么不简单使用'vprintf'?如果要写入文件(使用'vfprintf'),则同样如下所示。然后,在你的代码中根本没有缓冲区分配,你不必处理搞清楚有多少个参数或者一个缓冲区有多大。 –

+3

您忽略了传递的参数之一不是通过'char *'传递的NUL终止字符串的可能性。如果你得到一个'int'或者'double',会怎么样? –

+0

...或者带有%p的NULL –

回答

2

要确定一个缓冲区有多大容纳输出字符串需要,你需要充分解析整个格式字符串和实际扩大的论点

您可以自己做,重复的printf()所有处理及其同类,并希望没有犯任何错误,或者您可以使用vsnprintf() - 首先要确定大小,然后实实在在地扩大输入一个输出串。

#define FIXED_SIZE 64 

void write_log(const char *format, ...) 
{ 
    // set up a fixed-size buffer and a pointer to it 
    char fixedSizeBuffer[ FIXED_SIZE ]; 
    char *outputBuffer = fixedSizeBuffer; 

    // no dynamic buffer yet 
    char *dynamicBuffer = NULL; 

    // get the variable args 
    va_list args1; 
    va_start(args1, format); 

    // need to copy the args even though we won't know if we 
    // need them until after we use the first set 
    va_list args2; 
    va_copy(args2, args1); 

    // have to call vsnprintf at least once - might as well use a small 
    // fixed-size buffer just in case the final string fits in it 
    int len = vsnprintf(fixedSizeBuffer, sizeof(fixedSizeBuffer), format, args1); 
    va_end(args1); 

    // it didn't fit - get a dynamic buffer, expand the string, and 
    // point the outputBuffer pointer at the dynamic buffer so later 
    // processing uses the right string 
    if (len > sizeof(fixedSizeBuffer )) 
    { 
     dynamicBuffer = malloc(len + 1); 
     vsnprintf(dynamicBuffer, len + 1, format, args2); 
     outputBuffer = dynamicBuffer; 
    } 

    va_end(args2); 

    // do something with outputBuffer 

    free(dynamicBuffer); 
    return; 
} 
+2

好主意,但有些系统不能使用'args'两次(例如IBM Z系列上的linux),所以你应该添加'va_copy()' –

+0

@IngoLeonhardt好点。我会更新 –

+0

一个非常稳固的解决方案,经过测试,可以开箱即用:D。 – svenema

1

我在做什么错?

传递一个va_list arg

write_log(format, arg, (char *)NULL); 

是不一样的传球数char*

write_log("Welcome to %s%s", sDeviceName, sFiller1, (char *)NULL); 

你不会得到各地通过定点标识传递的参数的结束,这是一个(char*) NULL或任何你决定使用。


替代将

  • 传递参数的数目明确,也许第二个参数
  • 解析为转换说明的格式字符串,其实模仿什么printf一样。
1

如果你只想以确保所有电话在年底收到setinel,使用宏:

#define WRITE_LOG(...) write_log(__VA_ARGS__, (char*)0) 

这确保了总有一个最后的额外0。请注意0​​。它在C标准中没有详细说明这些解决方案的表达方式。常见情况是0(void*)0。所以在64位体系结构中,它们可能有不同的宽度(第一个是32位,第二个是64位)。对于可变参数在这里接收错误的宽度可能是致命的。因此我使用(char*)0这是你的功能似乎期望的类型。 (但(void*)0也会在这种特殊情况下执行。)

+0

考虑到目标平台是“一个小型嵌入式平台”,我建议OP验证'(char *)0'实际上是在使用之前使用的编译器的'NULL'指针。我不认为在C11之前'(char *)0'必须是一个有效的'NULL'指针。 –

+0

@AndrewHenle,你可能是指空指针而不是'NULL'指针。在标准意义上,'(char *)0'不是一个“空指针常量”,这里你实际上需要'(void *)0'。但是对于手头的情况来说,这是无关紧要的,它是程序期望的正确类型的空指针。 –

+0

它有时可以工作,有时会崩溃..看起来像依赖于参数的数量..真的不明白为什么。 – svenema