文章目录
- 一、链接属性
- 二、static变量
- 1、定义静态局部变量
- 2、在函数内部使用静态变量
- 3、函数中静态局部变量与递归
- 三、static变量与全局变量的区别
- 1、存储期与生命周期
- 2、可见性与作用域
- 3、使用场景
- 4、静态与动态内存分配
- 注意事项
当用于不同的上下文环境时,
static
关键字具有不同的意思。
当用于函数定义时,或用于代码块之外的变量声明时,static
关键字用于修改标识符的链接属性(从 external
变为 internal
),但标识符的存储类型和作用域不受影响。用这种方式声明的函数或变量只能在声明它们的源文件中访问。
当用于代码块内部的局部变量的声明的时候,static
用于修改变量的存储类型,从自动变量修改为静态变量,但变量的链接属性和作用域不受影响。用这种方式声明的变量在程序执行之前创建,并在程序的整个执行期间一直存在,而不是每次在代码块开始执行时创建,在代码块执行完毕之后销毁。
一、链接属性
链接属性一共有3种–external
(外部)、internal
(内部)和 none
(无)。
-
没有链接属性的标识符(
none
)总是被当作单独的个体,也就是说该标识符的多个声明被当作不同的独立实体。 -
属于
internal
链接属性的标识符在同一个源文件内的所有声明中都指同一个实体,但位于不同源文件的多个声明则分属不同的实体。 -
属于
external
链接属性的标识符不论声明多少次,位于几个源文件都表示同一个实体。
关键字 extern
和 static
用于在声明中修改标识符的链接属性。如果某个声明在正常情况下具有external
链接属性,在它前面加上static
关键字可以使它的属性变为internal
。 static int a;
那么变量a就将为这个源文件私有。其他源文件中,如果也链接到一个叫做变量 a 的变量,那么它所引用的是另一个不同的变量。类似的也可以把函数声明为 static
:
static int func();
这样可以防止被其他源文件调用。
extern
关键字的规则更为复杂。一般而言,它为一个标识符指定external
链接属性,这样就可以访问在其他任何位置定义的这个实体。
具有 external
属性的变量我们一般称之为全局变量,所有源文件中的所有函数均可以访问它。只要变量并非声明于代码块或函数定义内部,它在缺省的情况下的链接属性即为external
。如果一个变量声明于代码块内部,在它前面添加extern
关键字讲使它所引用的是全局变量而非局部变量。
具有 external
链接属性的实体总是具有静态存储类型。全局变量在程序开始执行前创建,并在程序整个执行过程中始终存在。从属于函数的局部变量在函数开始执行时创建,在函数执行完毕后销毁,但用于执行函数的机器指令在程序的生命期内一直存在。
局部变量由函数内部使用,不能被其他函数通过名字引用。它在缺省情况下的存储类型为自动。这是基于两个原因:其一,当这些变量需要时才为它们分配存储,这样可以减少内存的总需求量;其二,在堆栈上为它们分配存储可以有效地实现递归。如果你觉得让变量的值在函数的多次调用中始终保持原先的值非常重要的话,那么可以修改它的存储类型,把它从自动变量改为静态变量。
二、static变量
在C语言中,当在函数内部定义static
变量时,该变量具有静态存储期,这意味着该变量的生命周期将贯穿整个程序的执行过程,而不仅仅是局限于函数调用的局部范围。此外,静态局部变量只会被初始化一次,即在程序开始执行时,后续函数调用中不会再次初始化。由于这种特性,静态局部变量常用于在函数调用之间保持状态,如计数或累积数据。
1、定义静态局部变量
在函数内部,使用static
关键字定义变量。
int main() {
static int myStaticVar = 0; // 静态局部变量,只初始化一次
// ... 函数的其他代码 ...
}
2、在函数内部使用静态变量
像使用普通局部变量一样使用静态局部变量,通过变量名进行访问和修改。可以在多次函数调用中保持状态,由于静态局部变量在函数调用之间保持其值,因此它们可用于跨函数调用维护状态。
void myFunction() {
static int count = 0;
count++; // 每次调用函数时,count增加1
printf("Function has been called %d times.\n", count);
}
在上面的例子中,myFunction
函数有一个静态局部变量count
,它只会在程序开始时初始化一次。每次调用这个函数时,count
都会递增,并且由于它是静态的,所以它的值会在函数调用之间保持。
需要注意的是:
- 静态局部变量不会自动初始化为它们的默认值(比如
int
类型的0
),除非显式地进行初始化。如果未初始化,它们将包含未定义的值。- 静态局部变量不会占用函数每次调用时的栈空间,因为它们的存储期是整个程序的执行期间,通常存放在程序的数据段中。
- 在多线程环境中,静态局部变量可能不是线程安全的,因为所有线程共享同一份静态变量的存储。如果需要跨线程保持状态,应使用线程安全的机制,如互斥锁(mutex)或原子操作。
所有不加static
的全局变量和函数具有全局可见性,可以在其他文件中使用;加了static
之后只能在该文件所在的编译模块中使用。
一个函数被static修饰,使得这个函数只能在本源文件内使用,不能在其他源文件内使用。
在C语言中,static
关键字在函数调用中的主要用法是用于声明函数内部的局部变量,使其具有静态存储期。这意味着这些局部变量不会在函数调用结束时被销毁,而是会保留其值,直到程序结束。这在需要跨函数调用保留状态时特别有用。(注意:main
函数所在文件需声明 add
函数。)
3、函数中静态局部变量与递归
在递归函数中,static
局部变量也很有用,因为它们可以在递归调用之间保持其值。例如,下面是一个计算阶乘的递归函数:
unsigned long long factorial(int n) {
static unsigned long long result = 1; // 静态局部变量用于累积结果
if (n > 0) {
result *= n;
return factorial(n - 1); // 递归调用
} else {
return result; // 返回累积的结果
}
}
在这个例子中,result
是一个static
局部变量,用于在递归调用之间累积阶乘的结果。如果没有static
关键字,每次递归调用都会创建一个新的局部变量result
,导致结果不正确。
三、static变量与全局变量的区别
1、存储期与生命周期
全局变量:全局变量在程序开始执行时分配内存,并在整个程序的执行期间都保持其存在。无论函数是否被调用,全局变量都占据内存空间,并在程序结束时释放其内存。
static
变量:无论是全局还是局部,static
变量都具有静态存储期。这意味着它们在程序开始执行时分配内存,并且在程序的整个执行期间都存在。对于static
局部变量来说,这是一个关键区别,因为普通的局部变量只在函数被调用时存在,并在函数返回时释放其内存。
2、可见性与作用域
全局变量:全局变量的作用域是整个程序,这意味着它们可以在任何函数内部被访问和修改(除非被同名的局部变量覆盖)。同时,由于它们具有外部链接属性,它们可以在其他文件中通过extern
声明被引用。
static
全局变量:尽管它们在存储期上与全局变量相同,但static
关键字改变了它们的可见性。static
全局变量仅在其定义的文件中可见,即使其他文件包含了相应的头文件,也无法直接访问它们。这是因为static
全局变量具有内部链接属性,使得它们的链接仅限于定义它们的文件。
static
局部变量:这些变量在函数内部定义,其作用域仅限于该函数。但是,由于它们的静态存储期,它们的值在函数调用之间保持不变。这意味着每次函数被调用时,它都会看到上次调用时留下的static
局部变量的值。
3、使用场景
全局变量:全局变量通常用于需要在多个函数或文件之间共享的数据。然而,过度使用全局变量可能导致代码难以理解和维护,因为它们可以被程序的任何部分修改。
static
变量:static
全局变量通常用于限制变量的可见性,以避免命名冲突和提高代码模块的独立性。static
局部变量则常用于需要在函数调用之间保持状态的场景,如计数器或累加器。
总结来说,static
变量和全局变量在存储期、可见性、作用域、初始化以及使用场景方面存在显著的区别。选择使用哪种类型的变量取决于具体的应用需求和编程风格。在编写代码时,应该谨慎考虑变量的存储期和可见性,以确保程序的正确性和可维护性。
4、静态与动态内存分配
static
并不直接涉及动态内存分配(如malloc
和free
),但理解静态和动态内存分配的不同是很重要的。静态变量在编译时分配内存,而动态内存分配在运行时进行。
注意事项
static
局部变量只在定义它们的函数内部可见,它们不是全局变量。static
局部变量只会被初始化一次,即使函数被多次调用。static
变量在程序的整个执行期间都存在,这意味着它们会占用内存,直到程序结束。因此,过度使用static
局部变量可能会导致内存浪费。
总结来说,static
在函数调用中的主要用法是定义具有静态存储期的局部变量,以便在函数调用之间保留其值。这在需要跨函数调用保留状态或累积数据的场景中特别有用。