在学习汇编的过程中,小有所悟,遂把自己所思所想记下,以便日后查阅。
首先说说我对这个关键字的理解。static字面上就是静止的、静态的、不变的之类的意思,所以在被它修饰之后,应该也会带有这样的一些特点。事实证明也确实是这样,利用这样的特点能很方便的达到一些编程实践中所要达到的一些效果。而它的存在在本质上又有什么样的原理在里面呢?说到这,我就不得不说,学底层的东西有一点好,就是能了解原理。对于我这样的一个喜欢知根知底的人来说,确实很有吸引力。
存储角度:
在最近的汇编学习中,我了解到内存中有(但不是仅有)3中不同的区域,分别是:静态存储区域、堆和栈。然后它们有着不同的职能,而静态存储区就和我们这次要聊的static有着莫大的关联。
大家都知道,程序要先加载到内存中,才能正常工作。那有没有具体想过,程序究竟是怎么被加载到内存中的?以及,程序到底是啥?就我现在的理解,程序在存储的角度看,就是变量和函数。所以,把程序加载到内存也就是把变量和函数加载到内存。那么问题就来了,这里的变量是哪些变量呢?好吧。。其实哪种变量都有可能,我最关注的就是全局变量和静态变量(被static修饰的变量)。
因为这两种变量是要被放到内存的静态存储区的(对,变量虽然都是变量,但存储的位置却大有不同)。所以说,从存储的角度看,全局变量和静态变量是存储在同一地方的,而且这两种变量(所占的空间)还是要写死在生成的目标可执行文件中的(对于编译型语言)。
我推断:
1.全局变量好理解,它要先于任何函数而存在,所以它不可能在栈中;而堆变量也要从函数中申请,所以在没有函数存在的前提下,存放在静态存储区的全局变量只能由目标可执行文件提供。
2.静态变量则是在函数执行完毕从栈中弹出后依然存在的,所以它肯定不在栈上,而它又不是动态申请来的,所以也不在堆上,那从存储角度讲,它的存在其实和函数是没有关系的。所以要加载它只能从目标可执行文件中来。
所以,从存储的视角来看,static变量与全局变量并无差别。
逻辑角度:
这里有两个概念要说明一下,就是作用域和生存期。
一个变量的作用域就是能访问这个变量的范围;而一个变量的生存期则是我能访问这个变量的时期或者说时机。
这样就有了至少四种组合:1.全局可访问+局部(函数内)生存期 2.全局可访问+全局生存期 3.局部可访问+局部生存期 4.局部可访问+全局生存期
第1种可能性简直是一个逆天的存在。。。单线程的程序同一时间不可能跑两个函数,所以这样的做法毫无意义。所以这种可能性根本不存在。
第2种可能性就是全局变量。
第3种可能性就是栈变量,也就是函数的局部变量。
而第4种可能性就是我们今天的重头戏,被static修饰的静态变量。(其实我暗想,堆变量也能在十分别扭的操作下达到这种效果,但是实在不是常规途径)
全局生存期对全局变量来说理所当然,但是对一个作用域是局部的函数内部的变量来说意味着什么呢?这意味着函数结束从栈中被弹出后,变量的值还存在。
这就是static的语义所在——不变的,这里的不变指的是变量的生存状态不变。哈哈,中二一点其实也可以叫它作“不死变量”。
那也许有人会问:这有什么用呢?当然有用,我最感兴趣的一个作用是:函数可以存住东西了。函数终于不再那么的“动态”了,开始有点“静态”的影子了。
举个例子的话,比如我要从一个字符串中往外一个一个地取字符,运用了static修饰符,我就可以优雅地每调用一次一个函数就取到一个直达取完。C语言的话我可以写成下边的样子。
1 #include <stdio.h> 2 3 char getChar(char *str){ 4 static int index = 0; 5 char result = str[index]; 6 index++; 7 8 return result; 9 } 10 11 int main(int argc, char const *argv[]){ 12 char *str = "Hello World!"; 13 14 char c; 15 while( (c=getChar(str)) != '\0' ){ 16 putchar(c); 17 } 18 19 return 0; 20 }