将指针传递给有和没有&的函数。

时间:2015-06-04 15:52:21

标签: c pointers malloc

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

例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 = 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);
}
例2
def deep_access(x,keylist):
     val = x
     for key in keylist:
         val = val[key]
     return val

s = 'key2.key21.key211'

print deep_access(x,s.split('.'))

5 个答案:

答案 0 :(得分:3)

规则非常简单。您想调用一个带有指向struct对象的指针的函数。如果已经指向要使用该功能的对象的指针,则不需要&。否则,你这样做。

我的意思是&#34;已经有一个指针&#34;?

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;

在这种情况下,您可以使用print_structp_thomas_mann来呼叫&thomas_mann,效果会相同。

了解 C没有通过引用传递非常重要。所有函数参数,无论其类型如何,都由 value 传递。 1 函数print_struct的参数按值传递。该值碰巧是结构对象的指针,因此它可以使用类似于如何在已通过引用传递的语言中使用reference-argument ,但它实际上并不是语言理论意义上的参考。 (它 a&#34;引用&#34;因为这个词在英语中是随便使用的。你的混乱是可以理解的,但你必须超越它才能熟练使用C语言。)

1 你可能会看到人们在谈论&#34;通过不可见的引用&#34;但除非您正在实现C编译器,或者必须在汇编级别与C调用约定进行互操作,否则您不必担心这一点,因为它是不可见的。

还有一个皱纹:

struct Author a_thomas_mann[1];

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

答案 1 :(得分:1)

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

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

答案 2 :(得分:1)

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

  

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

     

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

似乎俞昊忘记了一个你还没有提供的例子。

  

我可以在不使用&的情况下通过引用传递规则(或至少是经验法则)吗?

除了前面提到的,当一个数组用于表达式sizeof&地址以外的表达式时,它将被静默转换为指针。因此,请考虑此示例#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);
}

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

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

答案 3 :(得分:0)

要通过引用传递,您需要将指针传递给要传递的事物。在第一个示例中,您要传递struct Author。要传递指向它的指针,您需要其地址,因此您需要使用&运算符。在第二个示例中,您有一个struct Author*。这已经是你要传递的struct Author的指针,所以你可以直接传递指针。

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

答案 4 :(得分:0)

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

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

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