355 votes

Pourquoi utiliser le double pointeur? ou Pourquoi utiliser des pointeurs sur des pointeurs?

Quand un double pointeur être utilisé en C? Quelqu'un peut-il expliquer avec un exemple?

Ce que je sais, c'est qu'un double pointeur est un pointeur vers un pointeur. Pourquoi aurais-je besoin d'un pointeur vers un pointeur?

574voto

pmg Points 52636

Si vous voulez avoir une liste de caractères (un mot), vous pouvez utiliser char *word

Si vous voulez une liste de mots (une phrase), vous pouvez utiliser char **sentence

Si vous voulez une liste de phrases (un monologue), vous pouvez utiliser char ***monologue

Si vous voulez une liste de monologues (une biographie), vous pouvez utiliser char ****biography

Si vous voulez une liste de biographies (une bio-bibliothèque), vous pouvez utiliser char *****biolibrary

Si vous voulez une liste de bio-bibliothèques (un ??lol), vous pouvez utiliser char ******lol

... ...

oui, je sais que ces peut-être pas le meilleur des structures de données

218voto

Asha Points 5976

Une des raisons est que vous souhaitez modifier la valeur du pointeur est passé à une fonction comme argument de la fonction, pour ce faire, vous avez besoin de pointeur vers un pointeur.

En termes simples, l'Utilisation ** lorsque vous souhaitez conserver (OU de conserver changement dans) l'Allocation de Mémoire ou de la Cession, même en dehors d'un appel de fonction. (Donc, Passer une telle fonction avec double pointeur arg.)

Cela peut ne pas être un très bon exemple, mais va vous montrer l'utilisation de base:

void allocate(int** p)
{
  *p = (int*)malloc(sizeof(int));
}

int main()
{
  int* p = NULL;
  allocate(&p);
  *p = 42;
  free(p);
}

26voto

ziyuang Points 1025

J'ai vu un très bon exemple, aujourd'hui, à partir de ce blog, que je résume ci-dessous.

Imaginez que vous avez une structure de nœuds dans une liste, ce qui est probablement

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

Maintenant, vous voulez mettre en œuvre un remove_if de la fonction, qui accepte la suppression du critère rm comme l'un des arguments et parcourt la liste liée: si une entrée satisfait au critère (quelque chose comme rm(entry)==true), son nœud sera retiré de la liste. En fin de compte, remove_if retourne la tête (qui peut être différent de l'original de la tête) de la liste chaînée.

Vous pouvez écrire

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;
}

que votre for boucle. Le message est sans doubles pointeurs, vous devez maintenir un prev variable pour ré-organiser les pointeurs, et de gérer les deux cas différents.

Mais avec le double de pointeurs, vous pouvez réellement écrire

// 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;
}

Vous n'avez pas besoin d'un prev maintenant, parce que vous pouvez modifier directement ce qu' prev->next a souligné.

Pour rendre les choses plus claires, nous allons suivre un peu le code. Lors de la suppression:

  1. si entry == *head: il sera *head (==*curr) = *head->next -- head pointe maintenant vers le pointeur de la nouvelle tête de nœud. Vous faites cela en modifiant directement heads'contenu d'un nouveau pointeur.
  2. si entry != *head: de même, *curr est-ce que prev->next a souligné, et maintenant les points de entry->next.

Peu importe dans quel cas, vous pouvez ré-organiser les pointeurs de manière unifiée avec double pointeurs.

19voto

Andrey Points 36869

Lorsque vous avez besoin de modifier la valeur d'un pointeur!

Exemple évident:

void MoveToNextElement(int** i)
{
   (*i)++;
}

19voto

Jason Points 20479

Les pointeurs de pointeurs également venir dans maniable comme des "poignées" à la mémoire où vous voulez passer autour d'une "poignée" entre les fonctions de re-localisable mémoire. Que, fondamentalement signifie que la fonction peut modifier la mémoire pointé par le pointeur à l'intérieur de la poignée variable, et chaque fonction ou l'objet à l'aide de la poignée va point à la récemment déplacé (ou attribués) de la mémoire. Les bibliothèques comme à ce faire avec "opaque" types de données, c'est-types de données ont été vous n'avez pas à vous soucier de ce qu'ils font avec la mémoire de relever le faire, il vous suffit de passer autour de la "poignée" entre les fonctions de la bibliothèque pour effectuer certaines opérations sur la mémoire ... la bibliothèque de fonctions d'allocation et de réallocation de la mémoire sous le capot sans avoir explicitement de s'inquiéter au sujet du processus de gestion de la mémoire ou lorsque la poignée est en arrêt.

Par exemple:

#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;
}

Espérons que cela aide,

Jason

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X