为什么要少用全局变量

news2025/8/7 6:08:55

     为什么要少用全局变量?甚至有些公司禁止用全局变量。有一个说法是这样的,全局变量的最佳前缀是什么?答://

 接下来就粗略说说这个问题。

1、全局变量和局部变量

(1)全局变量:定义在函数外,存放空间为静态存储区,作用域为整个工程文件,若其它文件使用该变量,可以在本文件中用extern声明一遍该变量或者包含声明了该变量的头文件;在整个程序运行期间全局变量的值都会存在。由于同工程中的所有函数都能引用全局变量的值,因此如果在一个函数中改变了全局变量的值, 就能影响到其他函数中全局变量的值。

(2)静态全局变量:只在定义它的文件内有效,效果和全局变量一样,不过就在本文件内部;也就是说,用static修饰过的全局变量,改变了他的作用域,限制了他的使用范围。

(3)局部变量:在定义它的函数内有效,但是函数返回后失效。“在函数内定义的变量”,即在一个函数内部定义的变量,只在本函数范围内有效。

(4)静态局部变量:只在定义它的函数内有效,只是程序仅分配一次内存,函数返回后,该变量的值不会消失;静态局部变量的生存期虽然为整个工程,但是其作用仍与局部变量相同,即只能在定义该变量的函数内使用该变量。退出该函数后, 尽管该变量还继续存在,但不能使用它。也就是说用static修饰过的局部变量,改变了他的生存期。 

关于四者的性能如表1-1所示。

                    表1-1:

内存空间作用域生存期
全局变量静态存储区全局整个程序运行期间
静态全局变量静态存储区作用于定义它的文件里整个程序运行期间
局部变量作用于定义它的函数里定义的函数运行期间
静态局部变量静态存储区作用于定义它的函数里整个程序运行期间

2、全局变量的优点和缺点

优点:

(1)全局可见,任何 一个函数或线程都可以读写全局变量-同步操作简单。

 (2)内存地址固定,读写效率比较高。

缺点

(1)过多的全局变量会占用较多的内存单元:全局变量保存在静态存贮区,程序开始运行时为其分配内存,程序结束释放该内存。与局部变量的动态分配、动态释放相比,生存期比较长,因此过多的全局变量会占用较多的内存单元。
(2)全局变量破坏了函数的封装性能:前面的章节曾经讲过,函数象一个黑匣子,一般是通过函数参数和返回值进行输入输出,函数内部实现相对独立。但函数中如果使用了全局变量,那么函数体内的语句就可以绕过函数参数和返回值进行存取,这种情况破坏了函数的独立性,使函数对全局变量产生依赖。同时,也降低了该函数的可移植性。
(3)全局变量使函数的代码可读性降低:由于多个函数都可能使用全局变量,函数执行时全局变量的值可能随时发生变化,对于程序的查错和调试都非常不利。

(4)导致软件分层的不合理:全局变量相当于一条快捷通道,它容易使程序员模糊了“设备层”和“应用层”之间的边界。写出来的底层程序容易自作多情地关注起上层的应用。这在软件系统的构建初期的确效率很高,功能调试进度一日千里,但到了后期往往bug一堆,处处“补丁”,雷区遍布。说是度日如年举步维艰也不为过。

(5)增加了bug出现的概率:全局变量大量使用,少不了有些变量流连忘返于中断与主回圈程序之间。这个时候如果处理不当,系统的bug就是随机出现的,无规律的。

(6)全局变量的读写,可能会延迟,这主要是体现在“写”操作上,由于写操作,一般需要2个周期操作,所以有可能会出现,这边没写完时,那边已经读了,结果读到的不是最终值,这个是一个概率事件,概率 很小,但是并不代表没有。

(7)在程序运行时,根据需要到内存中相应的存储单元中调用,如果一个变量在程序中频繁使用,例如循环变量,那么,系统就必须多次访问内存中的该单元,影响程序的执行效率。因此,C\C++语言还定义了一种变量,不是保存在内存上,而是直接存储在CPU中的寄存器中,这种变量称为寄存器变量。变量存储在寄存器上,就相当于直接操作CPU,程序当然会运转的很流畅;计算速度也是很快。全局变量不是分配在寄存器,对于一些循环,一定要避免频繁使用全局变量。但是如果又避免不了使用全局变量,一个巧妙的方法就是把全局变量赋值给一个临时变量,对临时变量进行操作,最后再将临时变量的值赋给全局变量。

3、减少使用全局变量的方法

(1)能不用全局变量尽量不用,除了系统状态和控制参数、通信处理和一些需要效率的模块,其它的基本可以靠合理的软件分层和编程技巧来解决。


(2) 如果不可避免需要用到,那能藏多深就藏多深

(a)如果只有某.c文件用,就用static限制作用域,如表1-1所示,静态全局变量的作用域是定义它的文件,工程中的其它文件不能使用。
(b) 如果只有一个函数用,那就定义局部变量。
(c) 如果非要开放出去让人读取,那就用函数return出去,这样该变量只读属性;防止被其它文件中的函数改写。
(d)如果其它文件一定要修改这个变量,就编写get_xxx和set_xxx接口函数。

