2010-02-26 87 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 *) 
    

    这样,每一段代码调用函数必须做一个关于在内存被写入到决定一个指针,删除分配的任何内存。