2010-07-20 87 views
4

當將char *作爲參數傳遞給函數時,被調用的函數是否應該對該字符串進行自由處理?否則,數據會「丟失」,程序會泄漏數據。或者,編譯器以特殊的方式處理char *,以避免每個人都必須一直空閒,並自動刪除它,使其超出範圍?我將「字符串」傳遞給該函數,因此不會將實例傳遞給已存在的char *。或者應該使用char []來代替?只是覺得非常愚蠢的是爲參數輸入設置一個固定的限制。char *作爲函數的參數C

+1

邁克爾的良好答案告訴你考慮將字符串存儲在所謂的「靜態存儲器」中 - 這是存儲器在程序映像加載時由操作系統初始化的存儲器。 Naveen的例子和free/malloc自己使用「免費商店」,有時稱爲「堆」。還有「自動存儲」通常作爲「堆棧」來實現。其他存儲可能性是操作系統映射的ABI內存區域,內存映射文件等。一個例子可能是操作系統調用返回當前目錄字符串 - 這個「char *」可能指向OS的每線程數據結構。 – 2010-07-20 06:39:32

+0

您通常不應該爲參數輸入設置固定限制。相反,讓調用者給你一個指針,讓*他們*告訴你*字符串是多長。 – bta 2010-07-20 16:47:22

回答

15

請牢記這一簡單原則:「始終釋放內存在您分配的相同級別」。換句話說,函數不應該嘗試釋放它本身沒有分配的內存。一個簡短的例子來闡明這一點:

#include "graphics.h" 

// The graphics API will get a Canvas object for us. This may be newly allocated 
// or one from a pool of pre-allocated objects. 
Canvas* canvas = graphics_get_canvas(); 

// If draw_image() frees canvas, that violates the above principle. 
// The behavior of the program will be unspecified. So, just draw the image 
// and return. 
draw_image (canvas); 

// This is also a violation. 
// free (canvas) ; 

// The right thing to do is to give back the Canvas object to the graphics API 
// so that it is freed at the same 'level' where it was allocated. 
graphics_return_canvas (canvas); 

注意,該函數的名稱不是graphics_free_canvas()之類的東西,因爲API可以選擇釋放它或將其返回到池重用。關鍵是,假定我們沒有創建資源的所有權,這是一個非常糟糕的編程實踐,除非我們另有具體說明。

+4

這是如何與malloc()和strcmp()的喜歡?他們打算在不同的層面上被釋放。事實上,free()本身不會釋放內存:-) – paxdiablo 2010-07-20 06:38:42

+0

@paxdiablo,strcmp()?你是說strdup()?如果是這樣,我不明白他們是如何在不同的層面上獲得釋放的。 strcmp()當然不會分配任何東西。 – 2010-07-20 06:41:41

+0

@HH,我不認爲他真的是這個意思。答案中概述的原則(清理你自己的混亂)是健全的。避免我以前的一位同事使用的構造函數,他的函數返回一個char *給(文本)答案,在這種情況下,您必須釋放它,或者返回錯誤消息,在這種情況下,您不得釋放它。 – 2010-07-20 06:55:35

7

這聽起來像你問這個用法:

void foo(char* str); 
foo("test string"); 

這是一個特殊情況; "test string"是存儲在可執行文件中的字符串表中的常量字符串,不需要釋放。 foo實際上應該採取const char*來說明,並允許字符串文字存儲在非恆定char* S IN C++

+0

爲了擴大這一點,字符串文字「測試字符串」以* static extent *的char(C++中的const char)的12元素數組的形式存在,這意味着它的內存在程序啓動時分配並保存到程序退出。當您將字符串文字作爲函數參數傳遞時,編譯器**不會**創建​​字符串的新實例;相反,它傳遞一個指向現有實例的指針。 – 2010-07-20 18:36:10

7

已被棄用,軟鍵功能是否應該做一個free與否取決於誰擁有的字符串。此代碼是完全有效的,不會導致任何內存泄漏:

int main() 
{ 
    char* s = malloc(.....); 
    f(s); 
    free(s); 
} 

free可以內部功能f,如果它需要字符串的所有權進行爲好。但是請注意,由於您假定傳遞給函數f的字符串始終使用malloc或相關函數在堆上進行分配,因此這很危險。如果用戶將指針傳遞給堆棧中分配的字符串,則程序將表現出不可預測的行爲。

一般情況下,編譯器不會對字符串的內存管理進行任何特殊處理。從編譯器的角度來看,它只是一堆字符。

