2010-09-22 65 views
28

我正在尋找一個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; 
} 

但在調用的sprintf()與NULL指針時,這immediatelly導致段錯誤。

因此,任何想法,解決方案或技巧?一個放在公共領域的sprintf()類似的解析器的小實現已經足夠了,然後我可以自己完成。

非常感謝!

+1

誰給了你這個建議可能意味着你應該使用'snprintf',而不是'sprintf'。 – 2010-09-23 00:08:58

回答

10
  1. 如果可能,請使用snprintf - 它提供了一種簡單的方法來測量將生成的數據的大小,以便分配空間。
  2. 如果你真的不能這樣做,另一種可能性是打印到臨時文件與fprintf獲取大小,分配內存,然後使用sprintf。 snprintf絕對是雖然是首選的方法。
23

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

asprintf()將是我選擇的功能 - 但不幸的是,它的非標準和不便攜性 - 差! – 2010-09-23 06:53:49

+1

@ sha-shamen - 你所要求的從定義上來說是非標準的,而不是便攜式的。獲取'asprintf'的源代碼,並在需要時將其引入到項目中,或者獨立重新實現它。 – bstpierre 2010-09-23 18:22:44

+11

我還沒有聽說過一個返回指針的'asprintf()'。 GNU和BSD(以及由gnulib和libstrl提供的)具有與等價的'printf()'調用相同的返回值,並將一個指針指向第一個參數。所以,char * s; int ret = asprintf(&s,「%s%d%s!」,a,c,b);'在'ret == -1'時出錯。想知道,什麼系統/庫提供了一個'asprintf()',它返回一個像這個答案中的指針? – binki 2014-02-07 04:14:43

4

GLib庫提供了一個g_strdup_printf函數,它正是你想要什麼,如果鏈接到的GLib的是一種選擇。從文檔:

標準C sprintf() 功能,但更安全的類似,因爲它 計算最大空間所需 並分配內存來存放 結果。當不再需要 時,返回的字符串應爲 ,並用g_free()釋放。

+0

你好,謝謝!但這只是glibc,我需要一個獨立於平臺的解決方案。所以也許最好自己做這個? – 2010-09-23 06:42:15

+3

GLib(GTK +的基礎),而不是GNU C庫(glibc)。但它相當於glibc的asprintf。 – 2013-10-16 16:46:40

+0

glib是獨立平臺 – Julius 2015-05-18 21:32:13

23

這是原來的答案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); 
    snprintf(buffer, needed, "%s: %s (%d)", msg, strerror(errno), errno); 
    return buffer; 
} 
+3

不應該爲1加上''needed'來解釋終止空字符嗎? – beldaz 2016-01-24 04:03:55

+2

沒有發現第一行末尾的+1(它在可見區域之外):'size_t needed = snprintf(...)+ 1;' – user2421739 2017-02-10 18:29:58

9

如果你可以用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; 
} 

您可以執行一些預處理器魔術,並僅在不支持它們的系統上使用您的函數版本。

+1

您只能將'va_list'變量傳遞給一個功能。要在'vasprintf()'中使用'vsnprintf()'兩次,你應該使用'va_copy()'。 – 2014-06-30 18:07:35

+0

@IliaK。現在是否正確? – 2014-07-01 06:42:42

+1

如果您在從'vasprintf()'返回之前添加'va_end(ap1)'(例如在調用'vsnprintf()'之後)。 – 2014-07-10 12:09:27