【问题标题】:Passing pointers to functions with and without &将指针传递给带和不带 & 的函数
【发布时间】:2015-06-04 15:52:21
【问题描述】:

我试图了解在通过引用传递参数时何时需要使用地址运算符&(对于介意我不精确的读者,请将其阅读为模拟传递引用 ) 到函数而不修改函数本身。我将使用structs 给出两个小例子。在两者中struct 都是通过引用传递的,但其中一个涉及& 的使用,而另一个则不涉及。在这种特殊情况下的解释可能会涉及到第二个示例中malloc() 的使用,我可以猜到,但我想要一个更有经验的意见。此外,我的问题更笼统:当我可以通过引用而不使用& 时,是否有规则(或至少是经验法则)?

示例 1
#include <stdio.h>
#include <stdlib.h>

struct Author {
        char *Name;
        int Born;
        int Died;
        char *Notable_Works;
};

void print_struct(struct Author *thomas_mann);

int main()
{
        struct Author thomas_mann;
        thomas_mann.Name = "Thomas Mann";
        thomas_mann.Born = 1875;
        thomas_mann.Died = 1955;
        thomas_mann.Notable_Works = "Der Zauberberg";

        print_struct(&thomas_mann);

        return EXIT_SUCCESS;
}

void print_struct(struct Author *thomas_mann)
{
        printf("%s was born in %d and died in %d.\n",
                        thomas_mann->Name, thomas_mann->Born, thomas_mann->Died);
        printf("His most notable work includes ‘%s’.\n",
                        thomas_mann->Notable_Works);
}
示例 2
#include <stdio.h>
#include <stdlib.h>

struct Author {
        char *Name;
        int Born;
        int Died;
        char *Notable_Works;
};

void print_struct(struct Author *thomas_mann);

int main()
{
        struct Author *thomas_mann = malloc(sizeof(struct Author));
        if (!thomas_mann) {
                fprintf(stderr, "memory allocation failed");
                exit(EXIT_FAILURE);
        }
        thomas_mann->Name = "Thomas Mann";
        thomas_mann->Born = 1875;
        thomas_mann->Died = 1955;
        thomas_mann->Notable_Works = "Der Zauberberg";

        print_struct(thomas_mann);

        free(thomas_mann);

        return EXIT_SUCCESS;
}

void print_struct(struct Author *thomas_mann)
{
        printf("%s was born in %d and died in %d.\n",
               thomas_mann->Name, thomas_mann->Born, thomas_mann->Died);
        printf("His most notable work includes ‘%s’.\n",
               thomas_mann->Notable_Works);
}

【问题讨论】:

  • 您没有“选择”是否通过x&amp;x(也没有重击规则)。它取决于函数所期望的 type。在您的两个示例中,这是传递参数的 only 正确方法(无需修改函数或更改调用者中的类型)。

标签: c pointers malloc


【解决方案1】:

规则真的很简单。您想调用一个函数,该函数接受一个指向 struct 对象的指针。如果您已经有一个指针指向要在其上使用该函数的对象,则不需要&amp;。否则,你会这样做。

“已经有指针”是什么意思?

struct Author *thomas_mann;

在这种情况下,您已经有了一个指针,因为变量thomas_mann 的类型为struct Author *,它是一个指针。我已经删除了初始化,因为你用什么来初始化它并不重要;它已经是一个指针,因为它的类型。

struct Author thomas_mann;

在这种情况下,您还没有指针。变量thomas_mann 的类型为struct Author,它不是指针类型。

struct Author thomas_mann;
struct Author *p_thomas_mann = &thomas_mann;

在这种情况下,您可以使用p_thomas_mann&amp;thomas_mann 调用print_struct,效果是一样的。


了解C 没有按引用传递 很重要。所有函数参数,无论其类型如何,都按传递。1函数print_struct的参数按值传递。该值恰好是一个指向结构对象的指针,因此它可以使用类似于在通过引用传递的语言中使用引用参数的方式,但它实际上并不是语言理论意义上的参考。 (不过,它一个“参考”,因为这个词在英语中被随意使用。你的困惑是可以理解的,但你必须超越它才能流利地使用 C。)

1 您可能会看到人们在谈论“通过不可见引用传递”,但除非您正在实现 C 编译器,或者必须在汇编级别与 C 调用约定互操作的东西,否则您会这样做不用担心这个,因为它是不可见的。


还有一个额外的皱纹:

struct Author a_thomas_mann[1];

