static用法总结

从C到C++。

***************************************************
C语言中用法:

全局静态变量:
作用:改变变量的作用域
内存:不变,全局变量本来就是存放在静态存储区(初始化数据段或非初始化数据段)
初始化:未经初始化的全局静态变量将被自动初始化为0(内核 or 编译器来做?)
作用域:限定作用域为文件内从定义之处开始到文件结尾(非静态全局变量在整个源程序内有效)。

C语言中这样做有两个好处:
1)不会被其他文件所访问,修改
2)其他文件中可以使用相同名字的变量,不用担心命名冲突
在C++中,这种方法将逐步被命名空间取代。

局部静态变量:
作用:改变存储方式(同时也改变了生存期,与该特性相对的是auto, register)
内存:静态存储区
初始化:static局部变量在所处模块在初次运行时进行初始化工作, 且只操作一次
作用域:不变

静态函数:在函数的返回类型前加上关键字static
作用:改变作用域,与之相对的是extern(缺省情况)
内存:代码段
作用域:限定作用域为文件内

好处:
同静态全局变量一样,即可以禁止其他编译单元的调用,又能屏蔽可能存在的外部的同名函数,不要担心命名冲突
(跟普通的函数比仅仅是限制了作用域而已,这个有待阅读C语言标准,想要调用还是可以的,通过函数指针;也有网友说静态函数所做的只是在汇编的时候把.globe符号给去掉了使得静态函数只在当前的文件中生效,有待继续看书)

补充:(基于个人的理解)
1):这几个关键词
auto,register,static这几个关键词在C语言中可以用来声明存储类型
同时呢,extern,static还可以用来声明编译与链接方式,从而改变作用域(要知道C/C++是以.c/.cpp/.cc文件为编译单元的,static修饰的函数与变量是不会出现在源文件编译后的导出符号表的,“extern C”{}用来声明函数名称在导出符号表中的修饰方式);

2)局部静态变量的初始化
从方便理解的角度,可以将初始化语句看成声明与赋值两个步骤,声明涉及存储的分配,而赋值步骤只进行一次;
有些会出现重复调用的则属于C语言的未定义行为:C++03 6.7:
The zero-initialization (8.5) of all local objects with static storage duration (3.7.1) is performed before any other initialization takes place. A local object of POD type (3.9) with static storage duration initialized with constant-expressions is initialized before its block is first entered. An implementation is permitted to perform early initialization of other local objects with static storage duration under the same conditions that an implementation is permitted to statically initialize an object with static storage duration in namespace scope(3.6.2). Otherwise such an object is initialized the first time control passes through its declaration; such an object is considered initialized upon the completion of its initialization. If the initialization exits by throwing an exception, the initialization is not complete, so it will be tried again the next time control enters the declaration. If control re-enters the declaration (recursively) while the object is being initialized, the behavior is undefined. [Example:
int foo(int i)
{
static int s = foo(2*i); // recursive call – undefined
return i+1;
}
上面例子在VS2010下调用foo(1)的结果是:s值为3,返回值为2。第一次调用该函数,为s分配内存单元,为进行赋值需再调用该函数,压栈保护现场,第二次进入时s已存在,则跳过初始化语句,返回3,退栈,将3赋值给s,返回2。

3)还是关于初始化
auto变量的初始化值是任意的,除非他被显示初始化;而C++中的类对象例外,在没用调用指定构造函数时,就调用默认构造函数,不管是不是静态类型的对象。

4)对于静态变量的使用
要考虑不可重入性,及在多线程编程时的不安全性。

5)“C++ primer Plus”上
a.编译器将分配固定的内存块来存储所有的静态变量,这些变量在整个程序执行期间一直存在。另外,如果没有显示地初始化静态变量,编译器将把它设置为0。
(保留疑问,编译器设置数据段的内存单元为指定值,在程序运行时,操作系统是不是直接拷贝对应数据到内存单元?)
b.静态持续变量有三种链接特性:
外部链接性:int global = 100;  //代码块之外
                          int main()
内部链接性:static int one_file = 50; //代码块外,并用static声明
                          int main()
无链接性:    void foo(){
                          static int count = 0;   //代码块内,并用static声明
上面三种变量在程序的整个执行期间都存在;最后一种,只能在foo函数内使用。

***************************************************
C++中新增的用法:
C++重用了这个关键字,并赋予它与前面不同的第三种含义:表示属于一个类而不是属于此类的任何特定对象的变量和函数。

静态成员变量:两种情况(存放在不同的区域,见const总结)
1)常量:
class Stock
{
private:
                 static const int Len = 30;
}

