【C语言】关键字static的初步剖析

简介: 【C语言】关键字static的初步剖析

变量


1.局部变量


平常的局部变量想必大家都非常熟悉,具有局域临时性。即在函数调用开辟空间时并初始化,函数结束释放空间并初始化。


而局部变量存储在栈区上,使用完毕后会立即释放。(为了让大家可以理解的轻松些,下面附上C程序地址空间的图。)

58e94116d8ac465bb4a0b64e8865facf.png


静态局部变量也就是由static修饰定义的变量,存储于进程的全局数据区,也就是图中的未初始化全局,已初始化全局和静态数据区域。


其特点有三:


1)静态局部变量在程序执行到该对象的声明处时被首次初始化,即以后的函数调用不再进行初始化。


2)静态局部变量一般在声明处初始化,如果没有显式初始化,会被程序自动初始化为0。


3)它始终驻留在全局数据区,直到程序运行结束。但作用域为局部作用域,当定义它的函数或语句块结束时,其作用域随之结束。


而其作用是更改局部变量的生命周期。因为静态局部变量存储在全局数据区,当静态局部变量离开作用域后,并没有被销毁。当该函数再次被调用的时候,该变量的值为上次函数调用结束时的值。

让我们用一段代码来理解它的作用:

#include<stdio.h>
void test1()
{
  int i = 0;
  i++;
  printf("i=%d\n", i);
}
void test2()
{
  static int i = 0;
  i++;
  printf("i=%d\n", i);
}
int main()
{
  for (int i = 0; i < 5; i++)
  {
    test1();
    test2();
  }
  return 0;
}

vs2022中运行结果如下:

test1
i=1
i=1
i=1
i=1
i=1
------------
test2
i=1
i=2
i=3
i=4
i=5

在调用test1函数时 ,由于i为局部变量,在test1函数执行结束后,其生命周期也就结束了,在每次调用test1函数时,i都会被赋值为0,因此输出结果为5个1;而局部变量i经static修饰后其生命周期变长,在离开作用域时并不会被销毁,当test2函数调用时,变量i的值为上次函数结束调用结束时的值,因此输出结果为1-5。


可见静态局部变量i在运行的过程中并没有被释放!!!

这也恰巧说明了static修饰的局部变量更改局部变量的生命周期。


但是请注意static并没有改变作用域!!!


我们用以下代码来证明:

e2b0c11af40a4534b5a3a37d695141af.png

代码中我们可以看到当fun函数调用完后,再次给a赋值发生了报错,a无法再使用了。所以static只是改变了局部变量的生命周期而并没有改变其作用域。且static修饰的局部变量而并非全局变量。




2.全局变量


与静态局部变量相似,静态全局变量就是在全局变量前加上关键字static修饰,该变量就被定义为一个静态全局变量。未经初始化的静态全局变量会被程序自动初始化为0。


静态全局变量的优点:


1)静态全局变量只能在本文件中有效且不能被其他文件所用。


2)其他文件中可以定义相同名字的变量且互不冲突。


为了让大家可以更加容易理解优点1,让我们先了解一下多文件使用:


(1)为什么使用多文件?

当我们写代码时,难免会碰到代码多,且逻辑复杂等情况,这时如果将代码全写在一个文件内,在他人阅读我们的代码时会造成一定的困难,代码的可读性低,代码十分臃肿。并且有多个函数的声明,定义,还有引用头文件,定义之类的东西,在一个文件里太过于复杂,且容易遗漏,所以衍生出来了头文件。


(2)多文件使用的注意事项

一般会有一个头文件(.h)和一个main.c和若干个源文件(.c)文件。头文件中主要用来所有变量的声明,所有函数的声明,#define,类型typedef,struct等等。main.c主要用于实现代码逻辑,其他.c用于实现头文件中声明函数的功能。但是请注意,它们都需要引用自己创建的头文件!!!(例:#include"test.h")


好了,以上就是对于多文件使用的科普,所以话不多说,我们还是接下来用例子来证明优点1吧:

全局变量的跨文件访问:

eade8fc082494a5f85787b0dc880a011.png


1da3f56af46e404cbc9a7203abc6030a.png

e67978d6ee104a6dbba5000a76dfd2ff.png


从上面三张图我们可以知道,在test.c中定义的全局变量g_val可以再main.c中被使用,因此全局变量是可以进行跨文件访问的!!!

那么static修饰的静态全局变量呢???


7b110a2fc17f483c9f90877a6b229e14.png

8e677f2f7d754d38bc6fba19737100b0.png


我们可以看到在全局变量g_val前加上static,就出现了一系列报错,test.c中定义的全局变量无法在main.c中被使用了!这也恰好证明了static修饰的全局变量不能被外部其他文件直接访问!!!

看到这可能大家会有些疑惑,你不是说静态全局变量只能在本文件内被访问吗,可你这不只证明了静态全局变量不能被跨文件访问,你是不是蒙我们啊?别急,接下来我们就来证明这个结论:


30462d8f07b24e0586ecde73680ca7e2.png

我们将test函数里面加上一行代码,看看g_val能不能被打印出来,结果是可以的。所以,静态全局变量可以在本文件内被访问。

但是请注意由于静态全局变量不能跨文件访问,在运行代码时需要将main.c中对g_val的打印需要注释掉!!!


3348bd8626784b459afc68f429c3b0b2.png


虽然不能直接访问,但是也可以采用间接访问的方式:


0c57b89332cc42fd916270f9b6f061e5.png


4f475dadc8af4817a19e09dd2e59a4b9.png



相信通过这些例子,大家也应该可以理解静态全局变量只能在本文件中有效且不能被其他文件所用这个优点了。全局变量是具有外部链接属性的,如果全局变量被static修饰,外部链接属性就变成了内部链接属性。其他源文件就没法通过链接找到这个内容。




函数



在函数的返回类型前加上static关键字,函数即被定义为静态函数。静态函数与普通函数不同,它只能在声明它的文件当中可见,不能被其它文件使用。


其优点与静态全局变量近似:


1)静态函数不能被其它文件所用。


2)其它文件中可以定义相同名字的函数,不会发生冲突。

话不多说,上例子!



3742cb48b009402baf8c3bba09e9ca2a.png

从上一个例子中我们可以看到原先这个代码是可以编译过去的,但是如果在函数前加上static那么就会直接形成报错。

4a4df3770b954fe18f8e02cd654ffef6.png

我们可以看到有一个叫做LNK1120,这个错误叫做链接器错误,是程序在进行编译链接形成和执行程序时,链接错误,无法解析到test函数。

同样的,虽然不能直接访问,也是可以采用间接访问的方式:


f82f12c9849c480fa8226f9fa1433977.png

a7c5edb69004427f9bb957aeb82a7628.png

f19394ac31bc4e039ba64931fc689979.png


由代码所示,在test.c中定义test_helper函数,并在test.h中声明该函数,最后在main.c中调用该函数,是没问题的。(但是请注意,无关的代码需要注释掉!!!)


函数是具有外部链接属性的,其他源文件想使用函数,需要正确的声明就可以。但是函数被static修饰,外部链接属性就变成了内部链接属性,这个时候函数只能在自己所在的.c文件内部使用,不能在外部使用。


(补充:外部链接属性 - 所有的其他文件都能找到 内部链接属性 - static 无链接属性 - 局部变量)

以上也就证明了静态函数不能被其他文件所使用,并且实现了静态函数的间接访问。





总结


static是一个很有用的关键字,因为其独特的优点,合理地使用static可以形成一个良好的编码风格。static也因其特性,也是一个对于项目维护和提供安全保证上一个强有力的帮手。作为一名程序员,养成良好的编码风格,也是十分重要的!!!




























