传递参数如* mode1或** mode2之间的区别

时间:2014-01-17 00:30:27

标签: c function pointers malloc realloc

我不太清楚传递给函数*mode1**mode2之间的区别。

我写了一些例子。 (注意:type可以是任何类型)

[代码1]

#include <stdio.h>

void function (type *vet)
{
    /* other */
}

int main ()
{
    type *vet;

    function (vet)

    /* other */

    return 0;
}

[代码2]

#include <stdio.h>

void function (type **vet)
{
    /* other */
}

int main ()
{
    type *vet;

    function (&vet)

    /* other */

    return 0;
}

我知道:第一种情况是指针,第二种情况是指针指针。但是为什么例如在第二种情况下,如果我通过&vet我可以在function()中分配内存并在main()中释放它而在第一种情况下不释放它吗?

我搜索能够很好地解释差异的人。在这两种情况下我该怎么办? malloc或realloc的方式和位置?免费?并修改函数中的兽医?

5 个答案:

答案 0 :(得分:4)

原始问题

主要(最重要的)区别在于是否可以更改调用函数中的值。

  1. 在第一个例子中,被调用函数在调用代码中获取指针的副本,并且不能修改调用代码中的指针。
  2. 在第二个示例中,被调用函数获取指向指针的指针,通过在被调用函数中分配*vet,可以修改被调用函数中的值。
  3.   

    为什么在第二种情况下,如果我通过&vet我可以在function()中分配内存并在main()和第一种中释放它吗?

    在第二种情况下,function()中的代码可以修改main()中的实际指针,因此vetmain()的值最终会带有分配的指针值因此可以被释放。在第一种情况下,main()中的值不会被被调用的函数修改,因此main()无法释放数据。

      

    malloc或realloc的方式和位置?免费?

    在第一种情况下,您可以在函数中使用malloc()realloc(),但在返回之前还应释放已分配的内存,除非您的代码将值存储在全局变量中(在这种情况下)你可以委托一些其他代码来处理free(),但最好还是要清楚哪些代码有责任,而且无论如何使用全局变量可能都不是一个好主意。或者,除非您更改函数签名并返回指向应由调用代码释放的已分配数据的指针。

    在第二种情况下,您可以在被调用函数中分配或重新分配内存,并将其分配给其他函数使用,并由调用函数释放。

      

    并修改功能中的兽医?

    在这两个函数中,您可以根据需要修改本地vet变量;任何函数的任何参数都是如此。您不一定要做的是修改调用函数中的值;你必须在调用函数中有一个指向值的指针才能做到这一点。在第一个函数中,您无法更改vetmain()的值;在第二,你可以。在这两个函数中,您可以更改vet指向的内容。 (一个小问题是三个上下文中名称vet的混合 - main()和两个不同的函数。两个函数中的名称vet指向不同的类型事情。


    扩展问题

      

    但它是否在function()中被释放,例如,这个?

    #include <stdio.h>
    #define NUM (10)
    
    struct example
    {
        /* ... */
    }
    
    void dealloc (struct example *pointer)
    {
        free (pointer);
    }
    
    int main()
    {
        struct example *e;
        e = malloc (NUM * sizeof(struct example));
        if (e == NULL)
            return -1;
        struct example *e_copy = e;
        dealloc (e_copy);
        return 0;
    }
    

    此代码是合法的。将指针值传递给dealloc()函数的副本(副本);它将指针传递给free(),释放分配的内存。在dealloc()返回后,ee_copy中的指针值与之前相同,但它们不再指向已分配的内存,并且对值的任何使用都会导致未定义的行为。可以为它们分配新值;旧值不能可靠地解除引用。

      

    与此有什么不同?

    #include <stdio.h>
    #define NUM (10)
    
    struct example
    {
        /* ... */
    }
    
    int main()
    {
        struct example *e;
        e = malloc (NUM * sizeof(struct example));
        if (e == NULL)
            return -1;
        struct example *e_copy = e;
        free (e_copy);
        return 0;
    }
    

    此示例与上一个示例之间的区别在于您直接在free()中调用main(),而不是从函数dealloc()调用。{/ p>

    有什么区别是:

    void dealloc(struct example **eptr)
    {
        free(*eptr);
        *eptr = 0;
    }
    
    int main()
    {
        ...
        dealloc(&e_copy);
        return 0;
    }
    

    在这种情况下,dealloc()返回后,e_copy是空指针。你可以再次将它传递给free()因为释放空指针是一个无操作。释放非空指针两次是未定义的行为 - 它通常会导致问题,应该不惜一切代价避免。请注意,即使是现在,e也包含malloc()最初返回的指针;但是对指针值的任何使用都会导致再次出现未定义的行为(但设置e = 0;e = NULL;e = e_copy;是正常的,并且使用e = malloc(sizeof(struct example));等也可以。

答案 1 :(得分:3)

正如您所说,第一种情况中的参数是a pointer,并且传递了指针vet的副本。我们可以修改vet指向的值(例如*vet = new value)。但我们无法修改指针vet的值,因为它只是原始vet指针的副本。因此,在第一个函数之后,*vet的值可能会更改,但vet的值不会更改。

那么我们怎么能修改指针vet的值呢?我们使用pointer to pointer。在第二个函数中,我们可以为*vet分配内存,这个修改后的值将保留在第二个函数之后。所以我们可以在main中释放它。

我们不能在第一种情况下执行此操作,因为如果我们尝试在function中分配内存,我们只需为指针vet的副本分配内存,而不是原始vet。< / p>

答案 2 :(得分:2)

您的理解是正确的,type *var;是指向type数据的指针,而type **var;是指向type数据指针的指针。

您所询问的差异,在函数中分配内存并跟踪它,是因为能够将值赋给指针。

在C中,只要你想修改一个函数中的值,就必须为它提供一个指针来修改它要修改的数据。

如果你想分配内存,你必须知道它的用途才能使用它,以后再释放它。如果只传递一个指向函数的指针,并为其分配内存,它就不能改变它传递的指针的值(相反,当你的程序返回到调用这个分配函数的函数时,堆栈将卸载该地址你需要);它只能读取它(在这种用途中它是毫无意义的)。

答案 3 :(得分:2)

考虑一下,指针变量

type *vet;

是在函数堆栈上创建的

main()

当&#34; function_1()&#34;从main调用,创建了这个新函数的堆栈。传递给此函数的任何参数都保存在此函数的堆栈中。在这种情况下,参数是指针变量。现在function_1()可以很好地改变这个指针变量的值,但是一旦函数返回,该函数的堆栈就被释放,任何更改都会丢失。

但是当你传递一个指向指针的指针时,你传递的实际上是指针变量的地址而不是指针变量。因此,当您在被调用函数内部处理此指针变量时,您实际上正在处理调用函数堆栈的内存。并且由于该内存位于调用函数的堆栈中,因此即使在释放被调用函数的堆栈之后,调用函数所做的任何更改都将保持不变。

答案 4 :(得分:1)

首先,C中没有垃圾收集器。因此,如果您没有明确地释放已分配的内存块,它将占用内存,直到进程退出。这是内存泄漏的强大来源。

因此,一旦您使用malloc或类似功能分配了内存块, 必须 保留指向它的指针,以便有一天释放它

如果你在一个函数中分配一个块并且你计划这个块在函数终止后仍然可以使用,你必须将它的值传递给一些更高级别的代码,这些代码最终会释放它,很久之后创建它的函数已经退出

为此,您有三个基本选择:

  1. 将指针存储在某个全局变量
  2. 将指针作为函数的结果返回
  3. 有一个函数参数指定指针存储的位置
  4. 案例1

    void * global_address_of_buffer;
    void alloc_a_buffer (int size)
    {
        global_address_of_buffer = malloc (size); // block reference in global var
    }
    
    alloc_a_buffer ();
    // ...  
    free (global_address_of_buffer);
    

    这显然是不切实际的。如果你两次调用你的函数,你将失去第一个缓冲区的地址 使用全局变量的无数插图之一会让你尖叫到地狱。

    然而,这是一种可能性。

    案例2

    void * alloc_a_buffer (int size)
    {
        return malloc (size); // block reference as return value
    }
    
    void * new_buffer;
    new_buffer = alloc_a_buffer (10); // retrieve pointer through return value
    // ...
    free (new_buffer);
    

    这并不总是可行的。例如,您可能希望所有函数都返回指示成功或失败的状态,在这种情况下,返回值将不适用于您的指针。

    <3>案例3
    void alloc_a_buffer (int size, void ** buffer)
    {
        *buffer = malloc (size); // block reference set through 2nd parameter
    }
    
    void * new_buffer;
    alloc_a_buffer (10, &new_buffer); // pass pointer address to the function
    // ...
    free (new_buffer);