C ++应用程序中的静态数据内存限制

时间:2017-11-14 11:08:12

标签: c++ c

有人在我们的C ++应用程序中编写了函数,并且已经在生产中,我不知道为什么它还没有崩溃应用程序。以下是代码。

char *str_Modify()
{
    char buffer[70000] = { 0 };
    char targetString[70000] = { 0 };

    memset(targetString, '\0', sizeof(targetString));

    ...
    ...
    ...
    return targetString;
}

如您所见,该函数返回一个局部变量的地址,一旦该函数返回,就会释放分配的内存。 我的问题

  • 想知道什么是静态数据内存限制?
  • 这段代码可以快速修复什么?将变量targetString设为静态?
  • 是一个好习惯

4 个答案:

答案 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设为静态是一个好习惯吗?

没有。有时它是你能做的最好的,但它有很多问题:

  1. 缓冲区总是大小相同,并且总是使用~68Kb的内存和/或地址空间。你不能在某些情境中使用较大的一个,而在其他情境中使用较小的一个。如果你真的需要memset整个事情,那么在缓冲区可能小得多的情况下会导致速度下降。
  2. 使用static(或全局)变量可以打破重新入侵。简单的例子:像

    这样的代码
    printf("%s,%s\n", str_Modify(1), str_Modify(2));
    

    无法正常工作,因为第二次调用会覆盖第一次调用(比较strtok,它不能用于交错两个不同字符串的标记化,因为它具有持久状态)。

  3. 由于它不是可重入的,因此如果您使用多个线程,它也不是线程安全的。这是一团糟。