函数-变量的作用域和生命周期

发布于:2025-07-27 ⋅ 阅读:(15) ⋅ 点赞:(0)

变量的作用域

引入问题

我们在函数设计的过程中,经常要考虑对于参数的设计,换句话说,我们需要考虑函数需要几个参数,需要什么类型的参数,但我们并没有考虑函数是否需要提供参数,如果说函数可以访问到已定义的数据,则就不需要提供函数形参。

那么我们到底要不要提供函数形参,取决于什么?答案就是变量的作用域(如果函数在变量的作用域范围内,则函数可以直接访问数据,无需提供形参)

变量作用域

**概念:**变量的作用范围,也就是说变量在什么范围有效。

变量的分类

根据变量的作用域不同,变量可以分为:

  • 全局变量

    说明:定义在函数之外,也称之为外部变量或者全程变量。

    作用域:从全局变量定义到本源文件结束。

    初始值:整型和浮点型,默认值是0;字符型,默认值是\0;指针型,默认值NULL

    举例:

     int num1;  // 全局变量,num1能被fun1、fun2、main共同访问
     void fun1(){}
     int num2;  // 全局变量,num2能被fun2、main共同访问
     void fun2(){}
     void main(){}
     int num3;  // 全局变量,不能被任何函数访问
    
  • 局部变量

    说明 作用域 初始值
    形式参数(形参) 函数作用域 随机值,需要手动赋初值
    函数内定义的变量 函数作用域 随机值,需要手动赋初值
    复合语句中定义的变量 块作用域 随机值,需要手动赋初值
    for循环表达式1定义的变量 块作用域 随机值,需要手动赋初值

    举例:

     // a,b就是形式参数(局部变量)
     int add(int a, int b)
     {
         return a + b;
     }
     
     int add2(int a, int b)
     {
         // z就是函数内定义的变量(局部变量)
         int z = a + b;
         return z;
     }
     
     int list(int arr[], int len)
     {
         // i就是for循环表达式1的变量(局部变量)
         for(int i = 0; i < len; i++)
         {
             // num就是复合语句中定义的变量(局部变量)
             int num = arr[i];
         }
     }
    

    使用全局变量的优缺点

    优点:

    1. 利用全局变量可以实现一个函数对外输出的多个结果数据。
    2. 利用全局变量可以减少函数形参的个数,从而降低内存消耗,以及因为形参传递带来的时间消耗。

    缺点:

    1. 全局变量在程序的整个运行期间,始终占据内存空间,会引起资源消耗。
    2. 过多的全局变量会引起程序的混乱,操作程序结果错误。
    3. 降低程序的通用性,特别是当我们进行函数移植时,不仅仅要移植函数,还要考虑全局变量。
    4. 违反了“高内聚,低耦合”的程序设计原则。

    总结:

    ​ 我们发现弊大于利,建议尽量减少对全局变量的使用,函数之间要产生联系,仅通过实参+形参的方式产生联系。

作用域举例

注意:

如果全局变量和局部变量同名,程序执行的时候,就近原则(区分作用域)

 int a = 10;  // 全局变量  全局作用域
 
 int main()
 {
     int a = 20;  // 局部变量  函数作用域
     
     printf("%d\n", a); // 20 就近原则
     
     for (int a = 0; a < 5; a++)  // 局部变量 块作用域
     {
         printf("%d", a); // 0 1 2 3 4  就近原则
     }
     
     printf("%d\n",a);  // 20 就近原则
 }

变量的生命周期

定义

**概念:**变量在程序运行中的存在时间(内存申请到内存释放的时间)

根据变量存在的时间不同,变量可分为静态存储方式动态存储方式

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

变量的存储类型

语法:

 变量的完整定义格式: [存储类型] 数据类型 变量列表;

存储类型:

  • auto

    auto存储类型只能修饰局部变量,被auto修饰的局部变量是存储在动态存储区(栈区和堆区)的。auto也是局部变量默认的存储类型。

     int main()
     {
         int a;
         int b;
         // 以下写法等价于上面写法
         auto int a;
         auto int b;
         
         int a,b;
         // 以下写法等价于上面写法
         auto int a,b;
     }
    
  • static

    **修饰局部变量:**局部变量会被存储在静态存储区。局部变量的生命周期被延长。但是作用域不发生改变,不推荐

    **修饰全局变量:**全局变量的生命周期不变,但是作用域衰减,一般限制全局变量只能在本源文件内访问,其他文件不可访问。

    **修饰函数:**被static修饰的函数,只能被当前文件访问,其他引用该文件的文件是无法访问的,有点类似于java中private

  • extern

    外部存储类型:只能修饰全局变量,此全局变量可以被其他文件访问,相当于扩展了全局变量的作用域。

    extern修饰外部变量,往往是外部变量进行声明,声明该变量是在外部文件中定义的。起到一个标识作用。函数同理。

    demo01.c

     #include "demo01.h"
     
     int fun_a = 10;
     int fun1(){..}
    

    demo02.c

     #include "demo01.h"
     
     // 声明访问的外部文件的变量
     extern int fun_a;
     // 声明访问的外部文件的函数
     extern int fun1();
         
     int fun2();
    
  • register

    寄存器存储类型:只能修饰局部变量,用register修饰的局部变量会直接存储到CPU的寄存器中,往往将循环变量设置为寄存器存储类型(提高读的效率)

     for (register int i = 0; i < 10; i++)
     {
         ...
     }
    
static关键字的作用
  1. static修饰局部变量,延长其生命周期,但不影响局部变量的作用域。
  2. static修饰全局变量,不影响全局变量的生命周期,会限制全局变量的作用域仅限本文件内使用(私有化);
  3. static修饰函数:此函数就称为内部函数,仅限本文件内调用(私有化)。static int funa(){..}

内部函数和外部函数

  • 内部函数:使用static修饰的函数,称作内部函数,内部函数只能在当前文件中调用。
  • 外部函数:使用extern修饰的函数,称作外部函数,extern是默认的,可以不写(区分编译环境),也就是说本质上我们所写的函数基本上都是外部函数,建议外部函数在被其他文件调用的时候,在其他文件中声明的时候,加上extern关键字,主要是提高代码的可读性。

网站公告

今日签到

点亮在社区的每一天
去签到