例如:

timer.c

  1. static volaitle uint32_t s_wCounter;
  2. ISR(Timer0_vect)
  3. {
  4.     s_wCounter++;
  5. }
  6. uint32_t get_counter(void)
  7. {
  8.     uint32_t wResult;
  9.     SAFE_ATOM_CODE(                           //! 原子操作的保护宏
  10.         wResult = s_wCounter;
  11.     )
  12.     return wResult;
  13. }
  14. void set_count(uint32_t  count)
  15. {
  16.   s_wCounter = count;
  17. }

timer.h

  1. #ifndef __TIMER_H__
  2. #define __TIMER_H__
  3. extern uint32_t get_counter(void);
  4. extern void set_count(uint32_t  count);
  5. #endif

在其它文件中就可以通过get_counter() 和set_count() 两个接口函数来操作静态全局变量s_wCounter。

(e)如果某个模块数据比较多,如果每个变量都编写接口函数显然太麻烦,此时可以通过结构体,联合体,位域将众多的参数进行封装,然后再通过接口函数进行操作。

下面是采用类似于stm32库函数的做法, 对这些参数进行打包封装为“结构体变量”类型, 并在h文件中导出。在c文件中把这个参数定义为static类型(静态变量),并实现读/写接口函数。

 // 模块接口h文件中:
typedef struct {
  Param_A;
  Param_B;
  Param_C;
} User_Param_Typedef;

bool ModuleName_SetParam(User_Param_Typedef *param);
bool ModuleName_GetParam(User_Param_Typedef *param);

 // 模块实现c文件中:
static User_Param_Typedef user_param;
static bool user_access_locked;

bool ModuleName_SetParam(User_Param_Typedef *param)
{
  // 此处还可以增加对参数的保护特性
  user_param.Param_A = param->Parma_A;
  user_param.Param_B = param->Parma_B;
  user_param.Param_C = param->Parma_C;
  return true;
}

bool ModuleName_GetParam(User_Param_Typedef *param)
{
  // 此处还可以增加对参数的保护特性
  param->Param_A = user_param.Parma_A;
  param->Param_B = user_param.Parma_B;
  param->Param_C = user_param.Parma_C;
  return true;
}
 

 // app文件中:

...
// 配置参数
User_Param_Typedef  user_param;    // 临时变量(局部变量 ) 或 静态变量
user_param.Param_A = 1;
user_param.Param_B = 2;
user_param.Param_C = 3;
ModuleName_SetParam(&user_param);
...

// 获取参数
ModuleName_GetParam(&user_param);

如果在应用中, 仅需要修改其中一个参数,可以根据上面的类似的方法修改, 增加基于 “基地址偏移量” 读/写的接口函数。

参考文件:

C语言中,全局变量滥用的后果竟如此严重?_张巧龙的博客-CSDN博客

c语言中如何避免过多使用全局变量_李肖遥的博客-CSDN博客

如何尽量地避免使用全局变量呢? (amobbs.com 阿莫电子论坛 - 东莞阿莫电子网站)

全局变量的优缺点及为什么要少用全局变量_猪哥-嵌入式的博客-CSDN博客_全局变量的好处

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/15670.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

RocketMQ NameServer 概览

🍊 Java学习:Java从入门到精通总结 🍊 深入浅出RocketMQ设计思想:深入浅出RocketMQ设计思想 🍊 绝对不一样的职场干货:大厂最佳实践经验指南 📆 最近更新:2022年11月18日 &#…

析构函数详解

析构函数1.概念与特性2.工作原理4.析构的顺序如果一个类中什么成员都没有,那么该类简称为空类。而空类中其实并不是真的什么都没有,任何类在什么都不写时,编译器会自动生成以下6个默认成员函数。构造函数:主要完成初始化工作析构函…

内网渗透神器CobaltStrike之配置与基础操作(一)

CobaltStrike简介 Cobalt Strike: C/S架构的商业渗透软件,适合多人进行团队协作,可模拟APT做模拟对抗,进行内网渗透。 Cobalt Strike 一款GUI的框架式渗透工具,集成了端口转发、服务扫描,自动化溢出,多模…

megahit源码迁移解析

megahit源码迁移大作业 在进行megahit源码迁移前需要清理自己的实验环境 1、链接鲲鹏服务器 2、进入源码存放地址/opt/portadv/portadmin/sourcecode 环境准备,清理之前实验环境后下载本次实验所需的源码 之前存在的其他文件,删除code 登录代码迁移工…

Python常见操作的时间复杂度

Python常见操作的时间复杂度 本文整理了Python中常见数据结构操作的时间复杂度,旨在帮助大家了解Python操作的性能,协助运行更快的代码。 文章目录标注方法List操作Set操作Deque操作标注方法 程序时间复杂度一般用"大O表示法(Big-O no…

windows11系统WSL2安装ubuntu20.04桌面

文章目录1. MobaXterm安装2.WSL安装xfce desktop3. 连接桌面参考链接1. MobaXterm安装 这个比较简单,没介绍 2.WSL安装xfce desktop 安装命令 sudo apt-get install xfce4-terminal sudo apt-get install xfce4安装完之后需要稍微配置一下: export …

