什么时候可以在C中使用全局变量?

显然,那里的意见多种多样,从“从不!永远封装(即使它只是一个宏!)”到“没什么大不了的-在方便时使用它们”。

所以。

具体的具体原因(最好举个例子)

  • 为什么全局变量很危险
  • 何时应使用全局变量代替替代方法
  • 对于那些不恰当地使用全局变量的人有哪些选择

尽管这是主观的,但我会选择一个答案(对我而言,最好的答案是每个开发人员与全球人士之间的爱恨关系),社区将对他们的投票仅次于此。

我认为对于新手来说,获得这种参考很重要,但是如果存在另一个与您的答案基本相似的答案,请不要打乱它-添加评论或编辑其他人的答案。

-亚当

Adam Davis asked 2020-06-29T20:42:20Z
15个解决方案
48 votes

变量应始终具有较小的范围。 其背后的观点是,每当您扩大范围时,就会有更多可能修改变量的代码,因此在解决方案中会引发更多的复杂性。

因此很明显,如果设计和实现自然允许,则最好避免使用全局变量。 因此,除非确实需要全局变量,否则我不愿使用全局变量。

我也不同意“从不”的说法,就像其他任何概念一样,全局变量是在需要时应使用的工具。 我宁愿使用全局变量,也不愿使用一些仅能掩盖真正意图的人为构造(如传递指针)。使用全局变量的好例子是单例模式实现或嵌入式系统中的寄存器访问。

关于如何实际检测全局变量的过度使用:检查,检查,检查。 每当我看到一个全局变量时,我都必须问自己:在全局范围内,真的需要吗?

Dan Cristoloveanu answered 2020-06-29T20:42:43Z
18 votes

使全局变量起作用的唯一方法是给它们命名,以确保它们是唯一的。

该名称通常具有与某些“模块”或功能集合相关联的前缀,全局变量尤其针对该模块或功能集合。

这意味着变量“属于”这些功能-它是其中的一部分。 确实,通常可以使用与其他功能一起使用的一个小功能“包装”全局文件-在相同的.h文件中使用相同的名称前缀。

奖金。

当您这样做时,突然之间,它不再是真正的全局性。 现在,它是相关功能模块的一部分。

这总是可以做到的。 稍加思考,就可以将每个以前的全局变量分配给某些函数集合,分配给特定的.h文件,并与允许您更改变量而不破坏任何内容的函数隔离。

与其说“从不使用全局变量”,不如说“将全局变量的职责分配给最有意义的某个模块”。

S.Lott answered 2020-06-29T20:43:30Z
11 votes

考虑一下这句话:“如果范围足够狭窄,那么一切都是全球性的”。

在这个时代,仍然有可能需要编写一个非常快速的实用程序来完成一项一次性工作。

在这种情况下,创建对变量的安全访问所需的能量大于在如此小的实用程序中调试问题所节省的能量。

这是我能想到的唯一的情况,即全局变量是明智的,而且相对罕见。 有用,新颖的程序如此之小,可以完全保留在大脑的短期记忆中,这种情况越来越少见,但它们仍然存在。

实际上,我可以大胆地宣称,如果程序不是那么小,那么全局变量应该是非法的。

  • 如果变量永远不会改变,则它是一个常量,而不是变量。
  • 如果该变量需要通用访问,则应该存在两个子例程来获取和设置它,并且应该将它们同步。
  • 如果程序从小处开始,然后又可能从大处开始,则将其当作今天的大程序进行编码,并取消全局变量。 并非所有程序都会增长! (当然,这是假设程序员愿意不时丢弃代码。)
Paul Brinkley answered 2020-06-29T20:44:22Z
9 votes

如果多个方法需要一个变量(而不是将变量传递到每个方法中),则C中的全局变量可用于使代码更具可读性。 但是,它们很危险,因为所有位置都具有修改该变量的能力,从而可能难以跟踪错误。 如果必须使用全局变量,请始终确保仅通过一种方法直接对其进行修改,并让所有其他调用者都使用该方法。 这将使调试与该变量更改有关的问题变得更加容易。

Jeff Yates answered 2020-06-29T20:44:43Z
9 votes

当您不担心线程安全代码时:在有意义的地方使用它们,换句话说,在适当的地方将其表示为全局状态。

