2011-02-15 20 views
10

當你在C中的char指針上調用strtok時會發生什麼,我有些困惑。我知道它修改了字符串的內容,所以如果我在名爲'line'的變量上調用strtok,它的內容將會改變。假設我遵循以下方法:如何正確使用C中的strtok,所以沒有內存泄漏?

void function myFunc(char* line) { 

    // get a pointer to the original memory block 
    char* garbageLine = line; 

    // Do some work 
    // Call strtok on 'line' multiple times until it returns NULL 
    // Do more work 

    free(garbageLine); 
} 

進一步假定'行'在傳遞給myFunc之前是malloced。我是否應該在使用strtok之後釋放原始字符串,還是爲我們完成這項工作?另外,如果'行'不是malloced,我試圖使用上面的函數會發生什麼?改爲執行以下操作更安全嗎? (假設程序員不會打電話免費的,如果他知道了行不malloced)

調用

char* garbageLine = line; 
myFunc(line); 
free(garbageLine); 

函數定義

void function myFunc(char* line) { 
    // Do some work 
    // Call strtok on 'line' multiple times until it returns NULL 
    // Do more work 
} 

回答

8

strtok()不會釋放任何東西,因爲它不知道字符串的存儲位置。它可能在堆棧或堆上,它不知道或關心! :)

改爲更安全嗎?

你的第二個例子是好得多,因爲它簡化了myFunc的(),並使其在更多情況下是有用的功能並不需要知道其中的字符串分配。通過從myFunc()中移除對free()的調用,您可以使用該函數從堆棧或堆中解析字符串。調用者分配內存,調用者釋放內存!

延伸閱讀: strtok()

0

這是什麼都做strtok()?如果你分配內存,你需要釋放它。你的應用程序決定分配和釋放內存取決於你。但是,如果將內存傳遞給strtok(),那麼只要分配內存或釋放內存時就沒有區別。

+0

我在這兩種方法中解除分配內存的方式是正確的嗎?我想知道的是,如果strtok隱式地釋放內存。請記住,我不是這門語言的專家。 – user246392 2011-02-15 05:54:04

+0

@ user246392:正如我在我的回答中所說的,調用`strtok()`對分配的內存沒有任何影響。它不分配或釋放任何內存。這與分配或釋放內存的問題無關。通常,從分配它的同一個例程中釋放內存是有意義的,但如果這不適合您的應用程序,那麼真正重要的是釋放內存。 – 2011-02-15 06:18:07

+0

@ user246392無法確定您是否正確釋放內存,因爲我們無法知道您是如何分配內存的。我可以說,你幾乎肯定是做錯了。 – 2011-02-15 09:15:11

-1

只要您只使用「char *」類型,由於緩衝區溢出而無法安全地執行任何字符串操作100%。

就內存泄漏而言,如果分配它,最好的做法是在同一個塊中刪除它(如果可能的話)。這意味着如果您提供了一個strtok實現,它應該刪除它在內部分配的所有內存,但不會刪除傳遞給該方法的任何內存。

原因有很多,但基本上不能保證其他人可能使用你的方法可以訪問它的源代碼,所以他們沒有辦法合理地準備好看他們分配的內存消失(導致代碼部分發生分段錯誤)。

要做一個「安全的」非緩衝區溢出字符串操作,您還需要有一個最大計數值。例如,strncpy(...)採用「最大字符數」參數。這可以防止某些類型的攻擊,其中字符串內存分配的內存「下游」可以被稍後被程序的某個其他部分解釋爲代碼的數據填充。

這意味着您的myFunc(char* line)需要簽名myfunc(char* line, int max_chars)並且您的所有後續操作都應該保證可以使用的內存量爲max_Chars + 1。加號是保留可能不存在的終止空值。在內部,你需要確保所有的操作,比如strcpy只在第一個max_chars上運行(strncpy這樣做)。

這意味着您最終總是會傾向於緩衝區溢出安全性的字符串操縱器的「n」(字符數)版本。再加上一個強大的「在你分配的塊中釋放」練習,你將避免90%的大部分與字符串有關的編程錯誤。

有時您會希望爲以後存儲分配的字符串(也許在地圖中)。對於這些情況,您需要在離開程序之前注意清除地圖,或者至少採取其他措施以確保地圖不會將未使用的內存保留超過合適的時間。

3

在你的問題的評論,你說你「呼籲‘行’多次strtok的,直到它返回NULL」。這聽起來好像你可能會錯誤地使用strtok。你第一次打電話時,應該用'line'作爲參數來調用它;在後續調用中,您應該將它傳遞給NULL。看看下面的例子:

void function myFunc(char* line) { 
    char *segment; // This will point at each delimited substring in turn. 

    segment = strtok(line, " "); 

    // Do something with segment. 

    segment = strtok(NULL, " "); 

    // Do something with the new segment. 

    free(line); 
} 

正如DrTwox說,雖然,你的第二個例子是更好的 - 「線」應該由malloced它(或沒有)相同的上下文中解脫出來,所以調用free( )不屬於這個功能。而且你最好循環它 - 是這樣的:

void function myFunc(char* line) { 
    char *segment; 

    segment = strtok(line, " "); 

    while (segment != NULL) { 
    // Do something with segment. 

    segment = strtok(NULL, " "); 
    } 
} 

調用是這樣的:

char *line = malloc(20*sizeof(char)); 

// Check that malloc succeeded here. 
// Put some data into 'line'. 

myFunc(line); 

free(line); 

// No 'garbageLine' required. 

該函數strtok的工作方式是有點複雜的解釋,但你有重要零件 - 它不分配或釋放任何內存。相反,它通過修改傳遞給它的字符串來工作。

1

strtok沒有比strlen更多的釋放內存。你爲什麼期望它?它會釋放什麼內存?也許你認爲strtok需要釋放內存,因爲它存儲了一個NUL,但內存的內容是不相關的。當你分配內存時,分配器跟蹤你分配的塊的大小,當你釋放它時,整個塊被釋放。