zoukankan      html  css  js  c++  java
  • 浅谈static其一之不死变量

    在学习汇编的过程中,小有所悟,遂把自己所思所想记下,以便日后查阅。

    首先说说我对这个关键字的理解。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)) != '' ){
    16         putchar(c);
    17     }
    18 
    19     return 0;
    20 }

    这样的一个好处是我可以不用破坏原字符串而且是很方便地用一个独立的函数,实现了所想即所得。

    全局生存期是它们的共同点,而不同则在于作用域的不同,这也是static的作用所在。全局变量,在所有函数中均可访问,那这样就会产生安全性问题,导致在用这个变量的时候不能确保它的值只被本函数所控制。这就是所谓的封装性差。而static修饰符恰能解决这一点,由于被它修饰的变量是函数的局部变量,导致它的作用域仅在这个函数内,这样我们便可安心大胆的断定,这个变量的值只由本函数控制。

    而在像Java这样的语言中,却不允许用static修饰局部作用域的变量。我想可能是因为已经能有其它的方式来表达类似的语义了,虽然不完全一样。而且到了面向对象编程的时候,我们完全可以换另一种思维来考虑问题,所以也不是那么的需要这样的函数设计方式了。在Java中,static的含义与C语言中大不相同。我们目前只说了“不死变量”这一种语义,就这一点Java是没有的,可它却有另外的含义。

    未完待续...

  • 相关阅读:
    登录校验
    com.sd.utils/Druidutils.java-连接池
    软件开发流程
    JSP九大内置对象
    . net程序员应该会什么(转载自知乎博主:专注.net领域极客)
    WPF 在属性栏中更改相关设置
    office 2016 专业版 删除部分组件
    AutoMapper安装及基本用法
    sql 求一张表中 列值重复中的 不同列值中的更新时间最大值对应的 全列数据,即相同的列值取时间最晚的哪一行
    C# 多线程 lock
  • 原文地址:https://www.cnblogs.com/joenahm/p/8970208.html
Copyright © 2011-2022 走看看