什么时候可以在C中使用全局variables?
显然,这里有很多不同的意见,从“ 永远不要!总是封装(即使它只是一个macros)! ”到“ 没什么大不了的 – 当它比较方便时使用它们 ”。
所以。
具体的具体原因(最好举一个例子)
- 为什么全局variables是危险的
- 当应该使用全局variables代替替代品
- 对那些试图不恰当地使用全局variables的人有什么select?
虽然这是主观的,但我会select一个答案(对我来说,最好的代表每个开发者应该与全局的爱/恨关系),社区将投票他们到下面。
我相信新手有这样的参考是很重要的,但是如果有另外一个与你的答案基本类似的答案,请不要把它弄乱 – 增加评论或者编辑别人的答案。
-亚当
variables应该总是有可能的更小的范围。 这背后的理由是,每当你增加范围,你就有更多的代码可能修改variables,因此在解决scheme中引入更多的复杂性。
因此很明显,如果devise和实现自然允许,则避免使用全局variables是优选的。 由于这一点,我宁愿不使用全局variables,除非他们真的需要。
我不能同意“永不”的说法,就像其他概念一样,全局variables是在需要时应该使用的工具。 我宁愿使用全局variables,而不是使用一些人为的构造(如传递指针),这只会掩盖真正的意图。 使用全局variables的好例子是单例模式实现或embedded式系统中的寄存器访问。
关于如何实际检测全局variables的过度用法:检查,检查,检查。 每当我看到一个全局variables时,我不得不问自己:这是否真的需要在全球范围内?
你可以使全局variables工作的唯一方法是给他们的名字,以确保他们是独一无二的。
这个名字通常有一个前缀关联了一些“模块”或者全局variables特别关注或者有意义的函数集合。
这意味着variables“属于”这些function – 这是它们的一部分。 事实上,全球通常可以用一个与其他函数一起使用的小函数“包装” – 在相同的.h
文件中使用相同的名称前缀。
奖金。
当你这样做的时候,突然之间,它不再是真正的全球化了。 它现在是相关function模块的一部分。
这总是可以做到的。 稍微思考一下,以前的全局variables都可以分配给一些函数集合,分配给一个特定的.h
文件,并与函数隔离,这些函数允许您在不破坏任何内容的情况下更改variables。
而不是说“从不使用全局variables”,你可以说“将全局variables的责任分配给最有意义的某个模块”。
考虑这个公文:“如果范围足够窄,一切都是全球性的”。
在这个年代,还是很有可能需要编写一个非常快速的应用程序来做一次性工作。
在这种情况下,创build安全访问variables所需的能量大于通过debugging这种小型实用程序中的问题所节省的能源。
这是我能想到的唯一情况,那就是全局variables是明智的,而且这种情况比较less见。 有用的,新颖的程序如此之小以至于完全处于大脑的短时记忆之中,这种方法越来越less见,但它们依然存在。
事实上,我可以大胆地宣称,如果程序不是这么小,那么全局variables应该是非法的。
- 如果variables永远不会改变,那么它是一个常量,而不是一个variables。
- 如果variables需要通用访问,则应该有两个子例程来获取和设置它们,并且它们应该是同步的。
- 如果程序开始时间较短,并且稍后可能会较大,那么就像今天程序很大一样进行编码,并取消全局variables。 不是所有的程序都会增长! (虽然当然,这假定程序员有时愿意抛弃代码。)
C语言中的全局variables对于在多个方法需要variables(而不是将variables传递给每个方法)时使代码更具可读性很有用。 但是,它们是危险的,因为所有地点都有能力修改这个variables,因此很难追踪到错误。 如果您必须使用全局variables,则始终确保只通过一种方法修改它,并让所有其他调用者使用该方法。 这样可以更容易地debugging与variables相关的问题。
当您不担心线程安全的代码时 :只要有意义就使用它们,换句话说,只要将某事物expression为全局状态,就可以使用它们。
当你的代码可能是multithreading的 :不惜一切代价避免。 抽象的全局variables进入工作队列或其他线程安全的结构,或者如果绝对必要的话把它们封装在锁中,牢记这些可能是程序中的瓶颈。
直到我开始在国防工业工作,我才从“永不”营地来到这里。 有一些行业标准要求软件使用全局variables而不是dynamic的(C case中的malloc)内存。 我不得不重新思考我的一些项目的dynamic内存分配方法。 如果你可以用适当的信号量,线程等来保护“全局”内存,那么这对你的内存pipe理来说是一个可接受的方法。
您需要考虑在什么情况下也会使用全局variables。 将来你会想要这个代码复制。
例如,如果您在系统中使用套接字来访问资源。 在将来,如果答案是肯定的,我想远离全局,所以不需要大的重构。
这是一个像其他任何通常被滥用的工具,但我不认为它们是邪恶的。
例如,我有一个程序,就像在线数据库一样。 数据存储在内存中,但其他程序可以操纵它。 内部例程的行为与存储过程和数据库中的触发器非常相似。
这个程序有几百个全局variables,但是如果你想一想,什么是数据库,但是有大量的全局variables。
这个程序已经使用了大约十年,现在已经有很多版本,这个问题从来都不是问题,我会在一分钟之内再做一次。
我承认,在这种情况下,全局variables是具有用于改变对象状态的方法的对象。 因此,在debugging时跟踪谁更改了对象并不是问题,因为我总是可以在改变对象状态的例程上设置一个断点。 甚至更简单,我只要打开logging更改的内置日志logging。
当多个函数需要访问数据或写入一个对象时,应该使用全局variables。 例如,如果您必须传递数据或对多个函数的引用(如单个日志文件,连接池或需要在整个应用程序中访问的硬件引用)。 这可以防止非常长的函数声明和大量的重复数据分配。
除非绝对必要,否则通常不应该使用全局variables,因为全局variables只有在明确告知这样做或程序结束时才被清除。 如果您运行的是multithreading应用程序,则多个函数可以同时写入variables。 如果你有一个bug,跟踪这个错误可能会变得更加困难,因为你不知道哪个函数正在改变这个variables。 除非使用明确赋予全局variables唯一名称的命名约定,否则还会遇到命名冲突的问题。
当你声明常量时。
代码复杂性不是唯一的优化问题。 对于许多应用程序来说,性能优化具有更高的优先级。 但更重要的是,在许多情况下,使用全局variables可以大大降低代码的复杂性。 全球variables不仅是一个可以接受的解决scheme,而且还是更受欢迎的。 我最喜欢的专业示例是它们用于在应用程序的主线程与在实时线程中运行的audiocallback函数之间提供通信。
在multithreading应用程序中提出全局variables是一个负债,这是误导性的,因为任何variables(不pipe范围如何)都是潜在的责任,如果它暴露在多个线程上。
谨慎使用全局variables。 应尽可能使用数据结构来组织和隔离全局名称空间的使用。
variables的作用域程序员非常有用的保护 – 但它可以有一个成本。 我今天晚上写了关于全局variables的东西,因为我是一个经验丰富的Objective-C程序员,他经常在数据访问方面面临着面向对象的障碍。 我认为,反全球狂热主要来自年轻的,理论化的程序员,他们主要面对面向对象的API,而没有系统级API的深入,实际的经验以及他们在应用程序开发中的交互。 但是我不得不承认,当供应商使用命名空间时,我感到沮丧。 有几个Linux发行版全局预定义了“PI”和“TWOPI”,例如,这打破了我个人的大部分代码。
我可以想到几个原因:
debugging/testing目的(警告 – 没有testing过这个代码):
#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使用了scopedvariables,但是这是globals可能有用的一个场景(纯math函数需要存储数据以避免永远占用)。
程序只使用一次(比如比赛),或者需要缩短开发时间
全局variables是有用的types常量,其中函数某处需要* int而不是int。
如果我打算使用该程序超过一天,我通常会避免使用全局variables。
- 何时不使用:全局variables是危险的,因为唯一的办法是知道全局variables是如何变化的,就是追踪声明的.c文件中的整个源代码(或者,如果是.c文件,好)。 如果你的代码出错了,你必须search你的整个源文件来查看哪个函数改变了它,以及什么时候改变它。 debugging出错是一场噩梦。 我们经常认为,当地variables的概念优雅地超出了范围 – 这很容易追踪
- 何时使用:全局variables应该在其利用率没有被过度掩盖的情况下使用,并且使用局部variables的代价过于复杂以至于不利于可读性。 通过这个,我的意思是不得不添加一个额外的参数函数参数和返回和传递指针等等。 三个经典的例子:当我使用popup和推栈 – 这是function之间共享。 当然,我可以使用局部variables,但是我将不得不传递指针作为附加参数。 第二个经典例子可以在K&R的“The C Programming Language”中find,它们定义了共享全局字符缓冲区数组的getch()和ungetch()函数。 再说一次,我们不需要把它变成全局的,但是当它很难把缓冲区的使用搞糊涂的时候,增加的复杂性是值得的吗? 第三个例子是你可以在Arduino爱好者的embedded式空间中find的东西。 主循环函数中的许多函数都共享函数调用时的瞬时时间millis()函数。 由于时钟速度并不是无限的,所以millis()将在一个循环内有所不同 。 要使其保持不变,请在每个循环之前对时间进行快照,并将其保存在全局variables中。 时间快照现在将与许多函数访问时相同。
- 替代品:不多。 尽可能坚持本地范围,特别是在项目开始时,而不是相反。 随着项目的增长,如果你觉得使用全局variables可以降低复杂性,那就这样做,但前提是满足第二点的要求。 请记住,使用局部范围和更复杂的代码是不负责任地使用全局variables相比,更小的恶意。
我在这里的“永不”营 如果你需要一个全局variables,至less要使用一个单例模式 。 这样,您就可以享受到懒惰实例化的好处,而且不会混淆全局名称空间。
全局常量是有用的 – 比预处理器macros可以获得更多的types安全性,如果您决定需要更改值,仍然可以轻松地进行更改。
全局variables有一些用途,例如,如果程序的许多部分的操作取决于状态机中的特定状态。 只要你限制了可以修改variables的位置数量,追踪涉及到的错误也不算太坏。
几乎一旦创build多个线程,全局variables就变得危险。 在这种情况下,您应该将范围限制为(最多)一个全局文件(通过声明为静态的)variables和getter / setter方法,以防止在可能有危险的情况下进行多路访问。