确实是两块不同的数据区域,这跟编译原理有关。"*****"这样的叫硬编码,比如常量123、#define PI 3.14后的PI也是,因为这些数据在程序中被写死了,编译器首先要为这些数据寻求存身之地,就要开辟空间给它们寄存,而又因为那是硬编码死数据,编译器自然有充分的理由认为那是不需要修改的,而只读的数据在运行处理时效率最高,因此优先把这类数据放在只读数据区。
s1[100]这样的声明,只有空间说明而无内容说明,很明显是后期需要填写、修改数据的,就无法放在只读区了,这种数据空间尺寸是常数,一般有限,为着效率起见,它们可以放在栈式空间中,除了尺寸无法改变,数据是可以擦写的,这个存储区擦写的速度很快。一般参数传递,需要反复写入和废弃数据,就利用这块存储区。这个区域本身是有限的,数据多了,剩余就少,因此不适宜放置长期赖死不走的数据。你在函数中用局部变量声明s1[100],它会在函数结束后被收回,就是这个道理。
对尺寸再大的数据,或者需要赖相当长时间的数据,就可以考虑在堆空间中存储。这个空间效率是最低的,但却胜在无节制。malloc()就是在这里申请存储空间,这个时候,编译器无法确认你什么时候才应该释放它,那么它干脆不做,留给你自己做,你不释放,它就一直占着,直到被操作系统强制收回。小程序影响不大,对内存紧张的机器(如手机之类),你这样霸占空间的后果就是大家(包括自己)都可能没有空间可用了。想象一下,在1G的磁盘中,你在磁盘中间占用了500M,理论上还有500M剩余,但事实上由于你占用的是中间区域,那么两侧剩下的都没有500M,要再申请500M空间,那是不可能,甚至连250M都拿不到。道理就是这样。
数组需要记忆入口地址,自由指针离开了入口,它无法记得入口地址在哪里,需要循原路返回,也就是说,你得另外分配一个变量记忆路径(比如p=s1, p+=i之后,就得靠i返回入口处,否则它就失根了),数组的名字,虽然可以“视为”指针,但它其实不是真正的指针,它只是一个入口地址罢了。数组名字转变为指针的时候,是它作为实参被值传送到函数中的时候,比如func(char *s)这样的函数,如果调用处是func(s1),那么到了func()中,s就成了自由指针。