这次或许以后的东西不像写教科书那样,写板书来介绍所有的东西,我觉得那样不如直接给人推荐一本大师的c语言书就好了,我想吧自己学习过程中的思考过程记录下来,不仅仅是阐述结果,而是结果是怎么来的,当然这么写,很可能随着思路不知道扯到哪里,显的逻辑性不强。但是我还是想记录下自己的学习方法或者思维方法。 另外我觉得关于内存这一块牵扯的比较多,可能一次讲不完,我会继续补充的。
扯了这么多,其实我想知道的是编程使用变量,它在内存中如何存储?
为了弄明白这一点,我们先来看看整个内存大致是如何分布的,事先声明,以下举例或者说明皆在linux 32位操作系统,4G内存的环境下,如果你用自己环境尝试和我说的结果不同欢迎讨论。如何知道内存是如何分布的?我选择的方法是百度,所采用的资料在文章结尾都有链接,不保证在将来是否失效,如果觉的相关部分我说的不够清楚,请自行参阅,欢迎学有所成后提供建议。
先粗略的看,内存分为用户模式空间和内核空间,如下图:
再详细一点划分如下:
一步跳到这里确实有点突兀,一般的介绍应该都是只讲堆,栈,数据段,代码段
稍微解释一下,栈中所存放的数据是局部变量,堆(heap)中存放的是动态分配的内存,(malloc/free,new/delete.....)所分配的内存
bss("block started by symbol"的缩写,意味符号开始的块)存放的是未初始化的全局变量和静态变量,该段变量只有大小,名称,却没有值,是编译器处理
未初始化数据的地方,一般在程序一开始就会被清零,由于bss段只保存没有值的变量,所以事实上它不需要保存这些变量的映像,运行时所需要的bss段大小记录
在目标文件中,但bss段(不像其它段)并不占据目标文件的任何空间.
data存放的是已经初始化的全局变量和静态变量
text段存放的是二进制代码。
和预想中整整齐齐一块接一块的区域不同,各个内存区域之间有着随机偏移值,为什么要弄这个东西?不是增加了内存碎片?
为了弄明白这个东西,我们来试试简单的缓冲区溢出,拿栈做例子,代码如下:
#include<stdio.h> #include<string.h> int main() { char str[4]; int access; while(1 ){ access=0; scanf("%s",str); if(strcmp(str,"1234")==0) access=1; if(access!=0) printf("Welcome to new world!\n"); else printf("wrong key,please input again\n"); } }测试结果如下:
为什么会是这样?
因为只分配了4个字节给str, str和access在内存中布局如下:
也许你会疑问,为什么access在内存中的地址会在str上面,根据上面整个内存的图,栈中分配方向是从高到低,而在main函数栈帧中,根据代码,str应该先分配
才是,我开始也有这个疑问,还准备去*去提问,不过在segmentfault中找到了答案,中文的答案看上去要亲切一些,解释如下:
局部变量的空间不是一个一个压入栈中的,而是一次性分配好的,所以理解为变量依次入栈是错误的。C语言也没有规定局部变量在内存中的位置,只是常常实现为先定义的变量在高地址、后定义的变量在低地址。不过局部变量在栈上的位置没有绝对的关系,甚至不一定会出现在栈上
这样,因为字符串函数只是根据\0,来判断结束,很容易造成溢出的情况,导致程序bug
发现写了这么多,好像和主题关系不是那么明确,决定下篇博文继续这个主题,好好写,而当务之急是把这个收尾,缓冲区溢出是问题很严重的,而各个段起始地址加上随机
偏移之后会让缓冲区溢出变的更加困难,我在后续的文章中会专门讨论这个,这里就简单pass,接下来真正见识变量在内存中的布局吧
参考链接(资料):
《c专家编程》