上图与下面这个图中,请注意main函数中s1和s2这两个变量。一个定义为指针,一个定义为数组。他的问题是:为什么下图中用数组定义的能正常运行,但是上图中用指针定义的取运行出错!
看起来差不多的程序,但是第一个能正常运行,第二个却不能运行,为什么呢?
要正确理解这个问题,需要了解C语言中变量及常量的存储位置,这个其实在咱们程序员互动联盟里面以前的文章中应该也讲到过,一直阅读和关心的朋友应该看到过。
这里我再简单重复一下,C语言变量分为BSS段,数据段和栈区;而常量数据则会被编译器放到文本段,这个段实际上跟代码段在一起。你分析执行程序时常常会看到一个.text或者.code。这个段默认是只读的,也就是你不能去改写它。
上面两部分程序的关键在main函数中定义的
char *s1 = “china”, *s2 = “ch”;
和
char s1[] = “china”, s2[] = “ch”;
按第一种方式,s1和s2本身是一个栈中的变量,但它们指向的字符串都放在代码段中,是一个只读的内存块,所以这种情况下,要用第二个字符串去逐个替换时,操作系统会检查到目标内存是一个只读属性的存储单元,会给程序返回一个异常,于是我们就看到下面这个出错的对话框了。
对于第二中方式,在编译的时候,同样会把两个字符串放到某个只读区。但是关键点来了,s1和s2是数组,他们的内存空间也是分配在栈中的,由于这两个变量在分配时同时需要用常量初始化的,所以在变量空间在栈中分配好后,编译器会做额外的工作,它会自动把那个只读的字符串拷贝过来初始化这个栈中的变量,于是这个变量就有了我们看到的初始值,实际上这个时候在进程的内存映像中有两份。既然是栈中的空间,默认就是可读写的,所以这种情况就可以对s1进行写了。由于只是对s1进行写操作,对s2只有读的要求,所以s2用第一种还是第二种都可以。
如果要对以上做更深入的理解,你需要知道进程的虚拟内存以及物理存储映射相关的知识。对于初学者,只要知道C语言中字符常量编译在只读区,不能写即可。