您的位置:首页 > 编程语言 > C语言/C++

消失的字符串——c语言函数中的数据存储方式以及字符串实现

2016-04-21 21:38 417 查看
事情是这样的,最近在学习翁老师的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',说到这里,我也就基本明白了字符串消失之谜的真相,用柯南的话,真相永远只有一个!

哦,原来是这样,果然不是我不明白,而是我并不知道,计算机的世界真的很精彩,而且很有意思,等着我,我一定要好好看看。



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  函数堆栈