在 C 中正确使用全局 const 变量?
我正在为我的计算机科学课程编写一个程序。它模拟了一家快递公司在机场的活动。
这是一个非常简单的小程序,由一些头文件和源文件以及一个用于编排模拟的 main.cpp 源文件组成。
有一些给定的常量值,例如货物到达的频率、飞机的负载能力、工人处理某些物品所花费的时间等(均为整数值)。我有必要在 main.cpp 中的多个函数中访问这些变量将
main() 函数上方的这些变量声明为 const ints 似乎是合理的,有效地使它们成为全局变量,例如const int kTotalTime = 2000;
const int kPlaneCapacity = 25;
int main(){//...program code}
我知道在大多数情况下应该避免使用全局变量,因为它们的调用和/或修改位置没有限制,这可能会导致意外破坏程序的某些部分,进而可能难以调试,并导致未来代码的兼容性问题等。 然而,由于这些是原始数据类型的只读值,在整个程序中使用,因此这似乎是一个合理的解决方案。此外,它还向任何阅读代码的人和编译器明确声明了变量的用途。
提问:我的逻辑有问题吗?为何如此?什么时候使用全局变量(const 或非 const)是合理的?如果这是一个糟糕的解决方案,那么您建议如何声明诸如此类的常量只读值?
非常感谢您抽出时间!
I am working on a program for my CS class. It is a simulation of a delivery company's activities at an airport.
This is a very simple, small program consisting of a few header and source files and a main.cpp source file that orchestrates the simulation.
There are certain given constant values, such as the frequency of shipment arrival, the load capacity of planes, the amount of time it takes a worker to process certain items, etc. (all are integer values). It is necessary for me to access these variables throughout several functions in main.cpp
It seemed reasonable to declare these above the main() function as const ints, effectively making them global, e.g.
const int kTotalTime = 2000;
const int kPlaneCapacity = 25;
int main(){//...program code}
I am aware that global variables are to be avoided in most situations, as there are no restrictions on where they can be called and/or modified, which can lead to accidentally breaking parts of the program which in turn may be difficult to debug, as well as causing compatibility issues for future code, etc.
However since these are read-only values of a primitive data type, which are used throughout the program, it seemed like a reasonable solution. Also, it makes an explicit statement about the purpose of the variables to anyone reading the code as well as to the compiler.
Questions: Is my logic flawed? How so? When are global variables (const or not) reasonable to use? If this is a bad solution, then how would you suggest declaring constant read-only values such as these?
Thank you very much for your time!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
关于您的程序的大小和目的(正如我从您的描述中理解的那样)可能并不重要,但由于它具有教育背景,我建议“做对”。
在这种情况下,我会选择一个
Config
结构(或类,如果你想让它更智能一点,请参见下文),它携带配置值并且可以在你的程序中使用。它的优点是,如果您必须从文件或命令行获取选项,您可以轻松更改它。至于类与结构的区别(请注意,我在这里进行逻辑上的区别,而不是技术上的区别)。要么将所有值作为成员放入结构中并传递它的 const 引用,要么将其设为一个完整的类,其中包含隐藏数据来源(以及数据生成方式)的访问器。编程始终是决策,这是您要做的决定。如果您认为将来必须允许更多配置可能性(如上所述),您可能需要进行类抽象。
另一种选择是将数据分散在程序中,这实际上比听起来要聪明得多。如果每个类只知道其配置选项(并隐藏它们),那么您实际上可以使用您正在使用的 OOP 语言。示例:
问题是如何初始化它。一种方法可能是创建一个如下所示的 config.cpp:
因此您可以隐藏信息,并且仍然将所有配置选项集中在一处(config.cpp >)。
编辑:
如果您有许多(多个)不同模块所需的配置选项,您可以进行一些复杂的操作(使用间接),如下所示:
config.cpp:
您甚至可以使选项成为非常量,如果您想要(但我没有看到配置选项中可变的点)。
Regarding the size and purpose of your program (as I understand it from your description) it probably doesn't matter, but since it has an educational context, I'd suggest to "do it right".
In such a situation I would go for a
Config
struct (or class, if you want to make it a bit smarter, see below) which carries the configuration values and can be tossed around your program. It has the advantage that you can easily change it if you have to, say, fetch your options from a file or from the command line.As for the class versus struct thingy (note that I am making a logical distinction here, not a technical). Either you just put all values as members in your struct and pass around const refs of it, or you make it a full fledged class with accessors that hide where the data is coming from (and how it is generated). Programming is always decision making and this is your decision to make. If you think you will have to allow more configuration possibilities in the future (like mentioned above) you may want to go for class abstraction.
Yet another option is to scatter your data across your program, which is actually a lot smarter than it sounds. If every class knows only its configuration options (and hides them) you can actually make use of the OOP language, you're using. Example:
The question is, how to initialise this. One way could be to create a
config.cpp
that looks like this:So you have information hiding, and you still have all the config options together at one place (
config.cpp
).Edit:
If you have config option that is required by many (more than one) different modules, you can go for a bit of sophistication (with indirection) like so:
config.cpp:
You can even make the options non-const if you want (but I don't see the point in a configuration option that is mutable).
我认为最好将常量放在类中静态。
我假设您有 Plane 类,只需执行以下操作:
Plane.h
Plane.cpp
另外,请注意您对常量的理解。 Pi 是一个常数。 10 是常数。我确实明白你会如何认为飞机载客量是恒定的,但想一想:如果你的老师说你的下一次作业,你的飞机载客量应该是 30 架,而不是 25 架,该怎么办?
I think it's best to put your constants as static inside the class.
I assume you have the class
Plane
, just do this:Plane.h
Plane.cpp
Also, take good care of what you understand by constant. Pi is a constant. 10 is a constant. I do see how you would think a plane capacity is constant, but think about this: What if your teacher says that for your next assignment, your plane capacity should be 30, and not 25.
什么时候使用全局变量(const 或非 const)是合理的?
如果您的程序是一个多线程程序,那么您应该认真考虑使用全局变量,因为它们需要适当的同步以避免竞争条件。通常,正确的同步并不是一项非常琐碎的任务,需要一些认真的理解和思考。
以下是好文章的摘录:
考虑到上述所有内容,只要您了解这些陷阱并了解您使用全局变量的方式确实可以使您的程序免受这些陷阱的影响,那么您就可以继续并很好地使用全局变量。
When are global variables (const or not) reasonable to use?
If your program is a multithreaded program then you should be giving a serious thought about using globals for they would need proper synchronization to avoid race conditions. Usually proper synchronization is not a very trivial task and requires some serious undrestanding and thought.
Here is an excerpt from a nice article:
Given all the above, As long as you understand the pitfalls and understand that the way you are using the globals does insulate your program against those pitfalls, then you can go ahead and very well use globals.
Arduino 代码是何时适合使用不同类型的全局变量的示例。
每个 Arduino 草图都包含一个在启动时运行一次的
setup()
函数,以及一个重复运行的loop()
函数。setup()
中声明的任何内容在loop()
中都不可用。因此实例化的类(例如MqttClient)需要是全局的。同样,在
loop()
中声明的任何内容都会在loop()
的下一次迭代中重新声明。有些传感器需要几毫秒的时间来轮询。轮询某些传感器会导致它们变热。因此,Arduino 草图通常会在不同时间执行多个操作。使用温度传感器的示例:您可以每 4 秒轮询一次并将其值打印到终端,每 10 秒将值打印到 LCD 屏幕,每 20 秒将值发布到 MQTT 代理。
在
loop()
中使用delay()
是一种不好的形式,因为这是一个阻塞函数,会阻止 CPU 执行任何其他操作。因此,改为使用计时器:要在不使用非常量全局变量的情况下存储我的
lastPollTime
,我需要使用一个类来跟踪实例变量中的上次运行时间。对于只需要两行代码的情况来说,这是很大的开销,因此我的代码对于我需要使用的每个唯一计时器都有一个全局unsigned long
。由于我可能不会在
loop()
的另外一千次迭代中使用 tempC 变量,因此它也需要是一个非常量全局变量。1在写这个答案时发现了一个:https://github.com/contrem/arduino-timer
Arduino code is an example of when it is appropriate to use different types of global variables.
Every Arduino sketch contains a
setup()
function which is run once at startup, and aloop()
function which is run repeatedly. Anything declared insetup()
is unavailable inloop()
. So instantiated classes (e.g. MqttClient) need to be global.Similarly, anything declared in
loop()
is redeclared in the next iteration ofloop()
. Some sensors take a few milliseconds to poll. Polling some sensors causes them to heat up. Because of all that, Arduino sketches will often have multiple actions being performed at different times.An example using a temperature sensor: you may poll it and print its value to the terminal every 4 seconds, print the value to a LCD screen every 10 seconds, and publish the value to a MQTT broker every 20 seconds.
It is bad form to use
delay()
inloop()
because that is a blocking function which prevent the CPU from doing anything else. So timers are used instead:To store my
lastPollTime
without using a non-const global variable, I would need to use a class¹ which keeps track of the last-run time in an instance variable. That's a lot of overhead for what otherwise takes only two lines of code, so my code has one globalunsigned long
for each unique timer I need to use.And since I may not use the
tempC
variable for another thousand iterations ofloop()
, it too needs to be a non-const global variable.¹Found one while writing this answer: https://github.com/contrem/arduino-timer