相关文章
|
2月前
|
存储 数据可视化 编译器
【C语言】union 关键字详解
联合体(`union`)是一种强大的数据结构,在C语言中具有广泛的应用。通过共享内存位置,联合体可以在不同时间存储不同类型的数据,从而节省内存。在嵌入式系统、硬件编程和协议解析等领域,联合体的使用尤为常见。理解和正确使用联合体可以使代码更加高效和灵活,特别是在内存受限的系统中。
120 3
【C语言】union 关键字详解
|
2月前
|
编译器 C语言
【C语言】extern 关键字详解
`extern` 关键字在C语言中用于跨文件共享变量和函数的声明。它允许你在一个文件中声明变量或函数,而在其他文件中定义和使用它们。理解 `extern` 的使用可以帮助你组织和管理大型项目的代码。
225 3
|
2月前
|
C语言
【C语言】break 关键字详解
- `break` 关键字用于提前退出循环体或 `switch` 语句的执行。 - 在 `for`、`while` 和 `do-while` 循环中,`break` 可以帮助程序在满足特定条件时退出循环。 - 在 `switch` 语句中,`break` 用于终止 `case` 代码块的执行,避免代码“穿透”到下一个 `case`。 - 注意 `break` 只会退出最内层的循环或 `switch` 语句,确保在嵌套结构中正确使用 `break` 以避免意外的控制流行为。
150 2
|
2月前
|
传感器 安全 编译器
【C语言】enum 关键字详解
`enum`关键字在C语言中提供了一种简洁而高效的方法来定义一组相关的常量。通过使用枚举,可以提高代码的可读性、可维护性,并减少错误的发生。在实际应用中,枚举广泛用于表示状态、命令、错误码等,为开发者提供了更清晰的代码结构和更方便的调试手段。通过合理使用枚举,可以编写出更高质量、更易维护的C语言程序。
149 2
|
2月前
|
缓存 安全 编译器
【C语言】volatile 关键字详解
`volatile` 关键字在 C 语言中用于防止编译器对某些变量进行优化,确保每次访问该变量时都直接从内存中读取最新的值。它主要用于处理硬件寄存器和多线程中的共享变量。然而,`volatile` 不保证操作的原子性和顺序,因此在多线程环境中,仍然需要适当的同步机制来确保线程安全。
89 2
|
2月前
|
存储 编译器 程序员
【C语言】auto 关键字详解
`auto` 关键字用于声明局部变量的自动存储类,其作用主要体现在变量的生命周期上。尽管现代C语言中 `auto` 的使用较少,理解其历史背景和作用对于掌握C语言的存储类及变量管理仍然很重要。局部变量默认即为 `auto` 类型,因此在实际编程中,通常不需要显式声明 `auto`。了解 `auto` 关键字有助于更好地理解C语言的存储类及其在不同场景中的应用。
99 1
|
2月前
|
C语言
【C语言】continue 关键字详解
`continue` 关键字在 C 语言中用于跳过当前循环中的剩余代码,并立即开始下一次迭代。它主要用于控制循环中的流程,使程序在满足特定条件时跳过某些代码。
165 1
【C语言】continue 关键字详解
|
2月前
|
存储 C语言
【C语言】static 关键字详解
`static` 关键字在C语言中用于控制变量和函数的作用域和生命周期。它可以用于局部变量、全局变量和函数,具有不同的效果。理解 `static` 关键字的用法有助于封装和管理代码,提高代码的可维护性和可靠性。
98 3
|
2月前
|
C语言
【C语言】return 关键字详解 -《回家的诱惑 ! 》
`return` 关键字在 C 语言中用于终止函数的执行,并将控制权返回给调用者。根据函数的类型,`return` 还可以返回一个值。它是函数控制流中的重要组成部分。
118 2
|
2月前
|
C语言
【C语言】sizeof 关键字详解
`sizeof` 关键字在C语言中用于计算数据类型或变量在内存中占用的字节数。它是一个编译时操作符,对性能没有影响。`sizeof` 可以用于基本数据类型、数组、结构体、指针等,了解和正确使用 `sizeof` 对于内存管理和调试程序非常重要。
109 2

热门文章

最新文章