全局变量何时被认为是良好/推荐的实践?

10

我一直在阅读关于全局变量为什么不好以及为什么不应该使用的文章。然而,大多数常用的编程语言都以某种方式支持全局变量。

所以我的问题是,为什么仍然需要全局变量?它们提供了一些无法替代的独特优势吗?与用户指定的自定义间接方法相比,全局寻址有何好处?

据我所知,在现代编程语言中,全局寻址的性能惩罚与从内存地址计算每个偏移量的性能惩罚相同,无论是从“全局”用户内存的开头还是从 this 或任何其他指针的偏移量。因此,就性能而言,用户可以使用常见的指针间接方法在必要时模拟全局变量,而不会失去实际全局变量的性能。那么除此之外呢?真的需要全局变量吗?


从这里您将获得一些信息。https://dev59.com/fnVC5IYBdhLWcg3w0EoD - Gangadhar
@Gangadhar - 在C语言中没有命名空间,但在C++中,我可以使用命名空间共享可见性,那么这与仅限于需要的地方使用全局变量的可见性有何不同呢? - user2341104
@user2341104,正如你所说,在C语言中没有命名空间。在C语言中,没有可能声明或定义全局变量,其可见性仅限于需要它的地方(除了声明静态全局变量以限制使用范围)。我链接了上面的一个帖子,其中有一个回答包含这句话:“你必须问自己:这真的需要在全局范围内吗?”这就是你从那里得到的信息。 - Gangadhar
6个回答

18

全局变量通常不是因为它们的性能而糟糕,而是因为在规模显着的程序中,它们使封装所有内容变得困难-存在信息“泄漏”,这通常会使弄清楚正在发生什么变得非常困难。

基本上,您的变量范围应该只包括代码工作和相对容易理解所需的内容,不多也不少。在一个打印12倍表格的程序中使用全局变量是可管理的,但在一个数百万行的会计程序中使用全局变量就不那么好了。


6

我认为这是另一个类似于 goto 的主题 - 它是一个“信仰问题”。

有很多方法可以“解决”全局变量,但如果您仍然在代码中的各个地方访问相同的内存位,可能会出现问题。

全局变量对某些事情非常有用,但一定要“小心使用”(比 goto 更需要如此,因为误用的范围更大)。

有两件事会让全局变量成为问题: 1. 很难理解对变量正在进行什么操作。 2. 在多线程环境中,如果一个全局变量由一个线程写入并被任何其他线程读取,您需要进行某种形式的同步。

但是,在某些时候,全局变量非常有用。例如,拥有一个config变量,其中包含来自应用程序配置文件的所有配置值。否则,将其存储在某个从一个函数传递到另一个函数的对象中,只是额外的工作,并且没有任何好处,特别是如果配置变量是只读的。

总的来说,我建议避免使用全局变量。


1
如果全局变量能够解决需要传递伪全局对象的问题,那么出于某种错误的纯洁感而使用后者是愚蠢的。对于基于唯一大型对象的当前项目,该对象在所有地方都浪费了空间/时间来触发“this”指针。我曾尝试将其设置为单例...然后意识到如果可能尽量使用(A)静态变量和全局变量,只有在静态作用域限制时才使用静态变量。毕竟这都是同一个程序。 - underscore_d

5
全局变量意味着全局状态。这使得在程序中存储与特定部分或函数本地相关的重叠状态变得不可能。
例如,假设我们在全局变量中存储了一个给定用户的凭证,并且这些凭证在我们的整个程序中都被使用。现在,要将我们的程序升级为允许同时使用多个用户将会更加困难。如果我们只是将用户的状态作为参数传递给函数,那么升级到多个用户时就会遇到更少的问题。

1
我的问题是为什么全局变量仍然是必需的原因。
有时你需要从很多不同的函数中访问相同的数据。这就是你需要全局变量的时候。
例如,我现在正在处理一段代码,它看起来像这样:
static runtime_thread *t0;

void 
queue_thread (runtime_thread *newt)
{
   t0 = newt;
   do_something_else ();
}

void 
kill_and_replace_thread (runtime_thread *newt)
{
   t0->status = dead;
   t0 = newt;
   t0->status = runnable;
   do_something_else ();
}

注意:以上内容是一种混合了C语言和伪代码的示例,旨在让您了解全局变量何时实际上是有用的。

但是如果我只是在同一个命名空间中声明它们,并在函数内部使用该命名空间呢? - user2341104
@user2341104,你误解了我的意思。如果你看一下我为你写的示例代码,你需要让t0在许多操作它的函数中全局可访问(在同一个文件中,在这种情况下)。你不能只将它定义到一个函数中,因为如果你这样做,其他许多函数将无法访问它。 - NlightNFotis

0

在编写任何跨平台库时,几乎必须使用静态全局。这些全局变量是静态的,以便它们保留在翻译单元内。由于需要向用户隐藏其特定于平台的实现,因此几乎没有任何不使用静态全局变量的跨平台库。这些特定于平台的实现保存在静态全局变量中。当然,如果它们使用不透明指针,并要求平台特定实现在这样的结构中保存,则可以创建没有任何静态全局的跨平台库。但是,这样的对象需要传递到此类库中的所有功能。因此,您必须在每个地方传递此不透明指针,或创建静态全局变量。

还存在标识符限制问题。编译器(尤其是旧版编译器)在作用域内处理的标识符数量受到限制。许多操作系统仍然使用大量的 #define 而不是枚举,因为他们的旧编译器无法处理膨胀了标识符的枚举常量。正确重写头文件可以解决其中一些问题。


-1

全局变量在每个函数中都可以使用,包括主函数。同时请记住,如果您在全局范围内初始化一个变量,则其初始值将在每个函数中相同,但是您可以在函数内重新初始化它,以在该函数中使用不同的值。通过这种方式,您不必在每个函数中再次声明相同的变量。但是有时候它们可能会引起麻烦。

  • 列表项

全局名称随处可用。当您认为正在使用本地变量时,可能会无意中使用全局变量。

  • 如果在声明全局变量时出现错误,则必须将更改应用于整个程序,例如,如果您意外地将其声明为int而不是float

如果您在全局范围内初始化变量,则其初始值将在每个函数中相同,但是您可以重新初始化它...以在该函数中使用不同的值来使用该变量。什么?不对。要么您正在更改全局变量以供所有未来函数查看(即不是“初始值将相同”),要么您正在声明一个不同的本地变量,恰好具有相同的名称,隐藏全局变量并且无法离开函数的作用域。在任何情况下,您都可能会遇到麻烦。 - underscore_d

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接