变量的类型为“struct Author 数组”。因为它有一个数组类型,所以a_thomas_mann 在大多数(但不是全部)上下文中将被视为&amp;a_thomas_mann[0] 的语法糖。这称为类型衰减,它可能会误导人们认为C 确实有通过引用传递。再一次,它没有。它有一些仅与数组有关的语法糖。

【讨论】:

    【解决方案2】:

    首先,在 C 中,所有参数都按值传递,尽管您可以使用指针来模拟 按引用传递,就像 print_struct 在您的示例中所做的那样。

    是否使用&amp; 取决于类型。函数print_struct 需要一个指向struct Author 的指针作为它的参数。在第一个示例中,thomas_mann 的类型为struct Author,因此您需要&amp;thomas_mann,它是指向struct Author 的指针。在第二个示例中,thomas_mann 的类型为 struct Author *,因此您不需要 &amp; 运算符。

    【讨论】:

      【解决方案3】:

      以下引用来自Yu Haos answer...我不相信我可以更好地解决这方面的问题。

      首先,在 C 中,所有参数都按值传递,尽管您可以使用指针来模拟 按引用传递,就像 print_struct 在您的示例中所做的那样。

      是否使用&amp; 取决于类型。函数print_struct 需要一个指向struct Author 的指针作为它的参数。在第一个示例中,thomas_mann 的类型为struct Author,因此您需要&amp;thomas_mann,它是指向struct Author 的指针。在第二个示例中,thomas_mann 的类型为 struct Author *,因此您不需要 &amp; 运算符。

      好像于浩忘记了一个你没有提供的例子。

      当我可以通过引用而不使用&amp; 时,是否有规则(或至少是经验法则)?

      除上述之外,当数组用于sizeof&amp;address-of 表达式以外的表达式时,它将被静默转换为指针。因此,考虑这个例子#3:

      #include <stdio.h>
      #include <stdlib.h>
      
      struct Author {
              char *Name;
              int Born;
              int Died;
              char *Notable_Works;
      };
      
      void print_struct(struct Author *thomas_mann);
      
      int main()
      {
              struct Author thomas_mann[] = { { .Name = "Thomas Mann"
                                              , .Born = 1875
                                              , .Died = 1955;
                                              , .Notable_Works = "Der Zauberberg" } };
      
              print_struct(thomas_mann);
              return EXIT_SUCCESS;
      }
      
      void print_struct(struct Author *thomas_mann)
      {
              printf("%s was born in %d and died in %d.\n",
                     thomas_mann->Name, thomas_mann->Born, thomas_mann->Died);
              printf("His most notable work includes ‘%s’.\n",
                     thomas_mann->Notable_Works);
      }
      

      ...或者见鬼,如果你想进一步压缩它,你可以从main 中删除thomas_mann,然后像这样调用print_struct

      print_struct((struct Author[]){ { .Name = "Thomas Mann"
                                      , .Born = 1875
                                      , .Died = 1955;
                                      , .Notable_Works = "Der Zauberberg" } });
      

      【讨论】:

        【解决方案4】:

        要通过引用传递,你需要传递一个指向你想要传递的东西的指针。在您的第一个示例中,您有一个要传递的struct Author。要传递一个指向它的指针,您需要它的地址,因此您需要使用&amp; 运算符。在您的第二个示例中,您有一个struct Author*。这已经是一个指向您要传递的struct Author 的指针,因此您可以直接传递指针本身。

        这两种方法的主要区别在于您是在堆栈上分配还是在堆上分配。在 #1 中,您在堆栈上分配了 struct Author,但在 #2 中,您在堆上分配了它。

        【讨论】:

          【解决方案5】:

          &amp; 运算符不是 C 中的 pass-by-reference 运算符,仅在 C++ 中。 在示例 1 中,您将 Author 结构的实例的地址传递给函数 print_struct

          在示例 2 中,您直接将指向 Author 结构实例的指针传递给函数 print_struct

          在这种情况下,两个示例的作用完全相同,但示例 2 要求程序员更加警惕,因为使用 malloc 进行堆分配而不是堆栈分配。如果您不打算使用超出函数范围的对象,我建议您使用示例 1,因为对象超出范围后将被清理。示例 2 并非如此,如果不注意,可能会泄漏到内存泄漏。

          【讨论】:

          • 即使在 C++ 中,我也不会将 & 称为传递引用运算符。还有地址操作符。只是在声明和定义的类型部分,& 用于指示(在与 * for 指针相同的位置)它是一个引用。
          猜你喜欢
          • 2018-08-25
          • 2020-08-06
          • 2013-06-12
          • 2021-02-19
          • 2022-11-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多