Why use double pointer? or Why use pointers to poi

2018-12-31 16:04发布

When should a double pointer be used in C? Can anyone explain with a example?

What I know is that a double pointer is a pointer to a pointer. Why would I need a pointer to a pointer?

标签: c pointers
19条回答
还给你的自由
2楼-- · 2018-12-31 16:37

Hopefully the following example will clear some concepts regarding pointers and double pointers , their differences and usage in common scenarios.

    int* setptr(int *x)
    {
        printf("%u\n",&x);
        x=malloc(sizeof(int));
        *x=1;
        return x;
    }

In the above function setptr we can manipulate x either
1. by taking fn arg as int *x , doing  malloc and setting value of x and return x 
Or
    2. By taking arg as int ** and malloc and then set  **x value to some value.
Note: we cant set any general pointer directly without doing  malloc.Pointer indicates that it is a type of variable which can hold address of any data type.Now either we define a variable and give reference to it or we declare a pointer(int *x=NULL) and allocate some memory to it inside the called function where we pass x or a reference to it .. In either case we need to have address of a memory in the  pointer and in the case pointer initially points  to NULL or it is defined like int *x where it points  to any random address then we need to assign a valid memory address to pointer 

    1. either we need to allocate memory to it by malloc

    int *x=NULL means its address is 0.
    Now we need to either o following
    1.



    void main()
        {
            int *x;
            x=malloc
            *x=some_val;
        }
        Or
        void main()
        {
            int *x
            Fn(x);
        }

        void Fn(int **x)
        {
            *x=malloc;
            **x=5;
        }
        OR
        int * Fn(int *x)
        {
            x=malloc();
            *x=4;
            Return x;
        }


        2. Or we need to point it to a valid memory like a defined variable inside the function where pointer is defined.


        OR
        int main()
        {
            int a;
            int *x=&a;
            Fn(x);
            printf("%d",*x);
        }
        void Fn(int *x)
        {
            *x=2;
        }


     in both cases value pointed by x is changed inside fn

    But suppose if we do like


    int main()
    {
        int *x=NULL;
        printf("%u\n",sizeof(x));
        printf("%u\n",&x);
        x=setptr(x);
        //*x=2;
        printf("%d\n",*x);
        return 0;
    }

/* output
4
1
*/

#include<stdio.h>
void setptr(int *x)
{
    printf("inside setptr\n");
    printf("x=%u\n",x);
    printf("&x=%u\n",&x);
    //x=malloc(sizeof(int));
    *x=1;
    //return x;
}
int main()
{
    int *x=NULL;
    printf("x=%u\n",x);
    printf("&x=%u\n",&x);
    int a;
    x=&a;
    printf("x=%u\n",x);
    printf("&a=%u\n",&a);
    printf("&x=%u\n",&x);
    setptr(x);
    printf("inside main again\n");

    //*x=2;
    printf("x=%u\n",x);
    printf("&x=%u\n",&x);
    printf("*x=%d\n",*x);
    printf("a=%d\n",a);
    return 0;
}
查看更多
弹指情弦暗扣
3楼-- · 2018-12-31 16:38

I saw a very good example today, from this blog post, as I summarize below.

Imagine you have a structure for nodes in a linked list, which probably is

typedef struct node
{
    struct node * next;
    ....
} node;

Now you want to implement a remove_if function, which accepts a removal criterion rm as one of the arguments and traverses the linked list: if an entry satisfies the criterion (something like rm(entry)==true), its node will be removed from the list. In the end, remove_if returns the head (which may be different from the original head) of the linked list.

You may write

for (node * prev = NULL, * curr = head; curr != NULL; )
{
    node * const next = curr->next;
    if (rm(curr))
    {
        if (prev)  // the node to be removed is not the head
            prev->next = next;
        else       // remove the head
            head = next;
        free(curr);
    }
    else
        prev = curr;
    curr = next;
}

as your for loop. The message is, without double pointers, you have to maintain a prev variable to re-organize the pointers, and handle the two different cases.

But with double pointers, you can actually write

// now head is a double pointer
for (node** curr = head; *curr; )
{
    node * entry = *curr;
    if (rm(entry))
    {
        *curr = entry->next;
        free(entry);
    }
    else
        curr = &entry->next;
}

You don't need a prev now because you can directly modify what prev->next pointed to.

