在C / C ++中,全局变量是否如我的教授所认为的那么糟糕?
在C / C ++中,全局变量是否如我的教授所认为的那么糟糕?
Answers:
全局变量的问题在于,由于每个函数都可以访问这些变量,因此越来越难弄清哪个函数实际读写这些变量。
要了解应用程序的工作方式,您几乎必须考虑修改全局状态的每个函数。可以做到,但是随着应用程序的增长,它将变得越来越困难,甚至几乎不可能(或者至少浪费时间)。
如果不依赖全局变量,则可以根据需要在不同函数之间传递状态。这样一来,您就更有机会了解每个功能的作用,因为您无需考虑全局状态。
重要的是要记住总体目标:清晰度
之所以有“无全局变量”规则,是因为大多数时候,全局变量会使代码的含义不清楚。
但是,就像许多规则一样,人们会记住该规则,而不是该规则的意图。
我看到过这样的程序,它们通过传递大量的参数来避免全局变量的危害,似乎使代码的大小增加了一倍。最后,使用全局变量会使程序对于阅读该程序的人更加清晰。通过漫不经心地遵守规则一词,原始的程序员未能遵守规则的意图。
因此,是的,全局变量通常很糟糕。但是,如果您最终觉得,使用全局变量可以使程序员的意图更加清楚,那么请继续。但是,请记住,当您强迫某人访问第二段代码(全局变量)以了解第一段代码的工作原理时,清晰度会自动下降。
我的教授曾经说过类似的话:如果正确使用全局变量就可以了。我认为我从来都不擅长正确使用它们,因此我很少使用它们。
static
大量使用全局变量,语言是C。由于局限于相对较小的翻译单元,它们开始类似于C ++对象的类变量。
program lifetime, file scope variables
。而一旦你传递一个指针变量对外部世界(这是不可能的自动变量),他们变得相当全球..
static
全局变量的作用域仅限于同一翻译单元。但是它们的生存期直到程序结束都是任何全局变量。
仅在没有其他选择时才应使用全局变量。是的,包括Singletons。90%的时间引入了全局变量,以节省传递参数的成本。然后发生多线程/单元测试/维护编码,您遇到了问题。
所以是的,在90%的情况下,全局变量都是错误的。您在大学期间不太可能看到例外。我可以想到的一个例外是处理诸如中断表之类的固有全局对象。诸如DB连接之类的事情似乎是全球性的,但并非如此。
全局变量为程序员创建的问题是,它扩展了正在使用全局变量的各个组件之间的组件间耦合表面。这意味着随着使用全局变量的组件数量增加,交互的复杂性也会增加。这种增加的耦合通常使进行更改时更容易将缺陷注入系统,也使缺陷更难以诊断和纠正。这种增加的耦合还可以减少进行更改时可用选项的数量,并且可以增加更改所需的工作量,因为通常必须跟踪也使用全局变量的各个模块才能确定更改的后果。
封装的目的与使用全局变量基本相反,它的目的是减少耦合,以使对源的理解和更改更容易,更安全,更容易测试。当不使用全局变量时,使用单元测试要容易得多。
例如,如果您有一个简单的全局整数变量被用作枚举指示符,表明各种组件都用作状态机,然后通过为新组件添加新状态来进行更改,则必须跟踪所有其他组件确保更改不会影响它们的组件。一个可能的问题的示例是,如果在各个地方都使用了一个switch
语句来测试枚举全局变量的值以及case
每个当前值的语句,并且这种情况发生在某些switch
语句没有default
大小写的情况下对于全局而言,这是一个意外的值。就应用程序而言,您突然具有不确定的行为。
另一方面,使用共享数据区域可能会包含一组在整个应用程序中引用的全局参数。这种方法通常用于内存占用较小的嵌入式应用程序。
在这类应用程序中使用全局变量时,通常将写入数据区域的责任分配给单个组件,而所有其他组件则将该区域视为该区域const
并从中读取,而不写入。采用这种方法限制了可能出现的问题。
来自全局变量的一些问题需要解决
修改诸如struct之类的全局变量的源时,必须重新编译使用该变量的所有内容,以便使用该变量的所有内容都知道其真实大小和内存模板。
如果一个以上的组件可以修改全局变量,那么全局变量中的数据不一致会导致问题。对于多线程应用程序,您可能需要添加某种锁定或关键区域来提供一种方法,以便一次仅一个线程可以修改全局变量,而当一个线程在修改变量时,所有更改都已完成。并且在其他线程可以查询或修改变量之前提交。
调试使用全局变量的多线程应用程序可能会更加困难。您可能会遇到竞争状况,这些竞争状况可能会造成难以复制的缺陷。由于几个组件通过全局变量进行通信,尤其是在多线程应用程序中,因此很难理解哪个组件在何时以及如何更改变量。
使用全局变量可能会导致名称冲突。与全局变量同名的局部变量可以隐藏全局变量。使用C编程语言时,还会遇到命名约定问题。解决方法是将系统分为子系统,该子系统具有特定子系统的全局变量,所有全局变量均以相同的前三个字母开头(有关在目标C中解决名称空间冲突的信息,请参阅此内容)。C ++提供了名称空间,使用C语言,您可以通过创建全局可见的结构来解决此问题,该结构的成员是各种数据项以及指向文件中以静态方式提供的数据和函数的指针,因此仅具有文件可见性,因此只能通过以下方式引用它们:全局可见的结构。
在某些情况下,原始应用程序的意图已更改,因此提供单个线程状态的全局变量被修改为允许多个重复线程运行。一个示例是为单个用户设计的简单应用程序,该应用程序使用全局变量来表示状态,然后管理部门发出了添加REST接口以允许远程应用程序充当虚拟用户的请求。因此,现在您不得不复制全局变量及其状态信息,以使单个用户以及远程应用程序中的每个虚拟用户都有自己独特的全局变量集。
使用C ++ namespace
和struct
C技术
对于C ++编程语言,该namespace
指令对减少名称冲突的可能性有很大的帮助。namespace
以及class
各种访问关键字(private
,protected
和public
)提供了封装变量所需的大多数工具。但是,C编程语言不提供此指令。此stackoverflow发布,C中的命名空间,为C提供了一些技术。
一种有用的技术是将单个内存驻留数据区定义为struct
具有全局可见性的,并且在其中struct
是指向要公开的各种全局变量和函数的指针。全局变量的实际定义使用static
关键字指定了文件范围。如果然后使用const
关键字指示哪些是只读的,则编译器可以帮助您强制执行只读访问。
使用该struct
技术还可以封装全局变量,以使其成为一种恰好是全局变量的包或组件。通过具有此类组件,可以更轻松地管理影响全局的更改以及使用全局的功能。
然而,尽管namespace
该struct
技术可以帮助解决名称冲突,但是组件间耦合的潜在问题仍然存在,使用全局变量尤其是在现代多线程应用程序中引入了这些问题。
全局变量与您使它们一样糟糕,不少。
如果要创建完全封装的程序,则可以使用全局变量。使用全局变量是一种“罪过”,但是编程上的过失在哲学上是很少的。
如果您查看L.in.oleum,将会看到一种语言,其变量仅是全局的。这是不可扩展的,因为库除了使用全局变量外别无选择。
就是说,如果您有选择并且可以忽略程序员的哲学,那么全局变量并不是那么糟糕。
如果正确使用Gotos,也不是。
最大的“坏”问题是,如果错误使用它们,人们将尖叫,火星着陆器坠毁,世界将爆炸……或类似的东西。
如果您的代码有可能最终在最高法院的审判中受到严格审查,那么您要确保避免使用全局变量。
请参阅本文: Buggy呼吸检测器代码反映了源代码审查的重要性
两项研究均确定了代码样式方面的一些问题。与审稿人有关的风格问题之一是广泛使用不受保护的全局变量。这被认为是较差的形式,因为它增加了程序状态变得不一致或值被无意修改或覆盖的风险。研究人员还对以下事实表示担忧:整个代码中十进制精度不能始终保持不变。
伙计们,我敢打赌那些开发人员希望他们没有使用全局变量!
我会用另一个问题回答这个问题:您是否使用singeltons / singeltons是否有害?
因为(几乎所有)singelton用法是一个荣耀的全局变量。
问题在于他们的坏处少,而危险的处境则更多。他们有自己的优缺点,在某些情况下,它们是完成特定任务的最有效方法或唯一方法。但是,即使您采取措施始终正确使用它们,它们也很容易被滥用。
一些优点:
一些缺点:
请注意,如果您愿意的话,我列出的前两个优点和前两个缺点完全相同,只是措辞不同。这是因为全局变量的功能确实可以使用,但是使它们有用的功能是所有问题的根源。
一些问题的一些潜在解决方案:
Globals
or GlobalVars
),或者对全局变量使用标准化的命名约定(例如global_[name]
or g_module_varNameStyle
(如注释中underscore_d所述) ))。这将记录它们的使用(您可以通过搜索名称空间/结构名称来找到使用全局变量的代码),并最小化对全局名称空间的影响。extern
在关联的标头中声明它们,因此可以将它们的使用限制为需要访问它们的编译单元。如果您的代码依赖于很多全局变量,但是每个编译单元只需要访问其中的少数几个变量,则可以考虑将它们排序到多个源文件中,因此将每个文件对全局变量的访问限制为更容易。它们的好坏取决于您如何使用它们。大多数人倾向于不良地使用它们,因此对它们普遍持谨慎态度。如果使用得当,它们可能是一大福音。但是,如果使用不当,它们会并且会再次咬你,何时何地以及如何让你最不期望。
观察它们的好方法是它们本身并不坏,但它们可以启用不良设计,并且可以使不良设计的影响成倍增加。
即使您不打算使用它们,也最好知道如何安全地使用它们并选择不使用它们,而不是不使用它们,因为您不知道如何安全地使用它们。如果您发现自己需要维护依赖于全局变量的现有代码,那么如果您不知道如何正确使用它们,可能会遇到困难。
g_module_varNameStyle
易懂的名称。需要明确的是,我没有使用全局变量,如果我可以很容易地避免它-关键字容易,因为自从我不再相信他们必须避免-或者说模糊 -不惜一切代价,我有一个更好的时间,和我的代码是(震惊!)更整洁了
正如某人在另一线程中所说(我在措辞中所说)“在您完全理解这样做的后果之前,不应破坏这样的规则”。
有时,全局变量是必需的,或者至少非常有用(例如,使用系统定义的回调)。另一方面,由于您被告知的所有原因,它们也非常危险。
编程的许多方面可能应该留给专家。有时您需要一把非常锋利的刀。但是直到准备就绪,您才可以使用它。
使用全局变量有点像扫地毯。这是一个快速修复,并且在短期内比使用簸a或真空吸尘器清理起来容易得多。但是,如果您以后再搬地毯,那下面就会大吃一惊。
绝对不。虽然滥用它们...这很糟糕。
为了这个目的而无意识地删除它们只是……没脑子。除非您知道优点和缺点,否则最好按照您的教taught /学到的方法去做明确的事情,但是全局变量没有任何隐含的错误。当您了解利弊时,请更好地做出自己的决定。
不,它们一点也不差。您需要查看由编译器生成的(机器)代码才能确定,有时使用局部而不是全局要差得多。还要注意,将“静态”放在局部变量上基本上是使其成为全局变量(并创建了真正的全局变量可以解决的其他难看问题)。“本地人”尤其糟糕。
全局变量还使您可以完全控制内存的使用,这与本地变量相比要困难得多。如今,仅在内存非常有限的嵌入式环境中才有意义。在假定嵌入式与其他环境相同并假定整个编程规则都相同之前,需要了解一些知识。
质疑所教的规则是件好事,大多数不是出于被告知的原因。最重要的教训虽然不是这是永远陪伴您的规则,但这是为了通过本课程并继续前进而必须遵守的规则。在生活中,您会发现,对于XYZ公司,您最终将不得不遵循其他编程规则,以保持薪水。在这两种情况下,您都可以争论规则,但我认为您的工作运气要比在学校好得多。教授们,您只是众多学生中的另一个,您的席位很快就会被替换,在您的工作中,您是一小队的成员,他们必须看完这种产品,并且在这种环境下,制定的规则适用于团队成员以及产品和公司的利益,因此,如果每个人都怀有想法,或者对于特定产品,有充分的工程学理由违反了您在大学或一本通用编程书籍中学到的知识,则将您的想法卖给团队,并将其写下来,作为一种有效的方法(如果不是首选的方法) 。在现实世界中,一切都是公平的游戏。
如果您遵守学校或书籍中教给您的所有编程规则,您的编程生涯将非常有限。您可能可以生存并取得丰硕的事业,但是可供使用的环境的广度和宽度将极为有限。如果您知道规则的存在方式和原因,并且可以为之辩护,那就太好了,如果您的唯一理由是“因为我的老师这么说”,那不是那么好。
请注意,像这样的主题经常在工作场所争论不休,并且随着编译器和处理器(和语言)的发展,这样的规则也会不断发展,而不会捍卫您的立场,并且可能会被其他持不同意见的人上课前进。
同时,只要说出最大声或携带最大摇杆的声音就做(直到您叫出最大声并携带最大摇杆的声音为止)。
我想反对整个线程中提出的观点,即它使多线程本身变得更难或更不可能。全局变量是共享状态,但是全局变量的替代方法(例如,传递指针)也可能共享状态。多线程的问题在于如何正确使用共享状态,而不是该状态是否通过全局变量或其他方式共享。
大多数时候,当您执行多线程时,您需要共享一些东西。例如,在生产者-消费者模式中,您可能共享一些包含工作单元的线程安全队列。并且您可以共享它,因为该数据结构是线程安全的。当涉及线程安全时,该队列是否全局是完全无关紧要的。
如果不使用全局变量,那么在整个线程中表达的将程序从单线程转换为多线程的隐含希望将变得更加容易。是的,全局球可以使自己用脚射击更容易,但是有很多方法可以射击自己。
我并不提倡全局变量,就其他观点而言,我的观点仅是程序中的线程数与变量作用域无关。
全局变量的使用实际上取决于要求。它的优点是,它减少了重复传递值的开销。
但是您的教授是对的,因为它引起了安全性问题,因此应尽可能避免使用全局变量。全局变量还会产生有时 难以调试的问题。
例如:-
在运行时修改变量值的情况。那时,很难确定代码的哪一部分正在修改它以及在什么条件下进行修改。
就配置而言,全局是好的。当我们希望我们的配置/修改有全球影响的整个项目。
因此,我们可以更改一种配置,并且更改直接针对整个项目。但是我必须警告您,使用全局变量必须非常聪明。
我通常将全局变量用于很少更改的值,例如单例或指向动态加载库中函数的函数指针。在多线程应用程序中使用可变全局变量往往会导致难以跟踪错误,因此,我通常尝试避免这种情况。
使用全局而不是传递参数通常会更快,但是如果您正在编写如今经常使用的多线程应用程序,则通常效果不佳(您可以使用线程静态函数,但是性能提升值得怀疑) 。
安全性较小意味着如果将变量声明为全局变量,则任何人都可以操作变量,为此,请以本示例为例,如果您在银行程序中将余额作为全局变量,则用户功能可以操作该变量,银行职员也可以操作这样就产生了一个问题。应该给唯一的用户只读和提取功能,但是当用户亲自在办公桌上提供现金时,银行的业务员可以增加金额。这就是它的工作方式。
在多线程应用程序中,请使用局部变量代替全局变量,以避免出现竞争状况。
当多个线程访问共享资源,并且至少一个线程具有对数据的写访问权时,就会发生竞争状态。然后,程序的结果是不可预测的,并且取决于不同线程对数据的访问顺序。