【并发编程六】c++进程通信——信号量(semaphore)

【并发编程六】c进程通信——信号量(semaphore)一、概述二、信号量三、原理四、过程1、进程A过程2、进程B过程五、demo1、进程A2、进程B六、输出七、windows api介绍1. 创建信号量 CreateSemaphore()2. 打开信号量 OpenSemaphore()3. 等待 WaitForSingle…

力扣LeatCode算法题第三题-无重复字符的最长子串

要求: 给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。 我一开始采用的第一种方法是使用hashmap去比对大小,在idea上可以跑通程序,但在leatcode的编译器中,无法通过字符串s"" 和s"…

苹果推送和开发证书更新

1.背景 推送证书(生产Apple Push Services和开发APNs Development iOS)的有效期都是一年,将要过期的时候,苹果官方会发邮件提醒。 2.csr和推送证书更新 打开mac电脑,找到启动台-->其他,打开钥匙串访问。…

uni-app入门:WXML数据绑定

1.简单数据绑定 2.组件属性数据绑定 3.运算绑定 3.1三元运算符 3.2算数运算 3.3字符串拼接运算 3.4逻辑判断运算 正文 WXML全称:wexin markup language,微信标签语言,可以理解为web中的html,…

JavaEE之HTTP协议 Ⅱ

文章目录一、认识请求"报头"(header)1.HOST2.Content-Length3.Content-Type4.User-Agent (简称 UA)5.Referer4.Cookie二、HTTP响应详解1. 认识"状态码"(status code)2. Content-Type3. 如何构造HTTP请求总结一、认识请求"报头"(header) 这里的键…

php资源列表|开发者应知晓

PHP PSR 代码标准 官网: https://www.php-fig.org原文: https://github.com/php-fig/fi...中文: https://psr.phphub.orgPHP资源列表PHP设计模式PHP知识技能树PHP资源站 PHP WeeklyCodeVisuallyPHP LeagueKnpLabs开发项目 FastAdmin - 基于 ThinkPHP5 + Bootstrap 的极速后台开…

ubuntu搭建Elasticsearch过程与问题

目录 一.下载Elasticsearh 二.解压 创建需要的文件目录 三.修改配置文件 四.遇到的问题 1.root账号启动问题 2.创建文件不授权切换到非root用户test的时候报错 3.启动最后还是报错 4.浏览器请求http://localhost:9200 报错:received plaintext http traffic…

【python】算法与数据结构作业记录分析

目录 算法与数据结构实验题 9.21 朋友圈 ★实验任务 ★数据输入 ★数据输出 输入示例 输出示例 代码实现 效果展示 算法与数据结构实验题 9.24 水杯 ★实验任务 ★数据输入 ★数据输出 输入示例 输出示例 代码实现 效果展示 算法与数据结构实验题 9.21 朋友圈 …

实验(四):LCD1602显示实验

一、实验目的与任务 实验目的: 1. 掌握LCD1602显示控制方法; 2. 掌握利用Proteus进行单片机控制系统的仿真及调试方法。 3. 掌握单片机开发板的使用。 任务: 1.根据要求编写程序,并写出原理性注释; 2. 将检查程序运行的…

Java入门项目——读书管理系统

Java简单实现读书管理系统一、前言二、思路及整体框架三、代码展示1.有关读书包(Book)2.有关用户包3.有关操作书的包一、前言 相信有很多小伙伴学习完了【JavaSE】基础语法,想知道自己到底学的怎么样,或则学完不知道这么把知识点…

JavaFX之Scene Builder的使用(开发一款GUI小工具原来这么简单)

文章目录一、前言二、JavaFX与Scene Builder下载三、Scene Builder的使用四、详细教学(示例)4.1 环境配置4.2 创建fxml文件以及Controller类文件4.3 自定义界面4.4 运行我们的程序五、拓展总结博主个人社区:开发与算法学习社区 博主个人主页&…

创建.gitignore文件并使用

创建 .gitignore文件 第一种方式 在项目根目录下直接创建一个文件,后缀改成 .gitignore 即可。 第二种方式 用git创建,到根目录下,执行 touch .gitignore,即可看见目录下已经出现了该忽略文件。 添加忽略规则 # 忽略所有以 …

httpOnly对于抵御Session劫持的个人小结

Ⅰ 什么是http only?起到什么防护作用? cookie中设置了HttpOnly属性,那么通过js脚本将无法读取到cookie信息,主要防护的攻击手段:XSS不能通过document对象直接获取cookie Ⅱ 怎么绕过http only的防护(三种&#xff…

5、CSS——三种样式和样式优先级、CSS中颜色设置方式、标签选择器和基本选择器的优先级

目录 一、行内样式 二、内部样式 三、外部样式 1、创建外部样式步骤 2、引入外部样式的两种方式 2.1 第一种 2.2 第二种 3、style标签内的注释符号 四、样式优先级 五、CSS中颜色设置方式 1、使用颜色的英文单词 2、使用十六进制表示法 3、使用rgb()表示法…