C Stack-Allocated String Scope

时间:2013-08-01 02:35:20

标签: c pointers scope stack cstring

对于直接C和GCC,为什么指向字符串在这里没有被破坏?

#include <stdio.h>

int main(int argc, char *argv[])
{
    char* str_ptr = NULL; 

    {
        //local to this scope-block
        char str[4]={0};
        sprintf(str, "AGH"); 

        str_ptr = str;
    }

    printf("str_ptr: %s\n", str_ptr);

    getchar();
    return 0;
}

| ---- ----- OUTPUT |

str_ptr: AGH

| -------------------- |

这里使用在线编译器编译和执行上述代码的a link

我理解如果str是字符串文字,str将存储在bss中(基本上作为静态),但是sprintf(ing)到堆栈分配的缓冲区,我认为字符串缓冲区将纯粹基于堆栈(因此在离开范围块后地址无意义)?我知道可能需要额外的堆栈分配来覆盖给定地址的内存,但即使使用递归函数直到发生堆栈溢出,我也无法破坏str_ptr指向的字符串。 / p>

仅供参考我在VS2008 C项目中进行测试,尽管GCC似乎表现出相同的行为。

2 个答案:

答案 0 :(得分:2)

虽然鼻蜥蜴是C民间传说的一个受欢迎的部分,但行为未定义的代码实际上可以表现出任何行为,包括神奇复苏其生命周期已过期的变量。具有未定义行为的代码似乎“工作”的事实既不令人惊讶也不应该是忽视纠正它的借口。通常,除非您从事编写编译器的工作,否则在任何给定环境中检查未定义行为的确切性质并不是非常有用,尤其是在您眨眼后可能会有所不同。

在这种特殊情况下,解释很简单,但它仍然是未定义的行为,因此根本不能依赖以下解释。它可能随时被爬行动物的排放所取代。

一般来说,C编译器会将每个函数的堆栈帧设置为固定大小,而不是随着控制流进入和离开内部块而扩展和收缩。除非被调用的函数被内联,否则它们的堆栈帧不会与调用者的堆栈帧重叠。

因此,在某些具有某些编译选项集的C编译器中,除月亮的特定阶段外,字符数组str不会被调用printf覆盖,即使变量是生命已经过期。

答案 1 :(得分:1)

编译器最有可能进行某种简单的优化,导致字符串仍然在堆栈中的相同位置。换句话说,编译器允许堆栈增长以存储'str'。但它不会在main范围内缩小堆栈,因为不需要这样做。

如果你真的想看到在堆栈上保存变量地址的结果,请调用一个函数。

#include <stdio.h>

char * str_ptr = NULL;
void onstack(void)
{
    char str[4] = {0};
    sprintf(str,"AGH");
    str_ptr = str;
}

int main(int argc, char *argv[])
{  

    onstack();
    int x = 0x61626364;
    printf("str_ptr: %s\n", str_ptr);
    printf("x:%i\n",x);
    getchar();
    return 0;
}

使用gcc -O0 -std=c99 strcorrupt.c我在第一个printf上获得随机输出。它将因机器,架构和架构而异。