新EE,这里的软件经验非常少。 在过去的几年里,在这个网站上看过很多问题,这将是我的第一个问题/帖子。 还没有找到答案。
我想知道函数修改体内的全局变量(不将其作为参数传递),以及传递变量地址之间的区别/动机。
以下是每个更清晰的示例。 假设我正在声明一些函数“peripheral.c”(在“peripheral.h”中使用它们的原型,并在“implementation.c”中使用它们
方法1:
//peripheral.c
//macros, includes, etc
void function(*x){
//modify x
}
//implementation.c
#include "peripheral.h"
static uint8 var;
function(&var); //this will end up modifying var
方法2:
//peripheral.c
//macros, includes, etc
void function(void){
//modify x
}
//implementation.c
#include "peripheral.h"
static uint8 x;
function(); //this will modify x
避免使用“全局”变量的唯一动机是什么? (另外,如果它只有文件范围,它真的是全局的吗?)
希望这个问题有道理。 感谢
答案 0 :(得分:5)
接收指向变量的参数的函数更通用。它可用于修改全局变量,本地变量或任何变量。修改全局的函数只能执行该任务和该任务。
哪个是首选完全取决于上下文。有时一种方法更好,有时另一种方法更好。不可能明确地说一种方法总是优于另一种方法。
至于你的全局变量是否真的是全局变量,它在整个流程中只有一个变量的实例就是全局变量。
答案 1 :(得分:2)
static
个变量具有内部链接,除了它们所在的 translation unit 之外,它们无法访问。
因此,如果要在另一个TU中修改static
全局变量,则必须将其作为指针传递给函数参数,如第一个示例所示。
您的第二个示例无法正常工作,因为x
无法在implementation.c
之外访问,它应该会给您一个编译错误。
答案 2 :(得分:1)
首先,在C / C ++中,“global”表示文件范围(尽管如果在头文件中声明全局,则它包含在#include该头文件的文件中)。
当调用函数具有被调用函数应修改的某些数据时,使用指针作为参数很有用,例如在您的示例中。作为参数的指针在修改其输入的函数不确切知道它正在修改的内容时特别有用。例如:
scanf("%d", &foo);
scanf不会对foo有任何了解,也无法修改其源代码以使其了解foo。但是,scanf接受指向变量的指针,这允许它修改任意变量的值(当然它支持的类型)。这使得它比依赖于全局变量的东西更具可重用性。
在您的代码中,您通常应该更喜欢使用指向变量的指针。但是,如果您注意到您将相同的信息块传递给许多函数,则全局变量可能有意义。也就是说,你应该更喜欢
int g_state;
int foo(int x, int y);
int bar(int x, int y);
void foobar(void);
...
到
int foo(int x, int y, int state);
int bar(int x, int y, int state);
void foobar(int state);
...
基本上,将globals用于应该由它们所在文件中的所有内容共享的值(或者如果在标头中声明全局,则使用文件)。使用指针作为值的参数,这些值应该在较小的一组函数之间传递以便共享,并且对于可能存在多个变量的情况,您希望对这些函数执行相同的操作。
编辑:另外,作为未来的注释,当你说“指向函数的指针”时,人们会认为你指的是指向函数的指针,而不是将指针作为参数传递给函数。 “指针作为参数”对你在这里要求的内容更有意义。答案 3 :(得分:0)
这里有几个不同的问题:
一般来说,“全局变量都很糟糕”。如果可以避免,请不要使用它们。是的,最好将指针传递给变量,以便函数可以修改它,而不是将其设置为全局,以便函数可以隐式修改它。
话虽如此,全局变量可以有用:通过各种方式使用它们。
是的,“全局”可以表示“功能之间”(模块内)以及“模块之间”(整个程序中的全局)。
您的代码有几个值得注意的事项:
a)大多数变量都是从“堆栈”中分配的。当你在这样的函数之外声明一个变量时,它是从“块存储”中分配的 - 该空间在程序的生命周期中存在。
b)当您将其声明为“静态”时,您将其“隐藏”在其他模块中:名称在模块外部不可见。
c)如果你想要一个真正的全局变量,你不使用关键字“static”。并且可能在头文件中声明它为“extern uint8 var”(因此所有模块都有定义)。
答案 4 :(得分:0)
我不确定你的第二个例子是否真的有效,因为你将x
声明为静态(因此将其范围限制为文件)但除此之外,指针传递版本有一些优点:< / p>
它为您提供了更多的分配和模块化灵活性。虽然在文件中只能只有一个全局变量的副本,但是你可以拥有任意数量的指针,它们可以指向在许多不同位置创建的对象(静态数组,malloc,堆栈变量......)
全局变量被强制进入每个函数,因此您必须始终意识到有人可能想要修改它们。另一方面,指针只能通过明确传递给它们的函数来访问。
除了最后一点之外,全局变量都使用相同的范围,并且可能会因为变量太多而变得混乱。另一方面,指针具有类似普通变量的词法范围,其范围受到更多限制。
是的,如果你有一个小的自包含文件,事情会变得有些模糊。如果您不打算实例化多个“对象”,那么有时静态全局变量(对于单个文件来说是本地的)就像指向结构的指针一样。
答案 5 :(得分:0)
全局变量的主要问题是它们在函数或模块之间提升了所谓的"tight coupling"。在您的第二个设计中,peripheral
模块了解并依赖于implementation
的设计,以至于如果您通过删除或重命名implementation
来更改x
,则'll 打破 peripheral
,即使不触及任何代码。您还无法独立于peripheral
模块重复使用implementation
。
同样,此设计意味着function
中的peripheral
只能处理x
的单个实例,无论x
代表什么。
理想情况下,函数及其调用者应仅通过参数,返回值和异常(在适当的情况下)进行通信。如果需要在调用之间保持状态,请使用可写参数来存储该状态,而不是依赖于全局。