2)非常量:  “C++ primer Plus” P380
class String
{
private:
                 static int num_strings; 
}

不能在类声明中初始化该静态成员变量!!否则会报错 “只有静态常量整型数据成员才可以在类中初始化”,
“C++ primer Plus”中解释,这是因为类声明描述了如何分配内存,但并不分配内存。类的静态成员是单独存储的,而不是对象的组成部分。
个人理解,首先区分声明和定义,定义涉及内存分配,静态类型变量只能被定义一次,然后被所有对象共享,而不是在每个对象中都定义一次。

初始化可如下进行(在实现文件中):
int String::num_strings = 0;    //注意使用作用域操作符

而且一旦在程序中使用到该变量,就必须先进行初始化,否则会出现链接错误,如main中有:
cout<<String::num_strings;
个人理解,其实是必须定义(分配内存单元),并在定义时赋予值,因为类只是声明对象形式,不分配内存(除了上面的常量的情况)。

静态成员函数:(少用到)
静态成员函数的地址可用普通函数指针储存,而普通成员函数地址需要用类成员函数指针来储存;
静态成员是可以独立访问的,也就是说,无须创建任何对象实例就可以访问
静态成员函数不可以调用类的非静态成员(不管是成员变量还是成员函数),因为静态成员函数不含this指针(this指针只传给非静态成员函数,这一点很重要)。

***************************************************
一些笔试题目:
1.关于static变量,请选择下面所有说法正确的内容:
A、若全局变量仅在单个C文件中访问,则可以将这个变量修改为静态全局变量,以降低模块间的耦合度;
B、若全局变量仅由单个函数访问,则可以将这个变量改为该函数的静态局部变量,以降低模块间的耦合度;
C、设计和使用访问动态全局变量、静态全局变量、静态局部变量的函数时,需要考虑重入问题;(函数是可重入的是指只要输入数据相同就应产生相同的输出)
D、静态全局变量过大,可那会导致堆栈溢出。

2.下列关于一个类的静态成员的描述中,不正确的是(D)(TX笔试题)
A、该类的对象共享其静态成员变量的值                              B、静态成员变量可被该类的所有方法访问                 
C、该类的静态方法只能访问该类的静态成员变量                D、该类的静态数据成员变量的值不可修改

static关键字是C, C++中都存在的关键字, 它主要有三种使用方式, 其中前两种只指在C语言中使用, 第三种在C++中使用(C,C++中具体细微操作不尽相同, 本文以C++为准). (1)局部静态变量 (2)外部静态变量/函数 (3)静态数据成员/成员函数 下面就这三种使用方式及注意事项分别说明 一、局部静态变量 在C/C++中, 局部变量按照存储形式可分为三种auto, static, register ( 谭浩强, 第174-175页) 与auto类型(普通)局部变量相比, static局部变量有三点不同 1. 存储空间分配不同 auto类型分配在栈上, 属于动态存储类别, 占动态存储区空间, 函数调用结束后自动释放, 而static分配在静态存储区, 在程序整个运行期间都不释放. 两者之间的作用域相同, 但生存期不同. 2. static局部变量在所处模块在初次运行时进行初始化工作, 且只操作一次 3. 对于局部静态变量, 如果不赋初值, 编译期会自动赋初值0或空字符, 而auto类型的初值是不确定的. (对于C++中的class对象例外, class的对象实例如果不初始化, 则会自动调用默认构造函数, 不管是否是static类型) 特点: static局部变量的”记忆性”与生存期的”全局性” 所谓”记忆性”是指在两次函数调用时, 在第二次调用进入时, 能保持第一次调用退出时的值. 示例程序一 #include using namespace std; void staticLocalVar() { static int a = 0; // 运行期时初始化一次, 下次再调用时, 不进行初始化工作 cout < < "a= " < (影印版)第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++重用了这个关键字,并赋予它与前面不同的第三种含义:表示属于一个类而不是属于此类的任何特定对象的变量和函数. 这是与普通成员函数的最大区别, 也是其应用所在, 比如在对某一个类的对象进行计数时, 计数生成多少个类的实例, 就可以用到静态数据成员. 在这里面, static既不是限定作用域的, 也不是扩展生存期的作用, 而是指示变量/函数在此类中的唯一性. 这也是”属于一个类而不是属于此类的任何特定对象的变量和函数”的含义. 因为它是对整个类来说是唯一的, 因此不可能属于某一个实例对象的. (针对静态数据成员而言, 成员函数不管是否是static, 在内存中只有一个副本, 普通成员函数调用时, 需要传入this指针, static成员函数调用时, 没有this指针. ) 请看示例程序四( (影印版)第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成员函数做线程函数
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值