1

对于直接的 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;
}

|----输出-----|

str_ptr: AGH

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

这是使用在线编译器编译和执行的上述代码的链接。

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

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

4

2 回答 2

2

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

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

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

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

于 2013-08-01T03:24:13.393 回答
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 上得到随机输出。它将因机器而异,因架构而异。

于 2013-08-01T02:47:18.017 回答