• 第2章 C语言高级的函数


    文档配套视频讲解链接地址

    1. 腾讯课堂视频链接地址 : 09_函数_变量作用域
    2. 腾讯课堂视频链接地址 : 10_函数_变量的可见性与生存期
    3. 腾讯课堂视频链接地址 : 11_函数_变量的作用域生存期实例
    4. 腾讯课堂视频链接地址 : 11_函数_main函数参数

    第02章 函数

    2.1 变量的作用域

    1. 函数原形的作用域
    • 函数原型中的参数,其作用域始于 “(”,结束于")"。
    • 例如,设有下列原型声明:
    double Area(double r);   // r 在 左圆括号开始声明, 结束语右圆括号 , 在这个范围内r可以使用  
    
    • 1
    • 不能用于程序正文其他地方,因而可有可无。
    2. 块作用域
    • 在块中声明的标识符,其作用域自声明处起,限于块中
    • 例如:
    void fun(int a)    // a 的作用域   开始的位置
    {  
        int b = a; 	  // b 的作用域开始的位置 
        if (b>0)
        {
            int c;   // c 的作用域开始的位置 
            ......
        }    // c 的作用域结束的位置             
    }   // a 的作用域结束的位置 , b 的作用域结束的位置 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 实例17
    • 源文件
    03-chigh/17-zuoyongyu.c
    
    • 1
    • 源代码
    #include 
    
    int func(int a) // a = 1
    {
        int b = a;
        {
            int c = 10;
            printf("1:c=%d\n", c);
        }
        printf("2:c=%d\n", c);
    
        printf("1:b=%d\n", b);
        printf("1:a=%d\n", a);
    } // a 和b 的作用域结束位置
    
    int main(int argc, char const *argv[])
    {
        func(1); // 调用函数 , 传递参数1
        printf("2:b=%d\n", b); // b  在这个范围内, 没有定义 
        printf("2:a=%d\n", a); // a  在这个范围内, 没有定义 
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 运行结果
    17-zuoyongyu.c:10:24: error: ‘c’ undeclared (first use in this function)
         printf("2:c=%d\n", c);
                            ^
    17-zuoyongyu.c:10:24: note: each undeclared identifier is reported only once for each function it appears in
    17-zuoyongyu.c: In function ‘main’:
    17-zuoyongyu.c:19:24: error: ‘b’ undeclared (first use in this function)
         printf("2:b=%d\n", b); // b  在这个范围内, 没有定义
                            ^
    17-zuoyongyu.c:20:24: error: ‘a’ undeclared (first use in this function)
         printf("2:a=%d\n", a); // a  在这个范围内, 没有定义
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    3. 文件作用域

    ​ 不在前述各个作用域中出现的声明,具有文件作用域,这样声明的标识符的作用域开始于声明点,结束于文件尾。

    • 实例18
    • 源文件
    03-chigh/18-zuoyongyu.c
    
    • 1
    • 源代码
    #include 
    
    // 不在块作用域内, 也不在函数原型的作用域, 这种作用域就是文件作用域 , 就是全局变量
    // 从变量定义的位置开始, 到文件的结束
    int a = 100 ;   // 文件作用域
    
    int func(int x) ; // x 是声明作用域
    
    
    int main(int argc, char const *argv[]) // 
    {
        int b = 10 ; // 块作用域 
    
        a=200; 
        printf("1:a=%d\n",a); 
    
        func(1);
    
        return 0;
    }
    
    
    int func(int x) 
    {
        a = 300 ; 
        printf("2:a=%d\n",a); 
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 运行结果
    1:a=200
    2:a=300
    
    • 1
    • 2

    2.2 变量的可见性

    1. 可见性是从对标识符的引用的角度来谈的概念
    2. 可见性表示从内层作用域向外层作用域“看”时能看见什么。
    3. 如果标识在某处可见,则就可以在该处引用此标识符
    4. 标识符应声明在先,引用在后。
    5. 如果某个标识符在外层中声明,且在内层中没有同名标识符的声明,则该标识符在内层可见。
    6. 对于两个嵌套的作用域,如果在内层作用域内声明了与外层作用域中同名的标识符,则外层作用域的标识符在内层不可见。
    • 例如
    int i;   //文件作用域 全局变量
    int main()
    { 
        i=5;  
        {  
            int i;  //块作用域 局部变量
         	i=7;
         	printf("i=%d\n",i); // i=7   同名的变量, 作用域内的变量会屏蔽作用域外的同名变量
        }
        printf("i=%d\n",i);    // i=5    作用域内内层中没有同名的变量, i在内层可见
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2.3 变量的生存期

    1. 变量生存期的bug程序

    变量从产生到结束的这段时间就是它的生存期。在变量生存期内,变量将保持它的值,直到被更新为止。

    • 实例19
    • 变量生存期的bug
    • 源文件
    linux@ubuntu:~/work/emb2207/03-chigh/35-static$ tree
    .
    ├── build
    ├── CMakeLists.txt
    ├── main.c
    └── src
        ├── beep.c
        ├── beep.h
        ├── led.c
        └── led.h
    
    2 directories, 6 files
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 源代码
    #include 
    
    
    int * func(void)
    {
        int a = 100 ;  // 后面加上 static 延长声明周期 为静态期 
        return &a; 
    }
    
    int main(int argc, char const *argv[]) 
    {
        int * p  = func();
        printf("*p=%d\n",*p);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 运行结果
    19-shengcunqi.c: In function ‘func’:
    19-shengcunqi.c:7:12: warning: function returns address of local variable [-Wreturn-local-addr]
         return &a;
                ^~
    段错误 (核心已转储)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    2. 静态生存期
    • 这种生存期与程序的运行期相同。
    • 在文件作用域中声明的变量具有这种生存期。
    • 在函数内部声明静态生存期变量,要冠以关键字static 。
    3. 动态生存期
    • 块作用域中声明的,没有用static修饰的变量是动态生存期的对象(习惯称局部生存期变量)。

    • 开始于程序执行到声明点时,结束于命名该标识符的作用域结束处。

    • 例如

    • 静态生存期与动态生存期

    #include 
    
    void fun(); 
    int main()
    { 
      fun();
      fun();
    }
    void fun()
    { 
      static int a=1; // a 是块作用域(局部作用域) , 静态生存期 , static修饰的局部变量只能被初始化一次
      int i=5;        // i 是块作用域(局部作用域) , 动态生存期
      a++;
      i++;
      printf("i=%d,a=%d\n",i,a); 
    } // 到这里时 i的值被销毁, a的值不销毁, 值也不变 
    
    // 第一次 func 输出 : i= 6 , a = 2; 
    // 第二次 func 输出 : i= 6 , a = 3;  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • static 修饰的局部变量的值可以被继承 , 上次执行的结果可以保留, 是因为这个局部变量是静态生存期, 整个程序运行期间都可以保持

    2.4 变量作用域、可见性与生存期实例

    • 实例20
    • 源文件
    03-chigh/20-zonghe.c
    
    • 1
    • 源代码
    #include  
    
    int i=1;   // i 为全局变量,具有静态生存期。
    void other(void); // 函数声明 
    int main()   
    { 
        static int a;   // 局部变量,块作用域 , 静态生存期,局部可见。 static 修饰的变量不赋值初始值, 初始值为0 
        int b=-10;      // b为局部变量,块作用域, 动态生存期,局部可见 。
        int c=0;        // c为局部变量,块作用域, 动态生存期,局部可见 。
        printf(<<"---MAIN---\n");
        printf("i:%d  a:%d  b:%d  c:%d \n",i,a,b,c);
        c=c+8;  
        other();  // 第一次调用other();
        printf("---MAIN---\n");
        printf("i:%d  a:%d  b:%d  c:%d \n",i,a,b,c);
        i=i+10;  
        other();   // 第二次调用other();
    }
    void other(void)
    {
      static int a=2; //只第一次进入函数时被初始化。
      static int b; // static 修饰的变量不赋值初始值, 初始值为0 
      // a,b 局部变量,块作用域 , 静态生存期,局部可见。
      
      int c=10; // c 局部变量,块作用域 , 动态生存期,局部可见。
      a=a+2; 
      i=i+32; 
      c=c+5;
      printf("---OTHER---\n");
      printf("i:%d  a:%d  b:%d  c:%d \n",i,a,b,c);
      b=a;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 运行结果
    // 推算出 4次输出的结果
    // 1: i=1 , a=0 , b=-10 , c=0
    // c=8 
    // 2: i=33, a=4 , b=0   , c=15
    // b=4 
    // 3: i=33, a=0 , b=-10 , c=8
    // i= 43 
    // 4: i=75, a=6 , b=4   , c=15
    
    // 计算机运算的结果:
    ---MAIN---
    i=1 , a=0 , b=-10 , c=0 
    ---OTHER---
    i=33 , a=4 , b=0 , c=15 
    ---MAIN---
    i=33 , a=0 , b=-10 , c=8 
    ---OTHER---
    i=75 , a=6 , b=4 , c=15 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    2.5 main命令行参数

    程序在运行时, 可以带入参数 , 有main函数的参数进行获取

    #include 
    
    int main(int argc, char const *argv[])
    {
    
        printf("argc = %d\n", argc); // argc : 表示参数的个数
        // argv 是指针还是数组  , 是数组, argv先结合[] , 因此是数组
        // 什么类型的数组, char const * 是修饰类型的 , 字符指针数组
        // argv 是一个字符指针类型的数组,里面的元素是字符指针变量, 指针变量中保存字符串的起始地址
        // argv 中的元素(字符指针变量)保存的是传入参数字符串的首地址
        for (int i = 0; i < argc; i++)
        {
            printf("argv[%d]=%s\n",i,argv[i]); // argv[i]是数组的一个元素, 这个元素保存的是传入参数字符串的首地址
        }
        return 0;
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • argc :表示参数的个数

    • argv: 表示参数的内容

    • 例如 , 获取运行程序时传入的参数的个数和内容

    • 实例21

    • 源文件

    03-chigh/21-argc.c
    
    • 1
    • 源代码
    #include 
    
    int main(int argc, char const *argv[])
    {
    
        printf("argc = %d\n", argc); // argc : 表示参数的个数
        // argv 是指针还是数组  , 是数组, argv先结合[] , 因此是数组
        // 什么类型的数组, char const * 是修饰类型的 , 字符指针数组
        // argv 是一个字符指针类型的数组,里面的元素是字符指针变量, 指针变量中保存字符串的起始地址
        // argv 中的元素(字符指针变量)保存的是传入参数字符串的首地址
        for (int i = 0; i < argc; i++)
        {
            printf("argv[%d]=%s\n",i,argv[i]); // argv[i]是数组的一个元素, 这个元素保存的是传入参数字符串的首地址
        }
        return 0;
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 运行结果
    linux@ubuntu:~/work/emb2207/03-chigh$ ./21-argc 1.c 2.c 3.c 4.c
    argc = 5
    argv[0]=./21-argc
    argv[1]=1.c
    argv[2]=2.c
    argv[3]=3.c
    argv[4]=4.c
    linux@ubuntu:~/work/emb2207/03-chigh$ ./21-argc
    argc = 1
    argv[0]=./21-argc
    linux@ubuntu:~/work/emb2207/03-chigh$ ./21-argc 1.c
    argc = 2
    argv[0]=./21-argc
    argv[1]=1.c
    linux@ubuntu:~/work/emb2207/03-chigh$ ./21-argc 1.c 2.c
    argc = 3
    argv[0]=./21-argc
    argv[1]=1.c
    argv[2]=2.c
    linux@ubuntu:~/work/emb2207/03-chigh$ ./21-argc 1.c 2.c 3.c
    argc = 4
    argv[0]=./21-argc
    argv[1]=1.c
    argv[2]=2.c
    argv[3]=3.c
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
  • 相关阅读:
    C++&QT---QT-day1
    软件测试|Python自动化测试实现的思路
    10.VScode下载---Windows64x
    lintcode 831 · 三数之和 II 【中等 vip 二分】
    IOS工程:如何在apple后台为app添加沙盒测试账户
    【ASM】字节码操作 转换已有的类 在方法进入和退出的时候 添加
    算法通关村第二关-青铜终于学会链表了
    Docker(1)
    TCP的优化
    uboot 添加命令
  • 原文地址:https://blog.csdn.net/shengli001842/article/details/127814048