2008-11-12 66 views
2

我有一個DLL,它必須是由C等可用的,所以我不能使用字符串對象等作爲一個正常的可以,但我不知道如何做到這一點安全..返回動態分配的內存,從C++到C

const char *GetString() 
{ 
    std::stringstream ss; 
    ss << "The random number is: " << rand(); 
    return ss.str().c_str(); 
} 

當ss掉下堆棧時,c字符串是否會被銷燬?我假設如此...

另一種選擇可能是在堆上創建一個新的字符串,但到底是怎麼回事解除分配呢?

const char *GetString() 
{ 
    std::stringstream ss; 
    ss << "The random number is: " << rand(); 
    char *out = new char[ss.str().size()]; 
    strcpy(ss.str().c_str(), out); 
    return out;//is out ever deleted? 
} 

指向其他事物以及字符串的指針也是一樣。

回答

8

因爲你返回一個指針到第一個變種不起作用堆棧對象將被破壞。(更確切地說,你返回一個指向堆內存的指針,它將被刪除()。)更糟的是,如果沒有人覆蓋內存,它甚至可能會工作一段時間,這使得調試非常困難。 )

const char *GetString() 
{ 
    return "a static string in DATA segment - no need to delete"; 
} 

你第二個變體具有恢復記憶的新的(分配的問題轉化爲C:

接下來,你不能,除非你返回一個指向靜態字符串這樣返回一個const char *程序將調用free()。這些可能不兼容。

如果返回一個字符串,C,有做2路:

char *GetString() 
{ 
    std::stringstream ss; 
    ss << "The random number is: " << rand(); 
    return strdup(ss.str().c_str()); // allocated in C style with malloc() 
} 

void foo() 
{ 
    char *p = GetString(); 
    printf("string: %s", p)); 
    free(p); // must not forget to free(), must not use delete() 
} 

或:

char *GetString(char *buffer, size_t len) 
{ 
    std::stringstream ss; 
    ss << "The random number is: " << rand(); 
    return strncpy(buffer, ss.str().c_str(), len); // caller allocates memory 
} 

void foo() 
{ 
    char buffer[ 100 ]; 
    printf("string: %s", GetString(buffer, sizeof(buffer))); // no memory leaks 
} 

取決於你的內存處理政策。

通常,您不能在C++中返回指向自動對象的指針或引用。這是許多C++書籍中分析的常見錯誤之一。

1

第一實際上行不通,因爲stringstream的重新分配它在破壞空間。所以,如果你試圖去參考該指針有一個很好的機會,你的程序會崩潰。

你提到的第二個選項是它是如何做,通常和功能的用戶需要解除分配的空間。 I如果這是使用功能的C程序請確保您使用malloc分配()和免費有免費()

另一種選擇是返回一個靜態的字符數組的地址。如果事先知道長度的上限,這是相關的。更重要的是這應該是僅用於如果沒有機會,功能將是從兩個不同的線程同時調用,因爲使用的是靜態數組本質上讓你的功能非reentrant

+0

好的,以及我的dll是python的actauly,那麼最好的方法是做什麼呢?我是否應該將dll函數封裝在說「Call dll function; Call dll deallocate function」的python函數中?我假設python使完整的新字符串,而不是隻是包裝周圍的對象?請問 – 2008-11-12 08:30:26

1

很明顯嘛,任何時候你正在返回指針的函數內部分配的內存解除分配必須來自外部,除非你正在使用的垃圾收集。如果你不希望這樣做,分配一個字符緩衝液B​​安伏調用GetString的()和改變原型

INT get_string(爲const char *緩衝區);

然後填滿緩衝區。但是返回一個指向malloced數據的罰款是沒問題的。

+0

,通過緩衝區的大小! – quinmars 2008-11-12 08:52:38

+0

int get_string(const char * buffer,int bufferSize); < - 像這樣...... – xan 2008-11-12 09:27:26

0

如果SS聲明爲static就可以避免這個問題。如果你的程序在單線程環境中運行,這可能是一個很好的解決方案。

+0

但是一旦函數被第二次調用,結果就會失效。從那一刻開始,只有第二次調用的結果纔有效。 – wimh 2008-11-12 08:58:31

0

如果你想安全地返回它,你必須在堆上分配字符串,也可以用malloc()i.s.o分配。 new()在編寫C函數時。當你返回指針時(並且與C++不同,在C中,你沒有多次真正的選擇),釋放總是一個問題。沒有確切的解決方案。

處理這個我已經看到了在相當長的一段API的呼籲所有的函數或者

一種方式
CreateString() 

內存時需要調用者被釋放,並

GetString() 

時不一個問題。

這是什麼,但當然是萬無一失的,但只要有足夠的紀律這是我見過的要誠實是最好的方法......

0

如果線程安全並不重要,