当您的代码可能是多线程的时:不惜一切代价避免。 将全局变量抽象到工作队列或其他线程安全的结构中,或者在绝对必要时将它们包装在锁中,请记住这些可能是程序中的瓶颈。

Dan Lenski answered 2020-06-29T20:45:08Z
4 votes

我来自“从不”营地,直到我开始在国防工业工作。 有一些行业标准要求软件使用全局变量而不是动态(C情况下为malloc)内存。 我不得不重新思考我正在处理的某些项目的动态内存分配方法。 如果您可以使用适当的信号量,线程等保护“全局”内存,那么这可能是内存管理的可接受方法。

gthuffman answered 2020-06-29T20:45:28Z
4 votes

代码复杂性不是唯一需要关注的优化。 对于许多应用程序而言,性能优化具有更高的优先级。 但更重要的是,在许多情况下,使用全局变量可以大大减少代码的复杂性。 在很多情况下,也许是专门的情况下,全局变量不仅是可接受的解决方案,而且是首选方案。 我最喜欢的专业示例是它们用于在应用程序的主线程与实时线程中运行的音频回调函数之间提供通信。

认为全局变量在多线程应用程序中是一种责任,这是一种误导,因为任何变量(无论作用域如何)都是潜在的责任,如果它暴露于多个线程上的更改。

谨慎使用全局变量。 应尽可能使用数据结构来组织和隔离全局名称空间的使用。

可变范围可为程序员提供非常有用的保护-但这会带来一定的成本。 我今晚来写有关全局变量的文章是因为我是一位经验丰富的Objective-C程序员,经常对数据访问中面向对象的障碍感到沮丧。 我认为反全局狂热主要来自年轻的,理论丰富的程序员,这些程序员主要是孤立地使用面向对象的API,而没有系统级API及其在应用程序开发中的互动的丰富实践经验。 但是我必须承认,当供应商随意使用命名空间时,我会感到沮丧。 例如,一些Linux发行版在全局范围内预定义了“ PI”和“ TWOPI”,这破坏了我的许多个人代码。

ctpenrose answered 2020-06-29T20:46:04Z
2 votes

您需要考虑在什么情况下也将使用全局变量。 将来您希望此代码重复。

例如,如果您正在使用系统内的套接字来访问资源。 将来,您将希望访问这些资源中的多个资源,如果答案是肯定的,那么我将首先远离全局变量,因此不需要进行重大重构。

JProgrammer answered 2020-06-29T20:46:29Z
1 votes

该工具与其他通常被过度使用的工具一样,但我不认为它们是邪恶的。

例如,我有一个实际上像在线数据库的程序。 数据存储在内存中,但是其他程序可以对其进行操作。 内部例程的行为与数据库中的存储过程和触发器非常相似。

这个程序有数百个全局变量,但是如果您考虑一下它是什么数据库,却有大量的全局变量。

到目前为止,该程序已经在许多版本中使用了十年,这从来都不是问题,我会在一分钟内再次进行。

我将承认在这种情况下,全局变量是具有用于更改对象状态的方法的对象。 因此,在调试时跟踪谁更改了对象不是问题,因为我总是可以在更改对象状态的例程上设置断点。 或更简单地说,我只是打开记录更改的内置日志。

Kevin Gale answered 2020-06-29T20:47:07Z
1 votes

当多个功能需要访问数据或写入对象时,应使用全局变量。 例如,如果您必须传递数据或对多个功能的引用,例如单个日志文件,连接池或需要在整个应用程序中访问的硬件引用。 这样可以防止很长的函数声明和重复数据的大量分配。

除非绝对必要,否则通常不应该使用全局变量,因为只有在明确要求这样做或程序结束时才清除全局变量。 如果运行的是多线程应用程序,则多个函数可以同时写入变量。 如果您有错误,则跟踪该错误可能会更加困难,因为您不知道哪个函数正在更改变量。 除非使用明确为全局变量赋予唯一名称的命名约定,否则还会遇到命名冲突的问题。

Steropes answered 2020-06-29T20:47:33Z
1 votes

声明常量时。

user15039 answered 2020-06-29T20:47:53Z
1 votes

我可以想到几个原因:

调试/测试目的(警告-尚未测试此代码):

