zoukankan      html  css  js  c++  java
  • static使用方法小结

    static使用方法小结

    statickeyword是C, C++中都存在的keyword, 它主要有三种使用方式, 当中前两种仅仅指在C语言中使用, 第三种在C++中使用(C,C++中详细细微操作不尽同样, 本文以C++为准).
    (1)局部静态变量
    (2)外部静态变量/函数
    (3)静态数据成员/成员函数
    以下就这三种使用方式及注意事项分别说明

    一、局部静态变量
    在C/C++中, 局部变量依照存储形式可分为三种auto, static, register
    (<C语言程序设计(第二版)>谭浩强, 第174-175页)
    与auto类型(普通)局部变量相比, static局部变量有三点不同
    1. 存储空间分配不同
    auto类型分配在栈上, 属于动态存储类别, 占动态存储区空间, 函数调用结束后自己主动释放, 而static分配在静态存储区, 在程序整个执行期间都不释放. 两者之间的作用域同样, 但生存期不同.
    2. static局部变量在所处模块在初次执行时进行初始化工作, 且仅仅操作一次
    3. 对于局部静态变量, 假设不赋初值, 编译期会自己主动赋初值0或空字符, 而auto类型的初值是不确定的. (对于C++中的class对象例外, class的对象实例假设不初始化, 则会自己主动调用默认构造函数, 无论是否是static类型)

    特点: static局部变量的”记忆性”与生存期的”全局性”
    所谓”记忆性”是指在两次函数调用时, 在第二次调用进入时, 能保持第一次调用退出时的值.
    演示样例程序一
    #include <iostream>

    using namespace std;

    void staticLocalVar()
    {
     static int a = 0; // 执行期时初始化一次, 下次再调用时, 不进行初始化工作
     cout<<"a="<<a<<endl;
     ++a;
    }

    int main()
    {
     staticLocalVar(); // 第一次调用, 输出a=0
     staticLocalVar(); // 第二次调用, 记忆了第一次退出时的值, 输出a=1
     return 0;
    }

    应用:
     利用”记忆性”, 记录函数调用的次数(演示样例程序一)
       利用生存期的”全局性”, 改善”return a pointer / reference to a local object”的问题. Local object的问题在于退出函数, 生存期即结束,. 利用static的作用, 延长变量的生存期.
    演示样例程序二:
    // IP address to string format
    // Used in Ethernet Frame and IP Header analysis
    const char * IpToStr(UINT32 IpAddr)
    {
     static char strBuff[16]; // static局部变量, 用于返回地址有效
     const unsigned char *pChIP = (const unsigned char *)&IpAddr;
     sprintf(strBuff, "%u.%u.%u.%u",  pChIP[0], pChIP[1], pChIP[2], pChIP[3]);
     return strBuff;
    }

    注意事项:
    1. “记忆性”, 程序运行非常重要的一点就是可反复性, 而static变量的”记忆性”破坏了这样的可反复性, 造成不同一时候刻至运行的结果可能不同.
    2. “生存期”全局性和唯一性. 普通的local变量的存储空间分配在stack上, 因此每次调用函数时, 分配的空间都可能不一样, 而static具有全局唯一性的特点, 每次调用时, 都指向同一块内存, 这就造成一个非常重要的问题 ---- 不可重入性!!!
    这样在多线程程序设计或递归程序设计中, 要特别注意这个问题.
    (不可重入性的样例能够參见<effective C++ (2nd)>(影印版)第103-105页)
    以下针对演示样例程序二, 分析在多线程情况下的不安全性.(为方便描写叙述, 标上行号)
    ① const char * IpToStr(UINT32 IpAddr)
    ② {
    ③  static char strBuff[16]; // static局部变量, 用于返回地址有效
    ④  const unsigned char *pChIP = (const unsigned char *)&IpAddr;
    ⑤  sprintf(strBuff, "%u.%u.%u.%u",  pChIP[0], pChIP[1], pChIP[2], pChIP[3]);
    ⑥  return strBuff;
    ⑦ }
    如果如今有两个线程A,B运行期间都须要调用IpToStr()函数, 将32位的IP地址转换成点分10进制的字符串形式. 现A先获得运行机会, 运行IpToStr(), 传入的參数是0x0B090A0A, 顺序运行完应该返回的指针存储区内容是:”10.10.9.11”, 现运行到⑥时, 失去运行权, 调度到B线程运行, B线程传入的參数是0xA8A8A8C0, 运行至⑦, 静态存储区的内容是192.168.168.168. 当再调度到A运行时, 从⑥继续运行, 因为strBuff的全局唯一性, 内容已经被B线程冲掉, 此时返回的将是192.168.168.168字符串, 不再是10.10.9.11字符串.

    二、外部静态变量/函数
    在C中static有了另外一种含义:用来表示不能被其他文件訪问的全局变量和函数。, 但为了限制全局变量/函数的作用域, 函数或变量前加static使得函数成为静态函数。但此处“static”的含义不是指存储方式,而是指对函数的作用域仅局限于本文件(所以又称内部函数)。注意此时, 对于外部(全局)变量, 不论是否有static限制, 它的存储区域都是在静态存储区, 生存期都是全局的. 此时的static仅仅是起作用域限制作用, 限定作用域在本模块(文件)内部.
    使用内部函数的优点是:不同的人编写不同的函数时,不用操心自定义的函数,是否会与其他文件里的函数同名。
    演示样例程序三:
     
    //file1.cpp

    static int varA;
    int varB;
    extern void funA()
    {
    ……
    }

    static void funB()
    {
    ……
    }

    //file2.cpp

    extern int varB; // 使用file1.cpp中定义的全局变量
    extern int varA; // 错误! varA是static类型, 无法在其它文件里使用
    extern vod funA(); // 使用file1.cpp中定义的函数
    extern void funB(); // 错误! 无法使用file1.cpp文件里static函数

    三、静态数据成员/成员函数(C++特有)
    C++重用了这个keyword,并赋予它与前面不同的第三种含义:表示属于一个类而不是属于此类的不论什么特定对象的变量和函数. 这是与普通成员函数的最大差别, 也是其应用所在, 比方在对某一个类的对象进行计数时, 计数生成多少个类的实例, 就能够用到静态数据成员. 在这里面, static既不是限定作用域的, 也不是扩展生存期的作用, 而是指示变量/函数在此类中的唯一性. 这也是”属于一个类而不是属于此类的不论什么特定对象的变量和函数”的含义. 由于它是对整个类来说是唯一的, 因此不可能属于某一个实例对象的. (针对静态数据成员而言, 成员函数无论是否是static, 在内存中仅仅有一个副本, 普通成员函数调用时, 须要传入this指针, static成员函数调用时, 没有this指针. )
    请看演示样例程序四(<effective c++ (2nd)>(影印版)第59页)
    class EnemyTarget {
    public:
      EnemyTarget() { ++numTargets; }
      EnemyTarget(const EnemyTarget&) { ++numTargets; }
      ~EnemyTarget() { --numTargets; }
      static size_t numberOfTargets() { return numTargets; }
      bool destroy();   // returns success of attempt to destroy EnemyTarget object
    private:
      static size_t numTargets;               // object counter
    };
    // class statics must be defined outside the class;
    // initialization is to 0 by default
    size_t EnemyTarget::numTargets;

    在这个样例中, 静态数据成员numTargets就是用来计数产生的对象个数的.
    另外, 在设计类的多线程操作时, 因为POSIX库下的线程函数pthread_create()要求是全局的, 普通成员函数无法直接做为线程函数, 能够考虑用Static成员函数做线程函数.

  • 相关阅读:
    HDU 5486 Difference of Clustering 图论
    HDU 5481 Desiderium 动态规划
    hdu 5480 Conturbatio 线段树 单点更新,区间查询最小值
    HDU 5478 Can you find it 随机化 数学
    HDU 5477 A Sweet Journey 水题
    HDU 5476 Explore Track of Point 数学平几
    HDU 5475 An easy problem 线段树
    ZOJ 3829 Known Notation 贪心
    ZOJ 3827 Information Entropy 水题
    zoj 3823 Excavator Contest 构造
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/4206143.html
Copyright © 2011-2022 走看看