const char *GetString() 
{ 
    static char *out; 
    std::stringstream ss; 
    ss << "The random number is: " << rand(); 
    delete[] out; 
    char *out = new char[ss.str().size()]; 
    strcpy(ss.str().c_str(), out); 
    return out;//is out ever deleted? 
} 

這時可把功能拿過來重新分配字符串的責任。

如果線程安全是很重要的,

那麼最好的方法是在把它作爲一個參數,如,

void GetString(char *out, int maxlen); 

我看到這是發生在舊的非thread-時安全的API更改爲線程安全。

0

函數調用後,你會希望調用者負責字符串的內存(尤其是取消分配)。除非你想使用靜態變量,但有龍!乾淨地做到這一點,最好的辦法是讓呼叫者做內存的分配在首位:

void foo() { 
    char result[64]; 
    GetString(result, sizeof(result)); 
    puts(result); 
}

然後的GetString應該是這樣的:

int GetString(char * dst, size_t len) { 
    std::stringstream ss; 
    ss << "The random number is: " << rand(); 
    strncpy(ss.str().c_str(), dst, len); 
}

傳遞的最大緩衝區長度並使用strncpy()將避免意外覆蓋緩衝區。

3

多年來下煮沸此下降到2種的標準方法:

  • 調用者傳遞緩衝液中。
    這有三個版本。
    版本1:傳遞緩衝區和長度。
    版本2:文檔指定預期的最小緩衝區大小。
    版本3:飛行前。函數返回所需的最小緩衝區。調用者首次使用NULL緩衝區調用兩次。
    • 例如:閱讀()
  • 使用靜態緩衝區是有效的,直到下一次調用。
    • 例子:tmpname()

一些非標準的人返回的內存,你必須明確地釋放

  • 的strdup()彈出在腦海中。
    常見擴展但實際上並不在標準中。
0

答案爲止如果結果所需的緩衝區的長度是未知的,可以將呼叫之間切換,即使採用相同的參數(如閱讀並沒有解決一個非常顯著的問題,做到即什麼來自數據庫的價值),所以我提供了我認爲是處理這種情況的最佳方法。

如果大小事先不知道,考慮通過一個回調函數來你的函數,它接收const char*作爲參數:

typedef void (*ResultCallback)(void* context, const char* result); 

void Foo(ResultCallback resultCallback, void* context) 
{ 
    std::string s = "...."; 
    resultCallback(context, s.c_str()); 
} 

ResultCallback實現可分配所需的存儲和複製緩衝區由result指出。我假設C,所以我沒有明確地向/從void*投射。

void UserCallback(void* context, const char* result) 
{ 
    char** copied = context; 
    *copied = malloc(strlen(result)+1); 
    strcpy(*copied, result); 
} 

void User() 
{ 
    char* result = NULL; 

    Foo(UserCallback, &result); 

    // Use result... 
    if(result != NULL) 
     printf("%s", result); 

    free(result); 
} 

這是最便攜的解決方案,即使是最難預先處理返回字符串大小的最困難情況。

0

有多種方法,開發了隨着時間的推移,從函數返回的數據的可變的量。

  1. 調用者傳遞緩衝液中。
    1. 的neccessary大小記錄,並沒有通過,太短緩衝區Undefined Behaviorstrcpy()
    2. 的neccessary大小是記錄和傳遞,錯誤是由返回值信號:strcpy_s()
    3. 的neccessary大小是未知的, snprintf
    4. 的neccessary尺寸是未知的,並且不能被查詢,儘可能適合於傳遞大小的緩衝區返回:但可以通過調用與緩衝長度爲0的函數進行查詢。如果neccessary,額外的呼叫必須由得到休息:fread
    5. 的neccessary大小是未知的,無法查詢,並通過過小的緩衝區Undefined Behavior。這是一個設計上的缺陷,因此功能已被棄用/在新版本中刪除,並在這裏剛纔提到的完整性:gets
  2. 調用者傳遞一個回調:
    1. 的回調函數獲取上下文參數:qsort_s
    2. 的回調函數獲取沒有上下文參數。獲取文意法寶:qsort
  3. 調用者傳遞一個分配器:在C標準庫未找到。但是,所有分配器感知的C++容器都支持這一點。
  4. 被調用約定指定了釋放器。調用了錯誤的一個是Undefined Behaviorfopen - >fclosestrdup - >free
  5. 被叫方返回它包含釋放器的對象:COM-對象std::shared_ptr
  6. 被叫方使用一個內部的共享緩衝器:asctime

通常,無論用戶何時需要猜測尺寸或在手冊中查看尺寸,他有時會錯誤地認爲它是錯誤的。如果他沒有犯錯,以後的修改可能會使他的謹慎工作失效,所以他曾經是對的並不重要。無論如何,這種方式在於madness (UB)

其餘的,選擇最舒適,最有效的一個,你可以。