有人在我们的C ++应用程序中编写了函数,并且已经在生产中,我不知道为什么它还没有崩溃应用程序。以下是代码。
char *str_Modify()
{
char buffer[70000] = { 0 };
char targetString[70000] = { 0 };
memset(targetString, '\0', sizeof(targetString));
...
...
...
return targetString;
}
如您所见,该函数返回一个局部变量的地址,一旦该函数返回,就会释放分配的内存。 我的问题
targetString
设为静态?答案 0 :(得分:6)
(请注意,您对memset
的调用无效,所有元素在调用之前均为零初始化。)
由于代码的未定义行为的一种表现形式(返回指向具有自动存储持续时间的现在超出范围的变量的指针)并未使应用程序崩溃,因此不会崩溃应用程序。
是的,使它static
确实会验证指针,但可以创建以并发访问为中心的其他问题。
选择你的语言:在C ++中还有其他技巧。
答案 1 :(得分:1)
在C和C ++中定义良好的行为因为在函数调用结束后返回一个静态变量的地址存在于内存中。
例如:
#include <stdio.h>
int *f()
{
static int a[10]={0};
return a;
}
int main()
{
f();
return 0;
}
它在GCC编译器上工作正常。 [Live Demo]
但是,如果删除static关键字,则编译器会生成警告:
prog.c: In function 'f':
prog.c:6:12: warning: function returns address of local variable [-Wreturn-local-addr]
return a;
^
另外,请参阅Ludin撰写的question评论。
我相信你对
int* fun (void) { static int i = 10; return &i; }
与int* fun (void) { int i = 10; return &i; },
的混淆是另一回事。前者定义明确,后者定义明确 未定义的行为。
另外,tutorialspoint说:
要记住的第二点是 C不提倡返回 函数外部的局部变量的地址,所以你会这样做 必须将局部变量定义为静态变量。
答案 2 :(得分:1)
正如其他答案所说,返回targetString
确实是UB。但是还有另一个补充原因,它可能会在某些平台(特别是嵌入式平台)上崩溃:堆栈大小。自动变量通常存在的堆栈段通常限制在几千字节; 64K可能很常见。两个70K阵列可能不安全使用。
使targetString
静态修复这两个问题并且是一个非合作的改进IMO;但如果从多个线程中重复使用代码,则可能仍然存在问题。在某些情况下,它也可以被认为是对记忆的无效使用。
另一种方法可能是动态分配返回缓冲区,返回指针,并在不再需要时让调用代码释放它。
至于为什么它不崩溃:如果堆栈段足够大并且没有其他功能使用足够的覆盖buffer[]
并且先被推送;然后targetString[]
可以毫发无伤地生存,悬挂在使用过的堆栈下方,实际上在它自己的世界中。虽然非常不安全!
答案 3 :(得分:1)
想知道什么是静态数据内存限制?
平台特定的。您尚未指定平台(操作系统,编译器,版本),因此没有人可以告诉您。这可能很好。
这段代码可以快速解决什么问题?
快速修复确实是为了使缓冲区静止。
好修复程序是将函数重写为
char *modify(char *out, size_t outsz) {
// ...
return out;
}
(返回输入只是为了简化在现有代码中重用新功能)。
将变量targetString设为静态是一个好习惯吗?
没有。有时它是你能做的最好的,但它有很多问题:
memset
整个事情,那么在缓冲区可能小得多的情况下会导致速度下降。使用static
(或全局)变量可以打破重新入侵。简单的例子:像
printf("%s,%s\n", str_Modify(1), str_Modify(2));
无法正常工作,因为第二次调用会覆盖第一次调用(比较strtok
,它不能用于交错两个不同字符串的标记化,因为它具有持久状态)。