013__作用域(空间)

server/2024/12/26 7:59:04/

[ 基本难度系数 ]:★★☆☆☆

一、基本概念

C语言中,标识符都有一定的可见范围,这些可见范围保证了标识符只能在一个有限的区域内使用,这个可见范围,被称为作用域(scope)

软件开发中,尽量缩小标识符的作用域是一项基本原则,一个标识符的作用域超过它实际所需要的范围时,就会对整个软件的命名空间造成污染,导致一些不必要的名字冲突和误解。

例子:你在一个工程里,定义了一个全局变量int num,那么接下来工程里面所有的区域,你都将要注意,定义变量时,不能和其产生冲突或者误解

二、函数声明作用域

  • 概念:在函数的声明式中定义的变量,其可见范围仅限于该声明式。
  • 要点:
    • 变量 num1 和 num2 只在函数声明式中可见。
    • 变量 num1 和 num2 可以省略,但一般不这么做,它们的作用是对参数的注解。
  • 示例代码:
#include <stdio.h>
extern int func1(int num1, int num2);    // 变量只作用在这一行
extern int func2(int, int);              // 上面的语句可以写成这样,但一般不建议,因为变量名可以为我们提供参考int main(int argc, char const *argv[])
{num1 = 100;  /*会报错:main.c:18:5: error: ‘num1’ undeclared (first use in this function) 原因:1、你真的没有定义这个变量num12、你定义了,但是num1变量,没能作用在你现在处在的内存区域*/
}

三、局部作用域

  • 概念:在代码块中定义的变量,其可见范围从其定义处开始,到代码块结束为止。
  • 要点:
    • 代码块指的是一对花括号 { } 括起来的区域。
    • 代码块可以嵌套包含,外层的标识符会被内嵌的同名标识符临时掩盖变得不可见。
    • 代码块作用域的变量,由于其可见范围是局部的,因此被称为局部变量。
  • 示例代码:
#include <stdio.h>
int main(int argc, char const *argv[])
{int num2 = 100;                  // 存储与栈空间,作用于本函数(main)的{}括号里面{ int num2 = 200;             // 存储与栈空间,作用于包括它的{}括号里面{int num2 = 300; num2 = 666;             // 优先使用最靠近它的{}括号(外层的标识符会被内嵌的同名标识符临时掩盖变得不可见)printf("num2(第三层) = %d\n", num2); }printf("num2(第二层) = %d\n", num2); }printf("num2(第一层) = %d\n", num2);    // printf函数因为在main函数的{}括号里,所以其打印的是作用范围在main函数的{}括号里num2变量}

四、全局作用域

  • 概念:在代码块外定义的变量,其可见范围可以跨越多个文件。
  • 要点:
    • 全局变量作用域在整个程序中,因此称其为全局变量
    • 如果a文件想要访问b文件的全局变量,需要在a文件函数体外,使用extern关键字在变量前面 进行修饰
    • 如果一个文件里的全局变量,只想作用于本文件,则在其前面加上static即可
  • 示例代码:
  • main.c文件
#include <stdio.h>
// 全局变量定义
int num3;                                // 存储与数据段的bss段中, 普通全局变量,跨文件可见(整个工程的文件都可以看到这个变量)
int num4 = 400;                          // 存储与数据段的data段中,普通全局变量,跨文件可见(整个工程的文件都可以看到这个变量)
static int num5;                         // 存储与数据段的bss段中, static全局变量,本文件可见(工程的其它文件不可以看到这个变量)
static int num6 = 600 ;                  // 存储与数据段的data段中,static全局变量,本文件可见(工程的其它文件不可以看到这个变量)// 全局函数声明
extern void a_func_1(void);              // 代码区(没有内存空间,只是一段跳转地址)int main(int argc, char const *argv[])
{num3 = 333;num4 = 444;printf("num3 = %d\n", num3);printf("num4 = %d\n", num4);// a_func_1();/*报错:a.c:(.text+0x27): undefined reference to `num5'a.c:(.text+0x31): undefined reference to `num5'原因:num5和num6由于在其它文件(a.c)不可见,其它文件(a.c)又调用了,这样会报错了*/
}

a.c文件

#include <stdio.h>
/*说明:对于函数:extern可以省略的,依然还是表示函数声明(因为你可以轻而易举的区分出谁是定义,谁是声明(有多条语句的就是定义,只有一条语句并且还有结束符;号的就是声明))对于全局变量:extern不可以省略的,因为可能会将其当成定义来对待(尤其是你没有初始化的时候)总结(给大家的建议):不论是函数,还是全局变量,都用extern来声明,不要省略不写*/
// 全局变量声明
extern int num3;    // 代码区,在本文件可见
extern int num5;    // 代码区,在本文件不可见(因为main函数使用static进行了修饰,掩盖了这个变量,所以找不到了)// 全局函数定义
void a_func_1(void) // 既是在代码区(代码文本+跳转地址),又在栈区(函数的具体内容)
{num3 = 100;printf("num3 = %d\n", num3);num5 = 555;printf("num5 = %d\n", num5);
}

五、作用域的临时掩盖

如果有多个不同的作用域相互嵌套,那么小范围的作用域会临时 “遮蔽” 大范围的作用域中的同名标识符,被 “遮蔽” 的标识符不会消失,只是临时失去可见性。

  • 示例代码:
int a = 100;// 函数代码块1
int main(void)
{printf("%d\n", a); // 输出100int a = 200;printf("%d\n", a); // 输出200// 代码块2 {printf("%d\n", a); // 输出200int a = 300;printf("%d\n", a); // 输出300}printf("%d\n", a); // 输出200
}void f()
{printf("%d\n", a); // 输出100
}

六、static关键字

C语言的一大特色,是相同的关键字,在不同的场合下,具有不同的含义。static关键字在C语言中有两个不同的作用:

  1. 将可见范围设定为标识符所在的文件:
    • 修饰全局变量:使得全局变量由原来的跨文件可见,变成仅限于本文件可见。
    • 修饰普通函数:使得函数由原来的跨文件可见,变成仅限于本文件可见。
  1. 将存储区域设定为数据段:
    • 修饰局部变量:使得局部变量由原来存储在栈内存,变成存储在数据段。
  • 示例代码:
      int a; // 普通全局变量,跨文件可见
static int b; // 静态全局变量,仅限本文件可见void f1()        // 普通函数,跨文件可见
{}static void f2() // 静态函数,仅限本文件可见
{}int main()
{int c; // 普通局部变量,存储于栈内存static int d; // 静态局部变量,存储于数据段
}

[ 课堂课后实验 ]:

【1】、实验1:(作用域)

回答如下问题:

  1. C语言总共有多少种作用域?
  2. 如果同一个作用域中,出现重名标识符,会怎样?
  3. 如果在并存的两个作用域中,出现重名标识符,会怎样?
  4. 如果在相互嵌套的两个作用域中,出现重名标识符,会怎样?‘

解析:


http://www.ppmy.cn/server/153269.html

相关文章

卷积神经网络(CNN)模型 CIFAR-10 数据集 例子

使用 TensorFlow 构建一个简单的卷积神经网络&#xff08;CNN&#xff09;模型&#xff0c;完成对 CIFAR-10 数据集的图像分类任务。 使用自动编码器作为特征提取器&#xff0c;先通过自动编码器对图像数据进行降维&#xff0c;将图像从高维映射到低维特征空间&#xff0c;然后…

【论文阅读】Comprehensive Review of End-to-End Video Compression

摘要&#xff1a; 近年来&#xff0c;端到端视频压缩作为一种新兴且有前景的解决方案开始在视频压缩领域受到关注。本文对端到端视频编码和解码技术的发展与现状进行了全面的综述&#xff0c;详细介绍了传统混合编码器和端到端编码器的基本原理。本研究深入探讨了从传统视频压…

【视觉惯性SLAM:相机成像模型】

相机成像模型介绍 相机成像模型是计算机视觉和图像处理中的核心内容&#xff0c;它描述了真实三维世界如何通过相机映射到二维图像平面。相机成像模型通常包括针孔相机的基本成像原理、数学模型&#xff0c;以及在实际应用中如何处理相机的各种畸变现象。 一、针孔相机成像原…

HarmonyOS NEXT 实战之元服务:静态案例效果--- 歌手推荐

背景&#xff1a; 前几篇学习了元服务&#xff0c;后面几期就让我们开发简单的元服务吧&#xff0c;里面丰富的内容大家自己加&#xff0c;本期案例 仅供参考 先上本期效果图 &#xff0c;里面图片自行替换 效果图1完整代码案例如下&#xff1a; import { authentication } …

AppAgent 源码 (xml 解析)

1. 数据准备 adb shell uiautomator dump /sdcard/output.xml # 获取手机ui界面的xml文件 adb pull /sdcard/output.xml output.xml # 将手机上的xml文件拉取到电脑上具体的xml文件&#xff1a; <?xml version1.0 encodingUTF-8 standaloneyes ?> <hierarchy ro…

威联通NAS部署openwrt软路由保姆级教程附镜像文件

创作立场&#xff1a;原创不易&#xff0c;拒绝搬运~ hello 大家好&#xff0c;我是你们的老伙伴&#xff0c;稳重的大王~ 本期教程为大家分享&#xff0c;怎么在NAS里面部署软路由&#xff0c;下面是软路由的镜像文件&#xff0c;有两个版本&#xff0c;400M的是定制版~ Sh…

基于vue-popperjs的二次封装弹窗

前言&#xff1a; 基于vue-popperjs的二次封装代码 <template><!-- 1. :appendToBody"true"是否把位置加到body外层标签上饿了么UI和antD是true&#xff0c;iview和vuetifyjs是false2. trigger属性触发方式&#xff0c;常用hover悬浮触发、clickToOpen鼠标…

如何部署SparkHistoryServer

spark-defaults.conf的配置&#xff1a; # 镜像内配置路径&#xff1a; /opt/spark/conf/spark-defaults.confspark.history.fs.logDirectoryhdfs://xxx spark.history.ui.port18080 spark.history.retainedApplications20 在提交Spark任务时&#xff0c;需要指定下面两个参数…