2016-06-21 55 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