《小菜狗 C 语言入门 + 进阶笔记》(14)作用域 -> 局部变量和全局变量

文章目录:《小菜狗 C 语言入门 + 进阶笔记》(0)简介

在这里插入图片描述


1、作用域

所谓 作用域 ,就是变量的有效范围。

C 语⾔的变量作用域主要有两种:块作用域(block scope)和 文件作用域(file scope)。

2、代码块(程序块)

所谓代码块(程序块),就是由 { } 包围起来的代码。代码块在 C 语言中随处可见,例如函数体、选择结构、循环结构等。 C 语言允许在代码块内部定义变量,这样的变量具有块级作用域;换句话说,在代码块内部定义的变量只能在代码块内部使用,出了代码块就无效了。

3、变量的作用域与存储类型

3.1、引出了两个问题
  • 第一,每一个变量在什么范围内起作用。【作用域问题
  • 第二,每一个变量何时生成、何时消失,在程序中能存在多久。【生命周期问题
3.2、生命周期问题

变量的生命周期取决于变量的存储类型, C 语言中变量有 4 种不同的存储类型,分别用关键字 auto、static、extern 、register 表示。

这个我们后面再讲解。

3.3、二者的关系

一个变量如果不在其生命周期,肯定无作用域可言;如果在其生命周期,也未必一直起作用,即使起作用也是在特定的范围内。

3.4、变量与作用域

(2.13)形参和实参 中提到,形参变量要等到函数被调用时才分配内存,调用结束后立即释放内存。这说明形参变量的作用域非常有限,只能在函数内部使用,离开该函数就无效了。

C 语言中所有的变量都有自己的作用域

有些变量可以在所有代码文件中使用,有些变量只能在当前的文件中使用,有些变量只能在函数内部使用,有些变量只能在 for 循环内部使用。

决定变量作用域的是变量的定义位置。在不同位置定义的变量,它的作用域是不一样的。

位置有 3 种:

  • 函数体外:在函数块内部的局部变量–块作用域;
  • 函数体内:在所有函数外部的全局变量–文件作用域;
  • 函数语句块内:形式参数在函数参数定义中;

接下来解释一下局部变量和全局变量。

4、局部变量

4.1、定义

定义在某个函数或块的内部声明的变量称为局部变量

  • 它们只能被该函数或该代码块内部的语句使用。
  • 局部变量在函数外部是不可知的。
  • 它的作用域仅限于函数内部,离开该函数后就是无效的。
4.2、实例

在这里,所有的变量( a、b、c)都是 main() 函数的局部变量。

#include <stdio.h>
 
int main ()
{
  /* 局部变量声明 */
  int a, b;
  int c;
 
  /* 实际初始化 */
  a = 10;
  b = 20;
  c = a + b;
 
  printf ("value of a = %d, b = %d and c = %d\n", a, b, c);
  return 0;
}
4.3、局部变量的 4 点说明
  1. 在 main 函数中定义的变量也是局部变量,只能在 main 函数中使用;同时,main 函数中也不能使用其它函数中定义的变量。main 函数也是一个函数,与其它函数地位平等。
  2. 形参变量、在函数体内定义的变量都是局部变量。实参给形参传值的过程也就是给局部变量赋值的过程。
  3. 可以在不同的函数中使用相同的变量名,它们表示不同的数据,分配不同的内存,互不干扰,也不会发生混淆。

如下案例:

#include <stdio.h>
 
/* 全局变量声明 */
int g = 20;
 