To make things clearer, let's follow the code a little bit. During the removal:

  1. if entry == *head: it will be *head (==*curr) = *head->next -- head now points to the pointer of the new heading node. You do this by directly changing head's content to a new pointer.
  2. if entry != *head: similarly, *curr is what prev->next pointed to, and now points to entry->next.

No matter in which case, you can re-organize the pointers in a unified way with double pointers.

查看更多
无色无味的生活
4楼-- · 2018-12-31 16:43

For instance if you want random access to noncontiguous data.

p -> [p0, p1, p2, ...]  
p0 -> data1
p1 -> data2

-- in C

T ** p = (T **) malloc(sizeof(T*) * n);
p[0] = (T*) malloc(sizeof(T));
p[1] = (T*) malloc(sizeof(T));

You store a pointer p that points to an array of pointers. Each pointer points to a piece of data.

If sizeof(T) is big it may not be possible to allocate a contiguous block (ie using malloc) of sizeof(T) * n bytes.

查看更多
永恒的永恒
5楼-- · 2018-12-31 16:45

Simple example that you probably have seen many times before

int main(int argc, char **argv)

In the second parameter you have it: pointer to pointer to char.

Note that the pointer notation (char* c) and the array notation (char c[]) are interchangeable in function arguments. So you could also write char *argv[]. In other words char *argv[] and char **argv are interchangeable.

What the above represents is in fact an array of character sequences (the command line arguments that are given to a program at startup).

See also this answer for more details about the above function signature.

查看更多
冷夜・残月
6楼-- · 2018-12-31 16:45

application of double pointer as shown by Bhavuk Mathur seems to be wrong. Here following example is the valid one

void func(char **str)
{
     strcpy(str[0],"second");
}

int main(){

    char **str;
    str = (char **)malloc(sizeof(char*)*1); // allocate 1 char* or string
    str[0] = (char *)malloc(sizeof(char)*10);      // allocate 10 character
    strcpy(str[0],"first");            // assign the string
    printf("%s\n",*str);
    func(str);            
    printf("%s\n",*str);           
    free(str[0]); 
    free(str);
}
查看更多
只若初见
7楼-- · 2018-12-31 16:46

Pointers to pointers also come in handy as "handles" to memory where you want to pass around a "handle" between functions to re-locatable memory. That basically means that the function can change the memory that is being pointed to by the pointer inside the handle variable, and every function or object that is using the handle will properly point to the newly relocated (or allocated) memory. Libraries like to-do this with "opaque" data-types, that is data-types were you don't have to worry about what they're doing with the memory being pointed do, you simply pass around the "handle" between the functions of the library to perform some operations on that memory ... the library functions can be allocating and de-allocating the memory under-the-hood without you having to explicitly worry about the process of memory management or where the handle is pointing.

For instance:

#include <stdlib.h>

typedef unsigned char** handle_type;

//some data_structure that the library functions would work with
typedef struct 
{
    int data_a;
    int data_b;
    int data_c;
} LIB_OBJECT;

handle_type lib_create_handle()
{
    //initialize the handle with some memory that points to and array of 10 LIB_OBJECTs
    handle_type handle = malloc(sizeof(handle_type));
    *handle = malloc(sizeof(LIB_OBJECT) * 10);

    return handle;
}

void lib_func_a(handle_type handle) { /*does something with array of LIB_OBJECTs*/ }

void lib_func_b(handle_type handle)
{
    //does something that takes input LIB_OBJECTs and makes more of them, so has to
    //reallocate memory for the new objects that will be created

    //first re-allocate the memory somewhere else with more slots, but don't destroy the
    //currently allocated slots
    *handle = realloc(*handle, sizeof(LIB_OBJECT) * 20);

    //...do some operation on the new memory and return
}

void lib_func_c(handle_type handle) { /*does something else to array of LIB_OBJECTs*/ }

void lib_free_handle(handle_type handle) 
{
    free(*handle);
    free(handle); 
}


int main()
{
    //create a "handle" to some memory that the library functions can use
    handle_type my_handle = lib_create_handle();

    //do something with that memory
    lib_func_a(my_handle);

    //do something else with the handle that will make it point somewhere else
    //but that's invisible to us from the standpoint of the calling the function and
    //working with the handle
    lib_func_b(my_handle); 

    //do something with new memory chunk, but you don't have to think about the fact
    //that the memory has moved under the hood ... it's still pointed to by the "handle"
    lib_func_c(my_handle);

    //deallocate the handle
    lib_free_handle(my_handle);

    return 0;
}

Hope this helps,

Jason

查看更多
登录 后发表回答