2010-02-26 143 views
5

我遇到了很多在一個遺留應用程序中返回字符指針的函數。 其中一些返回指向本地字符數組的指針。看來經過多次調用將導致崩潰(不會馬上!)請參見下面返回字符指針的函數

char *f1(){ 
    char buff[20]; 
    char *ptr; 

    ---- 
    ---- 
    ptr=buff; 
return ptr; 
} 

--- 
--- 

f2(f1()); 

F1()返回一個指向局部變量的使用情況,然後把它傳遞給另一個函數。我在MS DEV中使用_DEBUG模式編譯時直接遇到了崩潰。但在發佈模式下,它不會造成立即崩潰,但可能會在進行大量此類調用之後發生。

當我修改了下面的用法時,它沒有任何問題。以下用法 安全嗎?

strcpy(arr,f1()); /* arr is fixed char array*/ 
f2(arr); 

回答

12

不,這是不確定的行爲。它恰好適用於您的情況,但可能隨時停止工作。

+1

+1非常多。 – Tom 2010-02-26 12:46:46

+0

它正常工作的原因是在x86上,buff數組從低位地址填充到高位地址,並且可能不會被strcpy的堆棧使用所觸及。不要這樣做:由於信號和中斷等異步事件,堆棧使用可能會有所不同。 – 2010-02-26 12:57:25

3

不,這是不安全的。只需調用strcpy就可以修改堆棧,以便稍後導致問題,因爲返回地址和參數可能會覆蓋數組。

2

f1函數返回一個臨時函數(buff),該函數在函數返回時被釋放。你需要在函數內部使用malloc()。

+0

這幾乎可以確保內存泄漏,並帶有一些對函數的非引用。 – 2010-02-26 15:12:48

+0

你當然可能需要一個指向預先分配的內存的指針作爲輸入參數,然後讓函數填充它,但是我們不再有相同的函數簽名。 – Zitrax 2010-02-26 21:29:04

1

不..還是不安全。

當你在做strcpy(arr,f1());時,用作第二個參數的指針已經指向一個不存在的數組。

0

其實,最好的辦法是修改f1()以使用malloc()。您的解決方案沒有接近定義的行爲。

+1

好的,那麼所有來自f1()的調用者都應該在使用後正確釋放它。對? – cobp 2010-02-26 12:53:12

+0

是的,他們應該。 – moatPylon 2010-02-26 15:34:59

2

永遠不會返回指向局部變量的指針。它可能在某些情況下有效,但總的來說,你會遇到很多問題。 相反:

它返回一個指向分配的內存,並且返回的緩衝區必須由調用
  • 添加緩衝劑和尺寸參數,並在緩衝區填滿釋放
    • 文件的功能(這是什麼是Win32 API中一般都做)
    • 如果使用C++,使用的std :: string
  • +0

    我覺得這兩個解決方案。 1.在f1()中使用malloc。在這種情況下,所有呼叫者在使用後都應該正確地釋放內存。應避免使用像 f2(f1()),因爲它不會重新執行釋放內存的句柄。 2.將分配的ptr傳遞給該函數,以便f1()可以複製其中的文本。現有的用法如f2(f1())在這種情況下也是不可能的。 您更喜歡哪種解決方案?無論如何,它需要對現有應用程序進行很多更改。因爲這種模式存在於幾個地方。其中一些分配內存,但不釋放它。像f1(f2(f3()))這樣的用例確實存在。 2. – cobp 2010-02-26 13:06:16

    +0

    如果用「never」表示「除非局部變量是靜態的」,那麼我同意。 – 2010-02-26 13:53:08

    +0

    @William,如果局部變量是靜態的,它將起作用(至少在更長的時間內)。但這並不意味着這是一個好習慣。如果在您製作返回緩衝區的個人副本之前重新執行該功能,您仍然會遇到問題。與多線程應用程序類似(儘管您可以使用Thread-Local-Storage變量)。 – Patrick 2010-02-26 14:44:01

    2

    是以下使用安全嗎?

    如果你的函數返回一個指針到任何東西,請確保它分配一個存儲區,並返回指向它的指針:

    char *safe_f1(void) { 
        char *ptr; 
        ptr = (char *) malloc(20 * sizeof(char)); 
        ... 
        return ptr; 
    } 
    
    1

    不,你看buff[20]僅適用裏面的f1功能。準確地說,內存分配在f1的堆棧上。

    您需要使用malloc在堆上創建新的buff[20],並從f1內部返回指向該內存的指針。另一種方法是在f1外部創建buff[20](來自調用f1的函數)並將其作爲參數發送到f1。然後f1可以更改緩衝區的內容,甚至不必返回它。

    3

    malloc解決方案很有趣,除了內存使用後應該是免費的。 (在函數之外)。否則會有內存泄漏。

    2

    這是不安全的。原因很簡單:

    函數的任何變量都會分配到函數返回後釋放內存的堆棧上。內存被釋放的事實並不意味着它的內容被改變了。

    這意味着你沒有把在變量char buff[20]存儲器內容仍處於buffptr(因爲ptr=buff)內存位置。無論何時調用另一個函數(或執行另一個塊),它的函數/塊變量也將進入堆棧,從而創建更改位置ptr指向的內存內容的可能性。

    在您編寫的strcpy示例中,您足夠幸運地發現strcpy函數變量沒有進入舊的buff陣列中的位置。這就是您得到安全的印象。

    結論是,你無法真正保證在兩次函數調用之間堆棧的已釋放內存不會改變。

    解決方法是使用malloc,因爲malloc不會在堆棧上分配內存,而是在堆上分配內存。除非您選擇這樣做(通過免費通話),否則堆內存不會被釋放。

    這種方法可以保證ptr指向的內存可以安全地被任何其他函數使用。

    這種解決方案的缺點是內在的:曾經的記憶不釋放,除非你編程方式做到這一點,如果你忘記釋放該內存和失去ptr的內容,這種記憶會在那裏,分配給你的程序,但從來沒有隻要你的程序運行,就可以實現。該內存將成爲內存泄漏:-)

    這就是爲什麼一些語言有垃圾收集一個...但這是另一個故事了:-)

    PS:我認爲它是安全的(如果你的程序是單線程的),但我不推薦,做這樣的事情:

    { 
        char safe_buffer[20]; 
        char *unsafe_ptr; 
        int i; 
        unsafe_ptr = f1(); 
        /*Copy the buffer without calling any function 
        not to change the stack content 
        */ 
        for(i=0;i<20 && *(unsafe_ptr + i) != 0;i++) 
        { 
        *(safe_buffer + i) = *(unsafe_ptr + i); 
        } 
        *(safe_buffer + i) = 0; 
        f2(safe_buffer); 
    } 
    
    0

    我建議兩個可能的解決方案:

    1. 使用靜態char buff[20]f1中,除非該函數是從多個線程調用的,否則外部世界將指針存儲在strcpy之外。

    2. 使用return strdup (ptr);free指針在f1之外。這比malloc更容易使用(雖然技術上相同)。它比1慢,但是線程安全。

    0

    我建議改變這些功能,從而利用它使用

    void f1(char *) 
    

    這樣,每一段代碼調用函數必須做一個關於在內存被寫入到決定一個指針,刪除分配的任何內存。