使用和不使用 & 传递指向函数的指针

Passing pointers to functions with and without &

我试图了解在通过引用传递参数时何时需要使用地址运算符 &(对于介意我不精确的读者,请阅读此为 simulate pass-by -reference) 到函数而不修改函数本身。我将给出两个使用 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);
}

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

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

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

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

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

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

两个示例在这种情况下做的事情完全相同,但示例 2 要求程序员更加警惕,因为堆分配是 malloc 而不是堆栈分配。如果您不打算在创建函数的范围之外使用对象,我建议使用示例 1,因为该对象在超出范围后将被清理。示例2不是这样,一不留神可能会导致内存泄漏。

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

"already have a pointer" 是什么意思?

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&thomas_mann 调用 print_struct,效果是一样的。


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

1 你可能会看到人们谈论 "pass by invisible reference" 但除非你正在实现 C 编译器,或者必须在汇编级别与 C 调用进行互操作的东西约定,你不需要担心这个,因为它是不可见的。


还有一条皱纹:

struct Author a_thomas_mann[1];

这个变量的类型是"array of struct Author"。因为它具有数组类型,所以 a_thomas_mann 在大多数(但不是所有)上下文中将被视为 &a_thomas_mann[0] 的语法糖。这称为 type decay 并且它会误导人们认为 C 确实具有通过引用传递。同样,它没有。它有一点奇怪的语法糖,只与数组有关。

以下引述来自...我不相信我能在解决问题的这方面做得更好。

First, in C, all arguments are passed by value, although you could use a pointer to simulate pass by reference, as what print_struct does in your example.

Whether using & or not depends on the type. The function print_struct expects a pointer to struct Author as its argument. In the first example, thomas_mann is of type struct Author, so you need &thomas_mann which is a pointer to struct Author. In the second example, thomas_mann is of type struct Author *, so you don't need & operator.

看来于浩忘记说一个你没有提供的例子了。

Is there a rule (or a rule-of-thumb at least) when I can pass by reference without using &?

除上述情况外,当数组用于 sizeof&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" } });