对于直接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似乎表现出相同的行为。
答案 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上获得随机输出。它将因机器,架构和架构而异。