分类: LINUX
2009-04-29 13:44:27
转发自网上一段讨论
==================================
现有一程序如下:
main()
{
char *p="Hello";
strcat(p,"World");
printf("p is %s.\n",p);
}
运行结果为segmentation fault
如果改成char p[]="Hello";就正确运行.
我不理解的是,为什么必须p在栈里就正确,在堆里就错误呢?
而且segmentation fault又如何解释呢?
=================================
真搞不懂,指针有这么难学么?心血来潮,写点心得在下面。有兴趣的请指教。
不管是
char *p="Hello";
还是
char p[]="Hello";
都是错误的
这两种情况p都只向一块6字节大小的空间,使用这6字节之后的空间就会引起错误,因为也许它们是某个变量的空间。为了防止对内存地址非法操作,就有了段保护的机制。不可能让你修改内存中任意地址的内容。
比如说这段代码:
#include
int main()
{
char p[] = "Hello";
p[5] = '!';
p[6] = '\0';
printf("%s\n", p);
}
似乎不会出错,虽然p[6]好像超出了6个字符的限制。
但是如果把p[6]改成p[3000]又如何呢?你可以试一下,100%出错。p[]定义在栈里,运行时根据需要,栈会初始化一个大小,如果试图修改栈以外的地址,就出错。
经常看到有人报错说加一行printf程序就能正常运行,不加这一行就会segmentation falt,其实跟上面的程序一样,程序隐含了致命的问题,只不过因为 printf 改变了堆栈的布局,遮盖了你的程序的漏洞。再试一下在上面程序中char p[] = "Hello"前面加上一行char c[3000],那么p[3000]也不会造成错误,因为c[3000]这个变量的定义相当于让栈扩大了。
再看一个直观的例子:
#include
int main(int argc, char* argv[])
{
char p[] = "Hello";
char i = 1;
p[5] = '!';
for(i = 6; i < 100; i++)
p[i] = 0;
printf("%s, argc=%d\n", p, argc);
}
结果是造成core dump,并且argc的值被改成0
进一步看看
printf("%d, argc=%d\n", *(int*)(p+32), argc);
的输出结果,可以看到p[32]的值和argc是一样的,它们在栈里的地址是一样的。现在你应该相信非法指针操作可能带来的危害了吧?
顺便说一句,对于初学者的问题,不要什么都指到c99,那个东西我没看过,我想看过的人也不多。其实弄一本经典的书好好学一下,再多练习练习,我想就差不多了。
mikewang
(enthusiast)
03-05-26 16:34
Re: 关于segmentation fault
我的看法和你不一样
1, 什么堆呀,栈呀 都是编译器的问题, 不是你考虑的东西
2, 用C写程序,就要安装C的规范写, 不要考虑汇编
3, 入门的教材还是要的, 不过C99是比读的, 除非你不用C写程序
x86
(member)
03-05-26 16:52
Re: 关于segmentation fault
>1, 什么堆呀,栈呀 都是编译器的问题, 不是你考虑的东西
>2, 用C写程序,就要安装C的规范写, 不要考虑汇编
当然,这两点我同意。讲一下堆栈,指针的问题就更好理解一些。
>3, 入门的教材还是要的, 不过C99是比读的, 除非你不用C写程序
也许是我懒,每回看到c99都没法读下去,我觉得并非所有细节都需了解,就好像一个HTML高手也未必完全了解HTML规范,java想必也有标准,看的人就更少了。C/C++语言有很多经典教材,其中很多并非只用于入门。
teawater
(old hand)
03-05-26 17:48
Re: 关于segmentation fault
char p[]="Hello";有什么错?
我认为其是相当于
char p[6]="Hello";
本身是没问题的
只要你不访问超出其范围的地址就不会有问题
指针只是个概念问题,严格按照规范做就不会出问题。
过渡强调编译器相关的问题只能让问题复杂化
x86
(member)
03-05-27 09:35
Re: 关于segmentation fault
呵呵,老兄又误会我的意思了。
coolq说为什么char* p和char p[]的表现不一样,char p[]不会引起core dump,而char* p会,我的意思是不管定义成哪一个,代码都有问题。并非说char p[]="Hello"有问题。
有一点可能我没说清楚,char* p和char p[]并非与堆栈有关,而是因为char* p="Hello"是const,而char p[]不是。所以char* p的错误是语法问题,而char p[]的错误是指针越界。
本来是回的coolq的帖子,写了一大段才发现你已经回的差不多了,所以另开一个帖子,只是舍不得写的一大段文字罢了。
teawater
(old hand)
03-05-27 10:43
Re: 关于segmentation fault
哦
其实你写的挺好的 为原创鼓掌
jfwan
(addict)
03-05-27 10:55
Re: 关于segmentation fault
hehe, I also think so!
x86
(member)
03-05-27 11:07
Re: 关于segmentation fault
Thanks.
f1122
(stranger)
05-01-07 15:10
Re: 关于segmentation fault
char *p="hello";
"hello"不是const
请看规范
vrbear
(stranger)
05-01-07 15:24
Re: 关于segmentation fault
char *p="hello";
把看成const属于设计概念错误。
char *p = “Hello”; // non-constant pointer,non-constant data
const char *p = “Hello”; // non-constant pointer,constant data
char * const p = “Hello”; // constant pointer, non-const data
const char * const p = “Hello”; // constant pointer,constant data
不过VC是把"hello"看成常量放在静态存储区的,这可能是出于优化的目的。
gooderfeng
(stranger)
05-05-27 12:37
Re: 关于segmentation fault
想想看
char [] 是在堆栈里面保存的
char*这个指针也是在静态区里面保存的。
而指针指的串还是在堆栈区的。
我是看不出来他们本质上有什么区别。
caty
(newbie)
05-05-27 14:01
Re: 关于segmentation fault
看一下汇编代码,
gcc 把 "Hello" 放到只读数据区,
.section .rodata
只要改成 .section .data
程序就可以跑了。
当然用strcat你把别人的空间给覆盖了,这是另外一个问题。