返回字符指针的函数
我遇到了很多在一个遗留应用程序中返回字符指针的函数。 其中一些返回指向本地字符数组的指针。看来经过多次调用将导致崩溃(不会马上!)请参见下面返回字符指针的函数
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);
不,这是不确定的行为。它恰好适用于您的情况,但可能随时停止工作。
不,这是不安全的。只需调用strcpy就可以修改堆栈,以便稍后导致问题,因为返回地址和参数可能会覆盖数组。
f1函数返回一个临时函数(buff),该函数在函数返回时被释放。你需要在函数内部使用malloc()。
这几乎可以确保内存泄漏,并带有一些对函数的非引用。 – 2010-02-26 15:12:48
你当然可能需要一个指向预先分配的内存的指针作为输入参数,然后让函数填充它,但是我们不再有相同的函数签名。 – Zitrax 2010-02-26 21:29:04
不..还是不安全。
当你在做strcpy(arr,f1());
时,用作第二个参数的指针已经指向一个不存在的数组。
永远不会返回指向局部变量的指针。它可能在某些情况下有效,但总的来说,你会遇到很多问题。 相反:
它返回一个指向分配的内存,并且返回的缓冲区必须由调用- 文件的功能(这是什么是Win32 API中一般都做)
- 如果使用C++,使用的std :: string
我觉得这两个解决方案。 1.在f1()中使用malloc。在这种情况下,所有呼叫者在使用后都应该正确地释放内存。应避免使用像 f2(f1()),因为它不会重新执行释放内存的句柄。 2.将分配的ptr传递给该函数,以便f1()可以复制其中的文本。现有的用法如f2(f1())在这种情况下也是不可能的。 您更喜欢哪种解决方案?无论如何,它需要对现有应用程序进行很多更改。因为这种模式存在于几个地方。其中一些分配内存,但不释放它。像f1(f2(f3()))这样的用例确实存在。 2. – cobp 2010-02-26 13:06:16
如果用“never”表示“除非局部变量是静态的”,那么我同意。 – 2010-02-26 13:53:08
@William,如果局部变量是静态的,它将起作用(至少在更长的时间内)。但这并不意味着这是一个好习惯。如果在您制作返回缓冲区的个人副本之前重新执行该功能,您仍然会遇到问题。与多线程应用程序类似(尽管您可以使用Thread-Local-Storage变量)。 – Patrick 2010-02-26 14:44:01
是以下使用安全吗?
号
如果你的函数返回一个指针到任何东西,请确保它分配一个存储区,并返回指向它的指针:
char *safe_f1(void) {
char *ptr;
ptr = (char *) malloc(20 * sizeof(char));
...
return ptr;
}
不,你看buff[20]
仅适用里面的f1
功能。准确地说,内存分配在f1
的堆栈上。
您需要使用malloc
在堆上创建新的buff[20]
,并从f1
内部返回指向该内存的指针。另一种方法是在f1外部创建buff[20]
(来自调用f1
的函数)并将其作为参数发送到f1
。然后f1
可以更改缓冲区的内容,甚至不必返回它。
malloc解决方案很有趣,除了内存使用后应该是免费的。 (在函数之外)。否则会有内存泄漏。
这是不安全的。原因很简单:
函数的任何变量都会分配到函数返回后释放内存的堆栈上。内存被释放的事实并不意味着它的内容被改变了。
这意味着你没有把在变量char buff[20]
存储器内容仍处于buff
或ptr
(因为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);
}
我建议两个可能的解决方案:
使用静态
char buff[20]
在f1
中,除非该函数是从多个线程调用的,否则外部世界将指针存储在strcpy之外。使用
return strdup (ptr);
和free
指针在f1
之外。这比malloc
更容易使用(虽然技术上相同)。它比1慢,但是线程安全。
我建议改变这些功能,从而利用它使用
void f1(char *)
这样,每一段代码调用函数必须做一个关于在内存被写入到决定一个指针,删除分配的任何内存。
+1非常多。 – Tom 2010-02-26 12:46:46
它正常工作的原因是在x86上,buff数组从低位地址填充到高位地址,并且可能不会被strcpy的堆栈使用所触及。不要这样做:由于信号和中断等异步事件,堆栈使用可能会有所不同。 – 2010-02-26 12:57:25