事情是这样的,最近在学习翁老师的Mooc之C语言程序设计,插一句,真的是好老师,讲的真的太好了,在c里的字符串这里,翁老师演示了一个小程序,大概代码以下,无非就是想给咱们说明c语言的scanf读入字符串是遇到空格、Tab和回车为止,而且会在结尾自动加上表示字符串结束的‘\0’,而后想给咱们演示若是出现字符数组越界会怎样?!就出现了以下的示例:小程序
#include <stdio.h> int main() { char word[8]; char word2[8]; scanf("%s",word); scanf("%s",word2); printf("%s##%s\n",word,word2); return 0; }
如上就是程序运行结果,有没有以为很神奇,反正当时我是震惊了,WoW,太难以想象了,这究竟发生了什么,老师也留下了这个疑问给咱们,留下咱们苦苦思索,可是百思不得其解,天哪,还好老师给了一个小提示,这和c语言中的数据在计算机中的存放方式有关。数组
因而我开始思考,我仿照翁老师的作法,反正c语言提供了这样的工具——指针,咱们就输出地址来看看,我也是知道的,函数在计算机中是经过堆栈的方式来实现的,数据就是经过堆栈来保存,堆栈是很重要的数据结构,其有一个很重要的特性,FILO,后进先出。我增长了输出字符数组每一个元素的地址的代码,以下:安全
#include <stdio.h> int main() { char word[8]; char word2[8]; scanf("%s",word); scanf("%s",word2); printf("%s##%s\n",word,word2); printf("%p\n",word); printf("%p\n",word2); printf("\n"); int i; for(i=0;i<8;i++){ printf("%p\n",&word[i]); } printf("\n"); for(i=0;i<8;i++){ printf("%p\n",&word2[i]); } return 0; }
看到了输出地地址后,加上草图,一切都简单明了了,总结以下:数据结构
在32位的架构下(我是在32位下编译的,其实在64位下没有出现这个状况,那是由于64位下存储字大小又不一样了,其实我还不太解释的明白,留一个问号???可是经过相同程序的运行,咱们可以发现不一样,word和word2之间的地址差为16个字节,当我将字符数组大小扩大为20时,地址差是按一个存储字增长的,那64位的存储字就应该为16字节,这也确实符合计算机组成里讲的对齐的方式存储),由于函数是经过堆栈来存储变量的,是高地址向低地址存储,咱们能够把一个字符数组想成一个存储字,在字内是顺序存储的,就是从低地址向高地址,而存储字间是则是堆栈的顺序,是一种小端存放的方式,这里,word和word2当作指针是const的,是不变的,当scanf读入第一个12345678‘\0’字符串的时候是从word所指的位置向高地址依次写入,虽然越界到了规定的位置之外,可是c语言并无提供有效的机制,即便这是不安全的,可是咱们依然能够作,能够顺利的写入,而后读入第二个12345678‘\0’字符串的时候,一样的方式,从word2所指向的位置向高地址写入,很不幸的是,在定义字符数组的时候大小就决定了,而且是连续的分配的内存空间,因此写入到8的时候,其实就用完了定义的word2的数组大小,可是字符串末尾还有结束字符'\0',虽然是不安全的,可是c语言的编译器不会发现,所以'\0'被继续写到了接下来连续的地址中,就是word所指向的位置,写入就将原来的1所覆盖,读入就ok了,接下来是打印,首先先打印word所指向的字符串,第一个已经从新写入了,恰好是字符串结束字符,所以什么也不输出,而后是打印word2所指向的字符串,打印1.。。。一直要到'\0'才会认为是字符串的结束,因此就打印出了12345678‘\0',说到这里,我也就基本明白了字符串消失之谜的真相,用柯南的话,真相永远只有一个!架构
哦,原来是这样,果真不是我不明白,而是我并不知道,计算机的世界真的很精彩,并且颇有意思,等着我,我必定要好好看看。函数