2010-02-09 54 views
3

我經常使用的便利函數返回指向靜態緩衝區是這樣的:在地方作爲參數傳遞給其他功能不便靜態變量

char* p(int x) { 
    static char res[512]; 

    snprintf(res, sizeof(res)-1, "number is %d", x)); 

    return res; 
} 

,並使用它們:

... 
some_func(somearg, p(6)); 
.... 

然而,除了不是線程安全的(可能還有更多的原因),這種「便利」具有惱人的缺點:

some_func(somearg, p(6), p(7)); 

以上顯然不會做我想做的,因爲最後兩個參數將指向相同的內存空間。我希望能夠讓上述工作正常工作,而不會有很多麻煩。

所以我的問題是:

是否有某種神奇的辦法,我已經錯過了完成我想要的東西沒有做繁瑣的配置&釋放?

***** UPDATE 2010-04-20 *****

無恥插頭:看我自己的答案here

我想這會工作,但它也接壤矯枉過正。意見?

+1

在C領域內,沒有什麼好的答案。在某些情況下,您可以通過要求調用者通過緩衝區來解決問題,但這會失去很多便利。 – 2010-02-09 18:16:57

+2

這不僅僅是線程不安全,你會得到意想不到的副作用。假設你連續調用兩次函數,將結果分配給兩個不同的指針。當你輸出兩個字符串時,你會得到第二個值兩次,因爲兩個指針指向同一個緩衝區。 – 2010-02-09 18:55:59

回答

0

我找到了一種替代方法。事情是這樣的:

#define INIT(n) \ 
int xi = 0; \ 
char *x[n]; \ 

#define MACRO(s) \ 
(++xi, xi %= sizeof(x)/sizeof(*x), x[xi] = alloca(strlen(s)+1), strcpy(x[xi], (s)), x[xi]) 

,我可以這樣調用:

INIT(2); 
some_func(somearg, MACRO("testing1"), MACRO("testing2")); 

所以緩衝區是在棧上,而無需任何釋放。它甚至是線程安全的。

+0

我已經無恥地把這個正確的答案,直到有人來到這裏,並挑戰它:) – joveha 2010-05-28 13:05:58

+0

TBH,這是一個很奇怪的做法。我會跳過INIT宏,然後直接調用MACRO兩次,每次都在自己的行上。就像這樣:'char * s1 = MACRO(「hello」); char * s2 = MACRO(「world」); some_func(s1,s2);'然後,我會在沒有宏的情況下做到這一點。 – 2011-08-09 00:29:14

2

讓調用者提供緩衝區(以及緩衝區的大小)。它是線程安全的,並且緩衝區通常可以放在堆棧中,所以不會導致堆分配的開銷。

8

那麼,一個廣泛使用的方法是把準備結果的內存緩衝區的責任放在調用者上。來電者可以選擇最喜歡的任何方法。

在你的情況寫你p作爲

char* p(char *buffer, size_t max_length, int x) { 
    snprintf(buffer, max_length, "number is %d", x); 
    return buffer; 
} 

,並把它作爲

char buffer1[512], buffer2[512]; 
some_func(somearg, p(buffer1, sizeof buffer1 - 1, 6), p(buffer2, sizeof buffer2 - 1, 7)); 

這種方法至少有一個明顯的缺點,但:在一般情況下,主叫方不會事先怎麼知道需要爲緩衝區分配許多字符。如果一個好的常量編譯時間值是可用的,那麼它很容易,但是在更復雜的情況下需要額外的努力,比如提供某種「預計算」功能,它將所需的緩衝區大小作爲運行時間值返回。 (snprintf函數實際上是這樣工作的:您可以用空緩衝區指針和零緩衝區大小調用它,只是爲了確定緩衝區大小進行虛擬運行)。

0

總之,沒有。 C不提供任何形式的自動堆管理,因此您自己可以跟蹤分配的內存。標準的C類解決方案是讓調用者提供一個緩衝區,而不是在內部分配一個緩衝區。儘管這只是追蹤記憶的責任,但它往往會在更方便的地方結束。我想,如果你想在C中獲得垃圾收集的形式,你可以查看Boehm's conservative garbage collector

2

只要你明白這是線程不安全的,並且只要你的邏輯期望「便利」方法返回的值僅在對[可能各種]方法調用的持續時間內有效,你可以用兩種不相關和可能互補的方式來延伸這種便利。

  • 添加一個額外的參數,以方便方法,所以這些可以通過-optionally-返回值的容器(還添加了size_of_passed_buffer說法,如果這樣的大小不能隱式設置)。每當調用者提供緩衝區時,使用它,否則使用靜態緩衝區。
    順便說一句,如果傳遞給便捷方法的緩衝區是局部變量,那麼在調用便捷方法的子例程的生命週期之後,它們的分配將自動(並充分)管理。

  • 使用循環緩衝區,允許在緩衝區元素重用之前進行給定次數的調用。
    這樣的緩衝區也可以是全局的,即與多個「便利」方法(當然也需要a)共享,並且b)共享指向緩衝區中的下一個可用字節/元素的指針。


實現這一切似乎是

  • 了很多工作,(爲了方便邏輯),也
  • 一個潛在的幾個bug /問題

但是,只要

  • (s)爲(被)足夠大的緩衝器,並且
  • 使用這些方便的方法理解「的遊戲規則」的邏輯,

此圖案用品簡單化的自動化堆管理系統,這是一個很好的東西在C(它不像Java,.NET和其他系統不提供內置的基於GC的堆管理)

+0

這聽起來很合適,如果數據必須通過幾個接口返回和回調持續存在,並且開銷非常重要且需要設置,例如實時驅動程序。但是,調用者在堆棧上分配緩衝區有什麼優勢?這只是要求緩衝區溢出。 – Potatoswatter 2010-02-09 18:54:35

+0

有趣的方法。它會提供所尋求的便利性,其代價是實施這個以及它的繼承危險(希望只有一次);) – joveha 2010-02-09 18:56:22

0

如果參數助手總是字面值在這個例子中),你可以使用一個宏:

#define P(NUMLIT) ("number is " #NUMLIT) 

... 
somefunc(somearg, P(6), P(7)); 
... 

預處理器創建一個從宏參數NUMLIT的字符串,並追加到「號碼」,以建立一個單一的字符串文字,正如

"this" " " "string" 

被髮射作爲一個單一的字符串中,「這串」。