static关键字在C/C++中用处非常普遍,但是,在平时的工作中,一直没有好好地总结过static关键字的具体用法,有时定位问题或写代码时,遇到需要使用static关键字的地方,只有临时上网去查。这一次,对static常用方法做一个总结。
首先,在C语音中,static可以用来修饰变量和函数。看过自我修养的人都知道,程序在编译和链接过程中,会分为许多“段”结构,比如代码段(.text)、数据段(.data)、只读数据段(.rodata)、BSS段(.bss)等,其中,.data段保存的是已经初始化了的全局静态变量和局部静态变量,.bss段保存的是未初始化的全局变量和局部静态变量。如下面的小程序:
int global_init_var = 84; //.data段
int global_uninit_var; //.bss段,默认为0
int main(void)
{
static int static_var = 85; //.data段
static int static_var2; //.bss段,默认为0
...
}
BSS段在程序执行之前都会默认置0,也就是说,未初始化的全局变量和静态变量在程序执行之前值为0,静态变量在程序执行之前就已经被初始化,且只会初始化一次。static在C语音中可以作为静态局部变量、静态全局变量,并可以修饰函数成为静态函数。下面分别看一下它们各自的特点:
1)static修饰静态局部变量:
方法:局部变量前加static关键字,即为静态局部变量,它和全局变量一样,但存储区域和作用域不一样;
存储区域:静态存储区;
初始化情况:未经显式初始化的局部静态变量将自动初始化为0。
作用域:静态局部变量的作用域是局部的,当定义它的函数或者语句块结束的时候,作用域结束。但是当局部静态变量离开作用域后,并没有销毁,而是仍然驻留在内存当中,只不过我们不能再对它进行访问,直到该函数再次被调用,并且值不变;
示例如下:
#include <stdio.h>
int func1()
{
static int a; //初始化为0
a++;
return a;
}
int main()
{
int v1 = func1();
printf("v1 = %d\n", v1); //v1 = 1
int v2 = func1();
printf("v2 = %d\n", v2); //v2 = 2
return 0;
}
2)static修饰静态全局变量:
方法:全局变量前加static关键字,即为静态全局变量;
存储区域:静态存储区;
初始化情况:未经显式初始化的局部静态变量将自动初始化为0。
作用域:全局静态变量在声明他的文件之外是不可见的,准确地说,作用范围从定义之处开始,到文件结尾。
示例如下:
//static.c
#include <stdio.h>
static int a;
int b;
int func()
{
int f1 = a++;
printf("f1 = %d\n", f1);
int f2 = b++;
printf("f2 = %d\n", f2);
return 0;
}
int main1()
{
int v1 = a++;
printf("v1 = %d\n", v1); //v1 = 0
int v11 = b++;
printf("v11 = %d\n", v11); //v11 = 0
func(); //f1 = 1 //f2 = 1
int v2 = a++;
printf("v2 = %d\n", v2); //v2 = 2
int v22 = b++;
printf("v22 = %d\n", v22); //v22 = 2
return 0;
}
//static_1.c
#include <stdio.h>
extern int b;
extern int main1();
//extern static int a; //静态全局变量作用域只有定义它的文件,所以不可以在另一个文件中引用
int func2()
{
int f3 = b++;
printf("f3 = %d\n", f3);
return 0;
}
int main()
{
main1(); //见main1中
func2(); //f3 = 3
int v3 = b++;
printf("v3 = %d\n", v3); //v3 = 4
//int v4 = a++;
//printf("v4 = %d\n", v4);
return 0;
}
3)static修饰静态函数:
在函数返回类型前加关键字static,函数就定义成静态函数。静态函数只是在声明他的文件当中可见,不能被其他文件所用,作用域类似于静态全局变量,普通函数默认都是extern的。
示例如下:
#include <stdio.h>
//static_1.c
extern int b;
extern int main1();
//extern static int func(); //不可以引用另一个文件中的静态函数
//extern static int a;
int func2()
{
int f3 = b++;
printf("f3 = %d\n", f3);
return 0;
}
int main()
{
//func();
main1();
func2();
int v3 = b++;
printf("v3 = %d\n", v3);
//int v4 = a++;
//printf("v4 = %d\n", v4);
return 0;
}
在C++中,static关键字的用法比在C语音中稍微复杂一些,主要分为以下几种:
1)作为类的静态成员变量:
作为C++类中的静态成员变量,在cpp文件中必须对其进行初始化,初始化时使用作用域运算符来标明它的所属类,其属于该类的所有成员共有,只有一个拷贝;
示例如下:
//static_class_var.h
#include <iostream>
using namespace std;
class A
{
public:
A(){}
~A(){}
int printA();
public:
static int a;
};
//static_class_var.cpp
#include "static_class_var.h"
using namespace std;
int A::a = 5; //必须显式初始化类的静态变量,否则会错
int A::printA()
{
a += 5;
cout << "a = " << a << endl;
return 0;
}
int main()
{
A *oA = new A();
oA->printA(); //a = 10
return 0;
}
2)作为类的静态成员函数
当作为类的静态成员函数时,在头文件中进行static声明即可,在具体的实现中不需要再加static关键字。在声明静态函数的类中,该静态函数属于该类的全局函数,在静态函数中,只能访问类的静态成员,不能访问普通成员。调用时,直接使用类名::静态函数名即可调用,也可以使用普通成员对象.静态函数名调用。在继承和覆盖方面,类静态函数与普通函数一样,但不能将其声明成虚函数。
示例如下:
//static_class.h
using namespace std;
class A
{
public:
A();
~A();
int printA();
static int printB(); //类的静态函数
public:
static int a; //类的静态变量
int b;
};
//static_class.cpp
#include "static_class.h"
using namespace std;
int A::a = 5;
A::A(): b(3)
{
}
A::~A()
{
}
int A::printA()
{
a += 5;
b += 3;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
return 0;
}
int A::printB() //实现不用再加static声明
{
a += 5;
//b += 3; //静态类成员函数中不能访问非静态成员
cout << "a = " << a << endl;
//cout << "b = " << b << endl; //同理
return 0;
}
int main()
{
A *oA = new A();
oA->printA();
oA->printB(); //静态函数可以使用对象调用
A::printB(); //静态函数也可以使用类调用
return 0;
}
关于类静态成员的继承示例如下:
#include <iostream>
using namespace std;
class A
{
public:
static int m_a;
};
int A::m_a = 10;
class B : public A
{
public:
B(){}
~B(){}
int printB()
{
cout << m_a << endl;
return 0;
}
};
//int B::m_a = 20; //不可以直接使用B类对A中的静态成员变量赋值!
int main()
{
B b;
b.printB();
cout << b.m_a << endl;
cout << B::m_a << endl;
cout << A::m_a << endl ;
cout << &B::m_a << endl;
cout << &A::m_a << endl;
cout << &b.m_a << endl;
return 0;
}
/*
结果:
10
10
10
10
0x10e880080
0x10e880080
0x10e880080
*/
3)作为C++文件内的静态全局变量:
当在cpp文件中声明了静态全局变量时,此时这个静态全局变量只在这个cpp文件中有效,当其他cpp文件中也有同名的静态全局变量时,与该cpp中的同名静态全局变量无关。
4)作为C++文件内的静态全局函数:
当在cpp文件中声明了静态全局函数时,此时这个静态全局函数只在这个cpp文件中有效。当其他cpp文件中也有同名的静态全局函数时,与该cpp中的同名静态全局函数无关。但是在使用时,全局静态函数和全局函数要定义在cpp文件内,不要定义在头文件中,在它的头文件内引用即可,其他cpp也要使用这个函数时,包含那个头文件即可。
示例如下:
//static_global.h
#include <iostream>
using namespace std;
static int g_c;
extern int g_b; //其他cpp包含这个头文件即可。但不能对static全局变量或函数引用
class A
{
public:
A();
~A();
virtual int printA();
static int printB();
public:
static int m_a;
};
//static_global.cpp
#include "static_global.h"
using namespace std;
static int g_a;
int g_b;
A::A()
{
}
A::~A()
{
}
int A::printA()
{
g_a += 5;
cout << "g_a: " << g_a << endl;
g_b += 3;
cout << "g_b: " << g_b << endl;
g_c += 4;
cout << "g_c: " << g_c << endl;
return 0;
}
int A::printB()
{
g_a += 5;
cout << "g_a: " << g_a << endl;
g_b += 3;
cout << "g_b: " << g_b << endl;
g_c += 4;
cout << "g_c: " << g_c << endl;
return 0;
}
int main()
{
A *oA = new A();
oA->printA();
A::printB();
return 0;
}