int main ()
{
  /* 局部变量声明 */
  int g = 10;
 
  printf ("value of g = %d\n",  g);
 
  return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

value of g = 10
  1. 语句块中也可定义变量,它的作用域只限于当前语句块。

5、全局变量

5.1、定义

定义在所有函数外部定义的变量称为全局变量

  • 全局变量是定义在函数外部,通常是在程序的顶部。
  • 全局变量在整个程序生命周期内都是有效的,全局变量可以被任何函数访问。

也就是说,全局变量在定义后整个程序中都是可用的。

  • 它的作用域默认是整个程序,也就是所有的源文件,包括 .c 和 .h 文件。
5.2、实例 1

值输出

void func() {
    x = 1;     //报错
    y = 2;
}

float x,y;      //全局变量
int main() {
    x = 1;      //正确
    y = 2;
    return 0;
}

x、y 都是在函数外部定义的全局变量。C 语言代码是从前往后依次执行的,由于 x、y 定义在函数 func() 之后,所以在 func() 内无效;但在 func() 和 main() 内都有效。

5.3、实例 2

根据长方体的长宽高求它的体积以及三个面的面积

#include <stdio.h>

int s1, s2, s3;  //面积

int vs(int a, int b, int c){
    int v;  //体积
    v = a * b * c;
    s1 = a * b;
    s2 = b * c;
    s3 = a * c;
    return v;
}

int main(){
    int v, length, width, height;
    printf("Input length, width and height: ");
    scanf("%d %d %d", &length, &width, &height);
    v = vs(length, width, height);
    printf("v=%d, s1=%d, s2=%d, s3=%d\n", v, s1, s2, s3);

    return 0;
}

运行结果:

Input length, width and height: 10 20 30↙
v=6000, s1=200, s2=600, s3=300

根据题意,我们希望借助一个函数得到三个值:体积 v 以及三个面的面积 s1、s2、s3。全局变量的作用域是整个程序,在函数 vs() 中修改 s1、s2、s3 的值,能够影响到包括 main() 在内的其它函数。

6、局部变量和全局变量

6.1、初始化局部变量和全局变量

局部变量被定义时,系统不会对其初始化,您必须自行对其初始化。定义全局变量时,系统会自动对其初始化

如下所示:

数据类型初始化默认值
int0
char‘\0’
float0
double0
pointerNULL

注意:

正确地初始化变量是一个良好的编程习惯,否则有时候程序可能会产生意想不到的结果,因为未初始化的变量会导致一些在内存位置中已经可用的垃圾值。

6.2、局部变量和全局变量综合示例

输出变量的值

#include <stdio.h>

int n = 10;  //全局变量

void func1(){
    int n = 20;  //局部变量
    printf("func1 n: %d\n", n);
}

void func2(int n){
    printf("func2 n: %d\n", n);
}

void func3(){
    printf("func3 n: %d\n", n);
}

int main(){
    int n = 30;  //局部变量
    func1();
    func2(n);
    func3();
    //代码块由{}包围
    {
        int n = 40;  //局部变量
        printf("block n: %d\n", n);
    }
    printf("main n: %d\n", n);

    return 0;
}

运行结果:

func1 n: 20
func2 n: 30
func3 n: 10
block n: 40
main n: 30
  • 代码中虽然定义了多个同名变量 n,但它们的作用域不同,所有不会产生命名冲突。

下面是对输出结果的详细分析:

  • 对于 func1(),输出结果为 20,显然使用的是 func1() 内部的 n,而不是外部的 n。
  • 调用 func2() 时,会把 main() 中的实参 n 传递给 func2() 中的形参 n,此时形参 n 的值变为 30。形参 n 也是局部变量,所以就使用它了。
  • func3() 输出 10,使用的是全局变量,因为在 func3() 中不存在局部变量 n,所以编译器只能到函数外部,也就是全局作用域中去寻找变量 n。
  • main() 中 printf() 语句输出 30,说明使用的是 main() 中的 n,而不是外部的 n。

7、形参和局部变量

函数的参数,被当作该函数内的局部变量,如果与全局变量同名它们会优先使用。

下面是一个实例:

#include <stdio.h>
 
/* 全局变量声明 */
int a = 20;
 
int main ()
{
  /* 在主函数中的局部变量声明 */
  int a = 10;
  int b = 20;
  int c = 0;
  int sum(int, int);
 
  printf ("value of a in main() = %d\n",  a);
  c = sum( a, b);
  printf ("value of c in main() = %d\n",  c);
 
  return 0;
}
 
/* 添加两个整数的函数 */
int sum(int a, int b)
{
    printf ("value of a in sum() = %d\n",  a);
    printf ("value of b in sum() = %d\n",  b);
 
    return a + b;
}

当上面的代码被编译和执行时,它会产生下列结果:

value of a in main() = 10
value of a in sum() = 10
value of b in sum() = 20
value of c in main() = 30

全局变量与局部变量内存中的区别

  • 全局变量保存在内存的全局存储区中,占用静态的存储单元;
  • 局部变量保存在栈中,只有在所在函数被调用时才动态地为变量分配存储单元。

代码中虽然定义了多个同名变量 n,但它们的作用域不同,在内存中的位置(地址)也不同,所以是相互独立的变量,互不影响,不会产生重复定义(Redefinition)错误。

C 语言规定,只能从小的作用域向大的作用域中去寻找变量,而不能反过来,使用更小的作用域中的变量。对于 main() 函数,即使代码块中的 n 离输出语句更近,但它仍然会使用 main() 函数开头定义的 n,所以输出结果是 30。

8、关于变量的命名

每一段可运行的 C 语言代码都包含了多个作用域,即使最简单的 C 语言代码也是如此。

C 语言规定,在同一个作用域中不能出现两个名字相同的变量,否则会产生命名冲突;但是在不同的作用域中,允许出现名字相同的变量,它们的作用范围不同,彼此之间不会产生冲突。

这句话有两层含义:

  • 不同函数内部可以出现同名的变量,不同函数是不同的局部作用域;
  • 函数内部和外部可以出现同名的变量,函数内部是局部作用域,函数外部是全局作用域。

不同函数内部的同名变量是两个完全独立的变量,它们之间没有任何关联,也不会相互影响。

请看下面的代码:

#include <stdio.h>

void func_a(){
    int n = 100;
    printf("func_a: n = %d\n", n);
    n = 86;
    printf("func_a: n = %d\n", n);
}

void func_b(){
    int n = 29;
    printf("func_b: n = %d\n", n);
    func_a(); //调用func_a()
    printf("func_b: n = %d\n", n);
}

int main(){
    func_b();
    return 0;
}

运行结果:

func_b: n = 29
func_a: n = 100
func_a: n = 86
func_b: n = 29

下面是对输出结果的详细分析:

func_a() 和 func_b() 内部都定义了一个变量 n,在 func_b() 中,n 的初始值是 29,调用 func_a() 后,n 值还是 29,这说明 func_b() 内部的 n 并没有影响 func_a() 内部的 n。这两个 n 是完全不同的变量,彼此之间根本“不认识”,只是起了个相同的名字而已,这就好像明星撞衫,北京和云南都有叫李红的,赶巧了而已。

文章目录:《小菜狗 C 语言入门 + 进阶笔记》(0)简介

每日一更!

公众号、优快云等博客:小菜狗编程笔记

谢谢点赞关注哈!目前在飞书持续优化更新~

日更较慢有需要完整笔记请私我,C/C++/数据结构-算法/单片机51-STM32-GD32-ESP32/嵌入式/Linux操作系统/uboot/Linux内核-驱动-应用/硬件入门-PCB-layout/Python/后期小程序和机器学习!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小菜狗编程笔记

你的鼓励将是我最大的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值