#include <stdio.h>
#define MAX_INPUT 46
int runs=0;
int fib1(int n){
    ++runs;
    return n>2?fib1(n-1)+fib1(n-2):1;
};
int fib2(int n,int *cache,int *len){
    ++runs;
    if(n<=2){
        if(*len==2)
            return 1;
        *len=2;
        return cache[0]=cache[1]=1;
    }else if(*len>=n)
        return cache[n-1];
    else{
        if(*len!=n-1)
            fib2(n-1,cache,len);
        *len=n;
        return cache[n-1]=cache[n-2]+cache[n-3];
    };
};
int main(){
    int n;
    int cache[MAX_INPUT];
    int len=0;
    scanf("%i",&n);
    if(!n||n>MAX_INPUT)
        return 0;
    printf("fib1(%i)==%i",n,fib1(n));
    printf(", %i run(s)\n",runs);
    runs=0;
    printf("fib2(%i)==%i",n,fib2(n,&cache,&len));
    printf(", %i run(s)\n",runs);
    main();
};

我为fib2使用了作用域变量,但这是全局变量可能有用的另一种情况(纯数学函数需要存储数据以避免永远占用数据)。

程序仅使用一次(例如用于比赛),或者需要缩短开发时间的程序

全局变量可用作类型常量,其中某个地方的函数需要* int而不是int。

如果打算使用该程序超过一天,通常会避免使用globals。

yingted answered 2020-06-29T20:48:35Z
1 votes
  • 何时不使用:全局变量很危险,因为要知道全局变量如何更改的唯一方法是在声明它们的.c文件中跟踪整个源代码(或者将所有.c文件外部声明为)。 好)。 如果您的代码有错误,则必须搜索整个源文件,以查看哪些函数可以更改它,以及何时更改。 出错时进行调试是一场噩梦。 我们经常理所当然地认为局部变量概念背后的独创性会优雅地超出范围-易于跟踪
  • 何时使用:当全局变量的使用没有被过度掩盖,并且使用局部变量的成本过于复杂以至于损害了可读性时,应使用全局变量。借此,我的意思是必须在函数参数和返回中添加一个附加参数,以及在其中传递指针。三个经典示例:当我使用弹出和推入堆栈时-这在函数之间共享。当然,我可以使用局部变量,但是随后我必须将指针作为附加参数传递给周围。第二个经典示例可以在K&R的“ C编程语言”中找到,它们定义了共享全局字符缓冲区数组的getch()和ungetch()函数。再一次,我们不需要将其设置为全局,但是当它很难弄乱缓冲区的使用时,增加的复杂性值得吗?第三个例子是您会在Arduino爱好者中的嵌入式空间中找到的东西。主循环函数中的许多函数都共享millis()函数,这是调用该函数的瞬时时间。由于时钟速度不是无限的,因此Millis()在单个循环内将有所不同。为了使其恒定,请在每个循环之前拍摄时间快照并将其保存在全局变量中。现在,时间快照将与许多功能访问时的快照相同。
  • 替代方案:不多。 尽可能遵循本地范围,特别是在项目开始时,而不是相反。 随着项目的发展,如果您觉得可以使用全局变量降低复杂性,则可以这样做,但前提是要满足第二点的要求。 并且请记住,与不负责任地使用全局变量相比,使用局部范围和具有更复杂的代码要少得多。
Dean P answered 2020-06-29T20:49:08Z
0 votes

我在这里的“从不”营地。 如果需要全局变量,请至少使用单例模式。 这样,您可以获得延迟实例化的好处,并且不会弄乱全局名称空间。

Nik Reiman answered 2020-06-29T20:49:28Z
0 votes

全局常数很有用-与预处理器宏相比,类型安全性更高,并且如果需要的话,更改值仍然很容易。

全局变量具有某些用途,例如,如果程序的许多部分的操作取决于状态机中的特定状态,则它们具有某些用途。 只要您限制可以修改变量以跟踪涉及它的bug的位置的数目还不错。

几乎在创建多个线程后,全局变量就变得很危险。 在那种情况下,您实际上应该将范围限制为(最多)文件全局变量(通过声明为静态)和保护它免受危险的多重访问的getter / setter方法。

Adam Hawes answered 2020-06-29T20:49:58Z
translate from https://stackoverflow.com:/questions/176118/when-is-it-ok-to-use-a-global-variable-in-c