目录
前言
C语言编程中用到的关键词有很多,诸如const、static、typedef、define等,这里我将就static的主要用法给出用法和案例,以实现对static静态修饰的更深一步理解。
一、static用法
static用于修饰变量和函数
- static修饰局部变量
- static修饰全局变量
- static修饰函数
二、static修饰变量
1. 修饰成员变量
首先我们来看一段代码:
void add_i()
{
int i = 10;
printf("%d\t", i);
i++;
}
void test2()
{
int i = 0;
while(i<5)
{
add_i();
i++;
}
printf("print over!\n");
}
猜猜上面运行结果,是只输出一位10就结束吗,还是其他答案?
看看运行结果:
为什么输出了五次10?结果表明执行了5次循环,实际上对于test2()函数而言add_i()中的i是局部变量,当循环语句中每次调用add_i()函数时,会重新生成一个名为i的变量其值为10,而后printf()紧接的局部变量i累加已经没有意义,对下次循环再调用add_i()函数时不会有任何影响,新的调用新的开始,重新生成值为10名为i的局部变量。
OK 弄明白后再来一个小小改动:
void add_i()
{
static int i = 10;
printf("%d\t", i);
i++;
}
可以看出仅仅给上面add_i()函数加了static关键字,输出结果会相应产生什么变化呢?会保持5个10的输出吗?来看运行结果:
这里怎么又实现累加了?
原因是static使得局部变量在add_i()函数结束时的值得以保留,如果再次执行该函数时,被static修饰的变量不会被重新创建,将会以上次该函数运行结束时保留值赋给被修饰变量,客观上可以看作延长了局部变量的生命周期,使得局部变量被static修饰时,将在程序编译阶段被存入静态区,在程序完全结束时生命周期才结束。部分情况下全局变量可以用static修饰的局部变量替换,从而减少编程错用误用的可能。
2.修饰全局变量
奇奇怪怪全局变量已经存储在静态区,生命周期直到程序销毁,为何还要有static + 全局变量的用法呢? 首先给出一段代码:
// 源.c 源文件1
extern int num; // 定义全局变量并初始化
void test3()
{
printf("%d\n", num);
}
// 源1.c 源文件2
int num = 100;
声明:上面两段代码来自同一工程下的两个.c源文件
我们已经知道全局变量具有外部链接属性(多个文件之间的全局变量可以相互使用)
要使用该属性需要对需要用但是不含该全局变量的源文件作处理:对要用的全局变量加extern修饰的声明,从而让编译器知道这里用到的变量位于工程内的其他文件。
编译没有错误,运行结果:
这时候如果全局变量的外部链接属性给我们带来了麻烦,有没有什么办法可以使其失去外部链接性呢?
看如下代码:
static int num = 100;
如果对外部文件的全局变量使用static修饰符会有什么新变化,试着运行:
表明需要使用其他文件中的num变量此时是不被允许的,事实上static修饰全局变量会使其失去外部连接属性,避免了其他文件使用该文件内的全局变量,客观减小了全局变量的作用域。同时,其它文件中可以定义相同名字的变量,不会发生冲突。
三、static修饰函数
和上面一样给出两段代码:
// 源1.c
int add(const int a, const int b)
{
return a + b;
}
// 源.c
extern int add(int a, int b);
void test4()
{
const int a = 10;
const int b = 25;
const int c = add(a, b);
printf("c = %d\n", c);
}
可以看出全局函数有着和全局变量类似的性质,具有外部链接属性,那么定义处加上static修饰全局函数还能通过编译吗?
修改部分代码如下:
// 源1.c
static int add(const int a, const int b)
{
return a + b;
}
试着运行:
发现static修饰函数时同样使得函数失去了外部链接属性,避免了其他.c文件调用该函数。
四、static在类中的使用
来看下面代码:
class Loans // 贷款类
{
public:
Loans(const double amount) :m_amount(amount) {}
static void setInterest(double interest);
static double getInterest() { return m_interest; }
private:
double m_amount; // 贷款金额
static double m_interest; // 利息
};
double Loans::m_interest = 0.05;
void Loans::setInterest(const double interest)
{
m_interest = interest;
}
注意:
- 应用static定义的静态成员变量(这里以m_interest为例)需要在类内定义类外初始化,因为静态成员变量也是存放在静态区,在编译阶段就要进行初始化定义,而类只有在运行时才会生成,所以静态成员变量在类外初始化类内定义使得其可以在运行阶段前已经被初始化。
- 静态成员函数只能改变静态成员变量的值,由于静态成员变量不与任何生成的类对象相绑定,所以static函数内部不存在this指针用于访问类中的其他成员变量和函数,因为静态数据成员在全局数据区分配内存,属于本类的所有对象共享,所以,它不属于特定的类对象,在没有产生类对象时其作用域就可见,即在没有产生类的实例时,我们就可以操作类静态成员函数。
接着来看对上面定义后类的应用实例:
void test5()
{
Loans* l1 = new Loans(1000);
Loans* l2 = new Loans(80000);
cout << "调整l1利息前:\t" << "l1: " << l1->getInterest() << '\t' << "l2: " << l2->getInterest() << endl;
l1->setInterest(0.12);
cout << "调整l1利息后:\t" << "l1: " << l1->getInterest() << '\t' << "l2: " << l2->getInterest() << endl;
}
运行结果:
容易看出仅仅改变 l1 中静态成员变量的值,l2 中对应静态成员变量的值也会随之改变。
在这里将利息 m_interest 定义为静态成员变量有两个好处:① 不管定义多少个存款类对象,利息数据成员都共享分配在全局数据区的内存,所以节省存储空间; ② 一旦利息需要改变时,只要改变一次,则所有存款类对象的利息全改变过来了。
同全局变量相比,使用静态数据成员有两个优势:
1. 静态数据成员没有进入程序的全局名字空间,因此不存在与程序中其它全局名字冲突的可能性;
2. 可以实现信息隐藏。静态数据成员可以是private成员,而全局变量不能。
总结
以上内容对static关键字的用法做了浅显易懂的阐述,适用于早期浅层编程探索学习中应用,当然static仅仅利用上述基本用法的线性组合写出的程序同样可以实现惊艳的数据表现,有效解决数据共享与数据存储相关的编程需求。