+1

不只是一個堆棧分配的字符串。如果你傳遞'f()'字符串,並且'f()'試圖釋放它,你也會遇到麻煩。 – ptomato 2010-07-20 08:58:47

0

有時API需要一個分配的緩衝區,並且它的功能所有者可以調用該API釋放它。

myFunc() 
{ 
char *error = malloc(<max size of error string>); 
foo(error); 
//Free the pointer here 
free(error); 

} 

像GLIB一些API API的預期指針的聲明的變量

myFunc() 
{ 
GError *error; 

glib_api(&error); 
if (error) 
{ 
printf("Error %s", error-> message); 
// can use glib API to free if error is NON NULL but message is allocated by GLIB API 
g_error_free(error); 
} 
} 

所以,即使你還沒有分配的內存,你需要做的釋放,而使用標準庫的變量地址。

分配的一塊內存(如果未釋放)會導致多進程環境中的內存較小,從而降低系統的性能。

1

看來你已經習慣了OOP風格。我不喜歡OOP,對我來說,如果我在分配後獲得一個對象的副本,會很奇怪。在這種情況下,字符串在內存中的某處,其地址以char *形式發送,而不是整個字符串。 另外,請注意,您可以釋放()只有malloc()返回的指針,並且只有一次。

0

用普通的char *,我會建議總是編寫一個策略的代碼,調用者「擁有」字符串,並負責釋放它,如果它是由malloc獲得的。另一方面,當然可以設想C中的「僞值傳遞」字符串對象,作爲結構實現,其中策略規定在傳遞字符串時必須放棄字符串的所有權(或者先複製並傳遞重複)作爲論據。如果這個實現使用引用計數的存儲來傳遞對象只是對存儲的引用,那麼「重複」操作僅僅是一個引用計數增量加上簡單的包裝器結構分配(或即使通過值結構)。

0

指向char作爲函數變量的指針是指向相同變量的地址,除非它已被替換爲常量字符串。你的問題不能用簡單的是/否指導來解釋;它取決於上下文。在下面的代碼中,分別在堆和堆棧上分配的結構通過引用傳遞以及字符串char *,並將數據插入到結構中。請注意malloc在使用時的不同,但函數的工作原理完全相同。

#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 

// struct with all data within 
typedef struct d 
{ 
int number; 
char name[50]; 
}data; 
// struct with dynamic char * 
typedef struct d2 
{ 
int number; 
char *name; 
}dynamic_data; 

// generic function placing data into struct 
void InsertData (data * out, int a, char * b) 
{ 
    out->number = a; 
    strcpy(out->name, b); 
} 

// generic function placing data into second struct 
void InsertData2 (dynamic_data * out, int a, char * b) 
{ 
    out->number = a; 
    strcpy(out->name, b); 
} 


int main (void) 
{ 
    char * text = "some string\0"; 
    int n = 20; 
    // allocated struct 
    data stuff; 

    dynamic_data stuff2; 

    dynamic_data * stuff3; 
    // need to allocate pointer within struct only 
    stuff2.name = (char *) malloc(50 * sizeof(char)); 

    // heap allocated struct 
    stuff3 = (dynamic_data *) malloc(50 * sizeof(dynamic_data)); 
    // heap allocated sub element char * 
    stuff3->name = (char *) malloc(50 * sizeof(char)); 


    // this is the data 
    printf ("Pre insertion data\n"); 
    printf ("s=[%s]\n", text); 
    printf ("n=%d\n", n); 

    // this is the function insertting 
    InsertData (&stuff, n, text); 
    printf ("Post insertion data\n"); 
    printf ("stuff.name=[%s]\n", stuff.name); 
    printf ("stuff.number=%d\n", stuff.number); 

    // this is the function inserting 
    InsertData2 (&stuff2, n, text); 
    printf ("Post insertion data\n"); 
    printf ("stuff.name=[%s]\n", stuff2.name); 
    printf ("stuff.number=%d\n", stuff2.number);  

// 
// This is the segfault version - if nothing was allocated for pointers into 
// this function scope, it would crash 


    // this is the function insertting under a heap allocated 
    InsertData2 (stuff3, n, text); 
    printf ("Post insertion data - dynamic version\n"); 
    printf ("stuff3->name=[%s]\n", stuff3->name); 
    printf ("stuff3->number=%d\n", stuff3->number); 

    // free in reverse order 
    free(stuff3->name); 
    free(stuff3); 
    free(stuff2.name); 
    return 0; 
}