【问题标题】:Pointer to function vs global variable指向函数与全局变量的指针
【发布时间】:2012-06-04 19:35:37
【问题描述】:

这里的软件经验很少的新 EE。 在过去的几年里,在这个网站上阅读了很多问题,这将是我的第一个问题/帖子。 还没有完全找到这个问题的答案。

我想知道让函数修改主体内的全局变量(不将其作为参数传递)与传递变量地址之间的区别/动机。

以下是每个示例以使其更清楚。 假设我声明了一些函数“peripheral.c”(它们的原型在“peripheral.h”中,并在“implementation.c”中使用它们

方法一:

//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

方法二:

//peripheral.c

//macros, includes, etc

void function(void){
   //modify x
}

.

//implementation.c

#include "peripheral.h"

static uint8 x;

function();    //this will modify x

是避免使用“全局”变量的唯一动机吗? (另外,如果它只有文件范围,它真的是全局的吗?)

希望这个问题是有道理的。 谢谢

【问题讨论】:

  • 哇......这些答案很快就来了。他们都非常有帮助。谢谢!

标签: c function variables pointers global


【解决方案1】:

接收指向变量的参数的函数更通用。它可用于修改全局变量、局部变量或任何变量。修改全局的函数可以执行该任务并且只能执行该任务。

首选哪个完全取决于上下文。有时一种方法更好,有时另一种方法更好。不可能肯定地说一种方法总是比另一种更好。

至于你的全局变量是否真的是全局的,它是全局的,因为你的进程中只有一个该变量的实例。

【讨论】:

  • 哇......这些答案很快就来了。他们都非常有帮助。谢谢! (很遗憾,我只能评论一篇文章。)
【解决方案2】:

static 变量具有内部链接,无法在它们所在的 translation unit 之外访问它们。

因此,如果您想在另一个 TU 中修改 static 全局变量,则必须像第一个示例一样通过函数参数将其作为指针传递。

你的第二个例子不能工作,因为x不能在implementation.c之外访问,它应该给你一个编译错误。

好读:
What is external linkage and internal linkage?

【讨论】:

  • 如果我只需要在 implementationaion.c 中的 x (而不需要其他文件)怎么办?
【解决方案3】:

首先,在 C/C++ 中,“全局”确实意味着文件范围(尽管如果您在标头中声明全局,那么它会包含在 #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);
...

基本上,对于应该由它们所在的文件(或文件,如果您在标头中声明全局)中的所有内容共享的值使用全局变量。使用指针作为值的参数,这些值应该在一组较小的函数之间传递以进行共享,以及可能有多个变量您希望对其执行相同操作的情况。

编辑:另外,作为未来的说明,当您说“指向函数的指针”时,人们会假设您的意思是指向函数的指针,而不是将指针作为参数传递给函数. “作为参数的指针”对于您在这里的要求更有意义。

【讨论】:

  • 糟糕。你说得对。那是一种糟糕的表达方式。匆忙发布标题oO谢谢:)
【解决方案4】:

这里有几个不同的问题:

  1. 一般来说,“全局变量不好”。如果可以避免,请不要使用它们。是的,最好传递一个指向变量的指针以便函数可以修改它,而不是使其成为全局变量以便函数可以隐式修改它。

  2. 话虽如此,全局变量可能很有用:一定要酌情使用它们。

  3. 是的,“全局”可以表示“函数之间”(在一个模块内)以及“模块之间”(在整个程序中是全局的)。

  4. 关于您的代码有几件有趣的事情需要注意:

    a) 大多数变量是从“堆栈”分配的。当你在这样的函数之外声明一个变量时,它是从“块存储”中分配的——这个空间存在于程序的生命周期中。

    b) 当您将其声明为“静态”时,您将其对其他模块“隐藏”:该名称在模块外不可见。

    c) 如果您想要一个真正的全局变量,您将使用关键字“static”。你可能在头文件中声明它“extern uint8 var”(所以所有模块都有定义)。

【讨论】:

    【解决方案5】:

    我不确定您的第二个示例是否真的有效,因为您将 x 声明为静态(因此将其范围限制为文件),但除此之外,指针传递版本还有一些优点:

    • 它在分配和模块化方面为您提供了更大的灵活性。虽然您只能在文件中拥有一个全局变量的副本,但您可以拥有任意数量的指针,它们可以指向在许多不同位置创建的对象(静态数组、malloc、堆栈变量......)

    • 全局变量被强制到每个函数中,因此您必须始终意识到有人可能想要修改它们。另一方面,指针只能由您明确传递给它们的函数访问。

    • 除了最后一点之外,全局变量都使用相同的范围,并且它可能会因变量过多而变得混乱。另一方面,指针与普通变量一样具有词法作用域,并且它们的作用域受到更多限制。


    是的,如果您有一个小的、独立的文件,事情可能会变得有些模糊。如果您不打算实例化多个“对象”,那么有时静态全局变量(单个文件的本地变量)与指向结构的指针一样工作。

    【讨论】:

      【解决方案6】:

      全局变量的主要问题是它们在函数或模块之间促进了所谓的"tight coupling"。在您的第二个设计中,peripheral 模块知道并依赖于implementation 的设计,以至于如果您通过删除或重命名x 来更改implementation,您将中断 em> peripheral,即使没有触及任何代码。您还无法独立于implementation 模块重复使用peripheral

      同样,这种设计意味着peripheral 中的function 只能处理x 的单个实例,无论x 代表什么。

      理想情况下,函数及其调用者应仅通过参数、返回值和异常(在适当的情况下)进行通信。如果您需要在调用之间维护状态,请使用可写参数来存储该状态,而不是依赖全局。

      【讨论】:

      • 这很有意义。第二种方式似乎是一种更糟糕的方式——这很好地解释了原因。
      猜你喜欢
      • 2014-05-18
      • 2021-11-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多