Waarom dubbele indirectheid gebruiken? of Waarom pointers naar pointers gebruiken?

Wanneer moet een dubbele indirectie worden gebruikt in C? Kan iemand het uitleggen met een voorbeeld?

Wat ik weet is dat een dubbele indirectie een pointer naar een pointer is. Waarom zou ik een aanwijzer naar een aanwijzer nodig hebben?


Antwoord 1, autoriteit 100%

Als u een lijst met tekens (een woord) wilt hebben, kunt u char *word

gebruiken

Als u een lijst met woorden (een zin) wilt, kunt u char **sentence

gebruiken

Als je een lijst met zinnen (een monoloog) wilt, kun je char ***monologue

gebruiken

Als je een lijst met monologen (een biografie) wilt, kun je char ****biography

gebruiken

Als je een lijst met biografieën (een bio-bibliotheek) wilt, kun je char *****biolibrary

gebruiken

Als je een lijst met biobibliotheken (een ??lol) wilt, kun je char ******lol

gebruiken

… …

ja, ik weet dat dit misschien niet de beste gegevensstructuren zijn


Gebruiksvoorbeeld met een heel erg saaie lol

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int wordsinsentence(char **x) {
    int w = 0;
    while (*x) {
        w += 1;
        x++;
    }
    return w;
}
int wordsinmono(char ***x) {
    int w = 0;
    while (*x) {
        w += wordsinsentence(*x);
        x++;
    }
    return w;
}
int wordsinbio(char ****x) {
    int w = 0;
    while (*x) {
        w += wordsinmono(*x);
        x++;
    }
    return w;
}
int wordsinlib(char *****x) {
    int w = 0;
    while (*x) {
        w += wordsinbio(*x);
        x++;
    }
    return w;
}
int wordsinlol(char ******x) {
    int w = 0;
    while (*x) {
        w += wordsinlib(*x);
        x++;
    }
    return w;
}
int main(void) {
    char *word;
    char **sentence;
    char ***monologue;
    char ****biography;
    char *****biolibrary;
    char ******lol;
    //fill data structure
    word = malloc(4 * sizeof *word); // assume it worked
    strcpy(word, "foo");
    sentence = malloc(4 * sizeof *sentence); // assume it worked
    sentence[0] = word;
    sentence[1] = word;
    sentence[2] = word;
    sentence[3] = NULL;
    monologue = malloc(4 * sizeof *monologue); // assume it worked
    monologue[0] = sentence;
    monologue[1] = sentence;
    monologue[2] = sentence;
    monologue[3] = NULL;
    biography = malloc(4 * sizeof *biography); // assume it worked
    biography[0] = monologue;
    biography[1] = monologue;
    biography[2] = monologue;
    biography[3] = NULL;
    biolibrary = malloc(4 * sizeof *biolibrary); // assume it worked
    biolibrary[0] = biography;
    biolibrary[1] = biography;
    biolibrary[2] = biography;
    biolibrary[3] = NULL;
    lol = malloc(4 * sizeof *lol); // assume it worked
    lol[0] = biolibrary;
    lol[1] = biolibrary;
    lol[2] = biolibrary;
    lol[3] = NULL;
    printf("total words in my lol: %d\n", wordsinlol(lol));
    free(lol);
    free(biolibrary);
    free(biography);
    free(monologue);
    free(sentence);
    free(word);
}

Uitvoer:

totaal aantal woorden in mijn lol: 243

Antwoord 2, autoriteit 35%

Eén reden is dat u de waarde van de aanwijzer die als functieargument aan een functie wordt doorgegeven, wilt wijzigen, hiervoor heeft u een aanwijzer naar een aanwijzer nodig.

In eenvoudige bewoordingen, Gebruik **wanneer u de geheugentoewijzing of toewijzing wilt behouden (OF behouden) zelfs buiten een functieaanroep.(Dus dus , Geef een dergelijke functie door met dubbele aanwijzer arg.)

Dit is misschien niet zo’n goed voorbeeld, maar laat je het basisgebruik zien:

#include <stdio.h>
#include <stdlib.h>
void allocate(int **p)
{
    *p = (int *)malloc(sizeof(int));
}
int main()
{
    int *p = NULL;
    allocate(&p);
    *p = 42;
    printf("%d\n", *p);
    free(p);
}

Antwoord 3, Autoriteit 23%

  • Laten we zeggen dat je een aanwijzer hebt. De waarde is een adres.
  • maar nu wilt u dat adres wijzigen.
  • die je zou kunnen. Door pointer1 = pointer2te doen, geeft u aanwijzer1 het adres van Pointer2.
  • Maar! Als u dat binnen een functie doet, en u wilt dat het resultaat blijft bestaan ​​nadat de functie is voltooid, moet u wat extra werk doen. U hebt een nieuwe pointer3 nodig om te wijzen op Pointer1. Pas Pointer3 door naar de functie.

  • Hier is een voorbeeld. Bekijk de uitvoer hieronder eerst, om te begrijpen.

#include <stdio.h>
int main()
{
    int c = 1;
    int d = 2;
    int e = 3;
    int * a = &c;
    int * b = &d;
    int * f = &e;
    int ** pp = &a;  // pointer to pointer 'a'
    printf("\n a's value: %x \n", a);
    printf("\n b's value: %x \n", b);
    printf("\n f's value: %x \n", f);
    printf("\n can we change a?, lets see \n");
    printf("\n a = b \n");
    a = b;
    printf("\n a's value is now: %x, same as 'b'... it seems we can, but can we do it in a function? lets see... \n", a);
    printf("\n cant_change(a, f); \n");
    cant_change(a, f);
    printf("\n a's value is now: %x, Doh! same as 'b'...  that function tricked us. \n", a);
    printf("\n NOW! lets see if a pointer to a pointer solution can help us... remember that 'pp' point to 'a' \n");
     printf("\n change(pp, f); \n");
    change(pp, f);
    printf("\n a's value is now: %x, YEAH! same as 'f'...  that function ROCKS!!!. \n", a);
    return 0;
}
void cant_change(int * x, int * z){
    x = z;
    printf("\n ----> value of 'a' is: %x inside function, same as 'f', BUT will it be the same outside of this function? lets see\n", x);
}
void change(int ** x, int * z){
    *x = z;
    printf("\n ----> value of 'a' is: %x inside function, same as 'f', BUT will it be the same outside of this function? lets see\n", *x);
}

Hier is de output: (lees dit eerst)

a's value: bf94c204
 b's value: bf94c208 
 f's value: bf94c20c 
 can we change a?, lets see 
 a = b 
 a's value is now: bf94c208, same as 'b'... it seems we can, but can we do it in a function? lets see... 
 cant_change(a, f); 
 ----> value of 'a' is: bf94c20c inside function, same as 'f', BUT will it be the same outside of this function? lets see
 a's value is now: bf94c208, Doh! same as 'b'...  that function tricked us. 
 NOW! lets see if a pointer to a pointer solution can help us... remember that 'pp' point to 'a' 
 change(pp, f); 
 ----> value of 'a' is: bf94c20c inside function, same as 'f', BUT will it be the same outside of this function? lets see
 a's value is now: bf94c20c, YEAH! same as 'f'...  that function ROCKS!!!. 

Antwoord 4, autoriteit 11%

Toevoegen aan Asha’sreactie, als u een enkele verwijzing naar het voorbeeld hieronder gebruikt (bijv. alloc1() ), verliest u de verwijzing naar het geheugen dat binnen de functie is toegewezen.

#include <stdio.h>
#include <stdlib.h>
void alloc2(int** p) {
    *p = (int*)malloc(sizeof(int));
    **p = 10;
}
void alloc1(int* p) {
    p = (int*)malloc(sizeof(int));
    *p = 10;
}
int main(){
    int *p = NULL;
    alloc1(p);
    //printf("%d ",*p);//undefined
    alloc2(&p);
    printf("%d ",*p);//will print 10
    free(p);
    return 0;
}

De reden dat het zo gebeurt, is dat in alloc1de aanwijzer op waarde wordt doorgegeven. Dus wanneer het opnieuw wordt toegewezen aan het resultaat van de malloc-aanroep binnen alloc1, heeft de wijziging geen betrekking op code in een ander bereik.


Antwoord 5, autoriteit 5%

Ik zag vandaag een heel goed voorbeeld, van deze blogpost, zoals ik hieronder samenvat.

Stel je voor dat je een structuur hebt voor knooppunten in een gekoppelde lijst, wat waarschijnlijk is

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

Nu wil je een functie remove_ifimplementeren, die een verwijderingscriterium rmals een van de argumenten accepteert en de gekoppelde lijst doorloopt: als een item voldoet aan het criterium ( zoiets als rm(entry)==true), wordt het knooppunt uit de lijst verwijderd. Uiteindelijk retourneert remove_ifde kop (die kan verschillen van de oorspronkelijke kop) van de gekoppelde lijst.

Je mag schrijven

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

als uw forlus. Het bericht is, zonder dubbele wijzers, u moet een prevvariabele onderhouden om de wijzers opnieuw te organiseren en omgaan met de twee verschillende gevallen.

Maar met dubbele wijzers kunt u eigenlijk

schrijven

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

U hebt nu geen prevnodig omdat u direct kunt wijzigen wat prev->nextwees naar .

Laten we de code duidelijk maken, de code een beetje volgen. Tijdens de verwijdering:

  1. Indien entry == *head: het is *head (==*curr) = *head->nextheadwijst nu naar de aanwijzer van het nieuwe kopknooppunt. U doet dit door rechtstreeks head‘s inhoud aan een nieuwe aanwijzer te wijzigen.
  2. Indien entry != *head: Evenzo is *currWAT prev->nextwees naar, en wijst nu naar entry->next.

Ongeacht in welk geval u de wijzers op een uniforme manier opnieuw kunt organiseren met dubbele wijzers.


Antwoord 6, Autoriteit 4%

1. Basic Concept –

Wanneer u als volgt aangeeft: –

1. Char * CH – (Karakter Pointer genoemd)
– CH bevat het adres van een enkel teken.
– (* CH) zal nerfectiëren naar de waarde van het personage ..

2. Char ** CH –
‘CH’ bevat het adres van een reeks karakterwijzers. (zoals in 1)
‘* CH’ bevat het adres van een enkel teken. (Merk op dat het anders is dan 1, vanwege het verschil in de aangifte).
(**ch) verwijst naar de exacte waarde van het teken..

Door meer wijzers toe te voegen, wordt de dimensie van een gegevenstype uitgebreid, van teken tot tekenreeks, tot reeks tekenreeksen, enzovoort… U kunt het relateren aan een 1d, 2d, 3D-matrix..

Dus het gebruik van de aanwijzer hangt af van hoe je het declareert.

Hier is een eenvoudige code..

int main()
{
    char **p;
    p = (char **)malloc(100);
    p[0] = (char *)"Apple";      // or write *p, points to location of 'A'
    p[1] = (char *)"Banana";     // or write *(p+1), points to location of 'B'
    cout << *p << endl;          //Prints the first pointer location until it finds '\0'
    cout << **p << endl;         //Prints the exact character which is being pointed
    *p++;                        //Increments for the next string
    cout << *p;
}

2. Een andere toepassing van dubbele wijzers –
(dit geldt ook voor pass-by-referentie)

Stel dat u een teken van een functie wilt bijwerken. Als je het volgende probeert: –

void func(char ch)
{
    ch = 'B';
}
int main()
{
    char ptr;
    ptr = 'A';
    printf("%c", ptr);
    func(ptr);
    printf("%c\n", ptr);
}

De uitvoer is AA. Dit werkt niet, omdat je “Passed By Value” hebt voor de functie.

De juiste manier om dat te doen zou zijn –

void func( char *ptr)        //Passed by Reference
{
    *ptr = 'B';
}
int main()
{
    char *ptr;
    ptr = (char *)malloc(sizeof(char) * 1);
    *ptr = 'A';
    printf("%c\n", *ptr);
    func(ptr);
    printf("%c\n", *ptr);
}

Breid deze vereiste nu uit voor het bijwerken van een string in plaats van karakter.
Hiervoor moet u de parameter in de functie ontvangen als dubbele aanwijzer.

void func(char **str)
{
    strcpy(str, "Second");
}
int main()
{
    char **str;
    // printf("%d\n", sizeof(char));
    *str = (char **)malloc(sizeof(char) * 10);          //Can hold 10 character pointers
    int i = 0;
    for(i=0;i<10;i++)
    {
        str = (char *)malloc(sizeof(char) * 1);         //Each pointer can point to a memory of 1 character.
    }
    strcpy(str, "First");
    printf("%s\n", str);
    func(str);
    printf("%s\n", str);
}

In dit voorbeeld verwacht methode een dubbele aanwijzer als parameter om de waarde van een string bij te werken.


Antwoord 7, Autoriteit 4%

Pointers naar Pointers komen ook van pas als “handgrepen” in het geheugen waar u een “handvat” wilt doorgeven tussen functies om opnieuw te lokaliseren. Dat betekent in principe dat de functie het geheugen kan veranderen waarnaar wordt aangegeven door de aanwijzer in de handvatvariabele en elke functie of object die het handvat gebruikt, wijst op de juiste manier op het nieuw verplaatst (of toegewezen) geheugen. Bibliotheken houden van doen dit met “ondoorzichtig” datasypen, dat is datatypen waar je geen zorgen hoeft te maken over wat ze doen met het geheugen dat wordt aangegeven, je passeert eenvoudigweg de “hendel” tussen de Functies van de bibliotheek om sommige bewerkingen op dat geheugen uit te voeren … De bibliotheekfuncties kunnen het geheugen onder de kap aanbieden en de-toewijzen zonder dat u zich uitdrukkelijk moet zorgen over het proces van geheugenbeheer of waar het handvat wijzen.

Bijvoorbeeld:

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

Ik hoop dat dit helpt,

Jason


Antwoord 8

Eenvoudig voorbeeld dat je waarschijnlijk al vaker hebt gezien

int main(int argc, char **argv)

In de tweede parameter heb je het: pointer to pointer to char.

Merk op dat de aanwijzernotatie (char* c) en de array-notatie (char c[]) uitwisselbaar zijn in functieargumenten. Je zou dus ook char *argv[]kunnen schrijven. Met andere woorden char *argv[]en char **argvzijn onderling uitwisselbaar.

Wat het bovenstaande voorstelt, is in feite een reeks tekenreeksen (de opdrachtregelargumenten die bij het opstarten aan een programma worden gegeven).

Zie ook dit antwoordvoor meer details over de bovenstaande functiehandtekening.


Antwoord 9

Strings zijn een goed voorbeeld van het gebruik van dubbele pointers. De tekenreeks zelf is een aanwijzer, dus elke keer dat u naar een tekenreeks moet verwijzen, hebt u een dubbele aanwijzer nodig.


Antwoord 10

U wilt er bijvoorbeeld voor zorgen dat wanneer u het geheugen van iets vrijmaakt, u de aanwijzer daarna op nul zet.

void safeFree(void** memory) {
    if (*memory) {
        free(*memory);
        *memory = NULL;
    }
}

Als je deze functie aanroept, roep je hem aan met het adres van een aanwijzer

void* myMemory = someCrazyFunctionThatAllocatesMemory();
safeFree(&myMemory);

Nu is myMemoryingesteld op NULL en elke poging om het opnieuw te gebruiken zal duidelijk verkeerd zijn.


Antwoord 11

Een beetje laat op het feest, maar hopelijk helpt dit iemand.

Wijs in C-arrays altijd geheugen toe aan de stapel, dus een functie kan niet terugkeren
een (niet-statische) array vanwege het feit dat geheugen toegewezen aan de stack
wordt automatisch vrijgegeven wanneer de uitvoering het einde van het huidige blok bereikt.
Dat is echt vervelend als je met tweedimensionale arrays te maken wilt hebben
(d.w.z. matrices) en implementeer een paar functies die matrices kunnen wijzigen en retourneren.
Om dit te bereiken, kunt u een pointer-to-pointer gebruiken om een matrix te implementeren met:
dynamisch toegewezen geheugen:

/* Initializes a matrix */
double** init_matrix(int num_rows, int num_cols){
    // Allocate memory for num_rows float-pointers
    double** A = calloc(num_rows, sizeof(double*));
    // return NULL if the memory couldn't allocated
    if(A == NULL) return NULL;
    // For each double-pointer (row) allocate memory for num_cols floats
    for(int i = 0; i < num_rows; i++){
        A[i] = calloc(num_cols, sizeof(double));
        // return NULL if the memory couldn't allocated
        // and free the already allocated memory
        if(A[i] == NULL){
            for(int j = 0; j < i; j++){
                free(A[j]);
            }
            free(A);
            return NULL;
        }
    }
    return A;
} 

Hier is een illustratie:

double**       double*           double
             -------------       ---------------------------------------------------------
   A ------> |   A[0]    | ----> | A[0][0] | A[0][1] | A[0][2] | ........ | A[0][cols-1] |
             | --------- |       ---------------------------------------------------------
             |   A[1]    | ----> | A[1][0] | A[1][1] | A[1][2] | ........ | A[1][cols-1] |
             | --------- |       ---------------------------------------------------------
             |     .     |                                    .
             |     .     |                                    .
             |     .     |                                    .
             | --------- |       ---------------------------------------------------------
             |   A[i]    | ----> | A[i][0] | A[i][1] | A[i][2] | ........ | A[i][cols-1] |
             | --------- |       ---------------------------------------------------------
             |     .     |                                    .
             |     .     |                                    .
             |     .     |                                    .
             | --------- |       ---------------------------------------------------------
             | A[rows-1] | ----> | A[rows-1][0] | A[rows-1][1] | ... | A[rows-1][cols-1] |
             -------------       ---------------------------------------------------------

De dubbele pointer-naar-double-pointer averwijst naar het eerste element A[0]van een
geheugenblok waarvan de elementen zelf dubbele pointers zijn. Je kunt je deze voorstellen
dubbele wijzers als de rijen van de matrix. Dat is de reden waarom elke
double-pointer wijst geheugen toe voor num_cols elementen van het type double.
Verder wijst A[i]naar de i-de rij, d.w.z. A[i]wijst naar A[i][0]en
dat is slechts het eerste dubbele element van het geheugenblok voor de i-de rij.
Eindelijk heb je toegang tot het element in de i-de rij
en j-de kolom gemakkelijk met A[i][j].

Hier is een compleet voorbeeld dat het gebruik demonstreert:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
/* Initializes a matrix */
double** init_matrix(int num_rows, int num_cols){
    // Allocate memory for num_rows double-pointers
    double** matrix = calloc(num_rows, sizeof(double*));
    // return NULL if the memory couldn't allocated
    if(matrix == NULL) return NULL;
    // For each double-pointer (row) allocate memory for num_cols
    // doubles
    for(int i = 0; i < num_rows; i++){
        matrix[i] = calloc(num_cols, sizeof(double));
        // return NULL if the memory couldn't allocated
        // and free the already allocated memory
        if(matrix[i] == NULL){
            for(int j = 0; j < i; j++){
                free(matrix[j]);
            }
            free(matrix);
            return NULL;
        }
    }
    return matrix;
}
/* Fills the matrix with random double-numbers between -1 and 1 */
void randn_fill_matrix(double** matrix, int rows, int cols){
    for (int i = 0; i < rows; ++i){
        for (int j = 0; j < cols; ++j){
            matrix[i][j] = (double) rand()/RAND_MAX*2.0-1.0;
        }
    }
}
/* Frees the memory allocated by the matrix */
void free_matrix(double** matrix, int rows, int cols){
    for(int i = 0; i < rows; i++){
        free(matrix[i]);
    }
    free(matrix);
}
/* Outputs the matrix to the console */
void print_matrix(double** matrix, int rows, int cols){
    for(int i = 0; i < rows; i++){
        for(int j = 0; j < cols; j++){
            printf(" %- f ", matrix[i][j]);
        }
        printf("\n");
    }
}
int main(){
    srand(time(NULL));
    int m = 3, n = 3;
    double** A = init_matrix(m, n);
    randn_fill_matrix(A, m, n);
    print_matrix(A, m, n);
    free_matrix(A, m, n);
    return 0;
}

Antwoord 12

Bijvoorbeeld als u willekeurige toegang tot niet-aaneengesloten gegevens wilt.

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

Je slaat een pointer pop die naar een array van pointers verwijst. Elke aanwijzer wijst naar een stukje data.

Als sizeof(T)groot is, is het misschien niet mogelijk om een aaneengesloten blok (dwz met malloc) van sizeof(T) * nbytes toe te wijzen.

p>


Antwoord 13

Een ding waar ik ze constant voor gebruik, is wanneer ik een reeks objecten heb en ik zoekacties (binair zoeken) op ze moet uitvoeren door verschillende velden.
Ik behoud de originele array…

int num_objects;
OBJECT *original_array = malloc(sizeof(OBJECT)*num_objects);

Maak vervolgens een reeks gesorteerde verwijzingen naar de objecten.

int compare_object_by_name( const void *v1, const void *v2 ) {
  OBJECT *o1 = *(OBJECT **)v1;
  OBJECT *o2 = *(OBJECT **)v2;
  return (strcmp(o1->name, o2->name);
}
OBJECT **object_ptrs_by_name = malloc(sizeof(OBJECT *)*num_objects);
  int i = 0;
  for( ; i<num_objects; i++)
    object_ptrs_by_name[i] = original_array+i;
  qsort(object_ptrs_by_name, num_objects, sizeof(OBJECT *), compare_object_by_name);

Je kunt zoveel gesorteerde pointer-arrays maken als je nodig hebt, en vervolgens een binaire zoekopdracht op de gesorteerde pointer-array gebruiken om toegang te krijgen tot het object dat je nodig hebt op basis van de gegevens die je hebt. De originele array van objecten kan ongesorteerd blijven, maar elke pointerarray wordt gesorteerd op het gespecificeerde veld.


Antwoord 14

Waarom dubbele pointers?

Het doel is om met een functie te veranderen waar studentA naar verwijst.

#include <stdio.h>
#include <stdlib.h>
typedef struct Person{
    char * name;
} Person; 
/**
 * we need a ponter to a pointer, example: &studentA
 */
void change(Person ** x, Person * y){
    *x = y; // since x is a pointer to a pointer, we access its value: a pointer to a Person struct.
}
void dontChange(Person * x, Person * y){
    x = y;
}
int main()
{
    Person * studentA = (Person *)malloc(sizeof(Person));
    studentA->name = "brian";
    Person * studentB = (Person *)malloc(sizeof(Person));
    studentB->name = "erich";
    /**
     * we could have done the job as simple as this!
     * but we need more work if we want to use a function to do the job!
     */
    // studentA = studentB;
    printf("1. studentA = %s (not changed)\n", studentA->name);
    dontChange(studentA, studentB);
    printf("2. studentA = %s (not changed)\n", studentA->name);
    change(&studentA, studentB);
    printf("3. studentA = %s (changed!)\n", studentA->name);
    return 0;
}
/**
 * OUTPUT:
 * 1. studentA = brian (not changed)
 * 2. studentA = brian (not changed)
 * 3. studentA = erich (changed!)
 */

Antwoord 15

Het volgende is een heel eenvoudig C++-voorbeeld dat laat zien dat als je een functie wilt gebruiken om een aanwijzer naar een object te laten wijzen, je een aanwijzer naar een aanwijzer nodig hebt. Anders wordt de aanwijzer steeds weer null.

(Een C++-antwoord, maar ik geloof dat het hetzelfde is in C.)

(Ook ter referentie: Google(“pass by value c++”) = “Standaard worden argumenten in C++ doorgegeven door waarde. Wanneer een argument wordt doorgegeven door waarde, wordt de waarde van het argument gekopieerd naar de parameter van de functie.” )

Dus we willen de aanwijzer bgelijk stellen aan de tekenreeks a.

#include <iostream>
#include <string>
void Function_1(std::string* a, std::string* b) {
  b = a;
  std::cout << (b == nullptr);  // False
}
void Function_2(std::string* a, std::string** b) {
  *b = a;
  std::cout << (b == nullptr);  // False
}
int main() {
  std::string a("Hello!");
  std::string* b(nullptr);
  std::cout << (b == nullptr);  // True
  Function_1(&a, b);
  std::cout << (b == nullptr);  // True
  Function_2(&a, &b);
  std::cout << (b == nullptr);  // False
}
// Output: 10100

Wat gebeurt er op de regel Function_1(&a, b);?

  • De “waarde” van &main::a(een adres) wordt gekopieerd naar de parameter std::string* Function_1::a. Daarom is Function_1::aeen verwijzing naar (d.w.z. het geheugenadres van) de string main::a.

  • De “waarde” van main::b(een adres in het geheugen) wordt gekopieerd naar de parameter std::string* Function_1::b. Daarom zijn er nu 2 van deze adressen in het geheugen, beide null-pointers. Op de regel b = a;wordt de lokale variabele Function_1::bdan gewijzigd in Function_1::a(= &main::a), maar de variabele main::bis ongewijzigd. Na de aanroep van Function_1is main::bnog steeds een null-pointer.

Wat gebeurt er op de regel Function_2(&a, &b);?

  • De behandeling van de variabele ais hetzelfde: binnen de functie is Function_2::ahet adres van de string main::a.

  • Maar de variabele bwordt nu doorgegeven als een aanwijzer naar een aanwijzer. De “waarde” van &main::b(het adres van de aanwijzermain::b) wordt gekopieerd naar std::string** Function_2::b. Daarom zal binnen Function_2 door hier naar te verwijzen als *Function_2::bmain::bworden geopend en gewijzigd. Dus de regel *b = a;stelt eigenlijk main::b(een adres) gelijk aan Function_2::a(= adres van main::a) en dat is wat we willen.

Als je een functie wilt gebruiken om iets te wijzigen, of het nu een object of een adres (pointer) is, moet je een pointer naar dat ding doorgeven.Het ding dat je eigenlijkdoorgeven kan niet worden gewijzigd (in het aanroepende bereik) omdat er een lokale kopie wordt gemaakt.

(Een uitzondering is als de parameter een referentie is, zoals std::string& a. Maar meestal zijn dit const. In het algemeen, als u f(x), als xeen object is, zou je moeten kunnen aannemen dat fnietx. Maar als xeen aanwijzer is, dan moet je aannemen dat fmisschienhet object waarnaar wordt verwezen door x.)


Antwoord 16

Ik heb vandaag dubbele aanwijzers gebruikt terwijl ik iets voor mijn werk aan het programmeren was, dus ik kan antwoorden waarom we ze moesten gebruiken (het is de eerste keer dat ik dubbele aanwijzers moest gebruiken). We hadden te maken met real-time codering van frames in buffers die lid zijn van sommige structuren. In de encoder moesten we een pointer naar een van die structuren gebruiken. Het probleem was dat onze aanwijzer werd gewijzigd om naar andere structuren uit een andere thread te verwijzen. Om de huidige structuur in de encoder te gebruiken, moest ik een dubbele aanwijzer gebruiken om naar de aanwijzer te wijzen die in een andere thread werd gewijzigd. Het was in eerste instantie niet duidelijk, althans voor ons, dat we deze aanpak moesten volgen. Er zijn veel adressen afgedrukt tijdens het proces :)).

U MOET dubbele aanwijzers gebruiken wanneer u werkt aan aanwijzers die op andere plaatsen in uw toepassing zijn gewijzigd. Het kan ook zijn dat dubbele verwijzingen een must zijn als u te maken hebt met hardware die naar u terugkeert en aan u adresseert.


Antwoord 17

Vergelijk het wijzigen van waarde van variabeleversus het wijzigen van waarde van aanwijzer:

#include <stdio.h>
#include <stdlib.h>
void changeA(int (*a))
{
  (*a) = 10;
}
void changeP(int *(*P))
{
  (*P) = malloc(sizeof((*P)));
}
int main(void)
{
  int A = 0;
  printf("orig. A = %d\n", A);
  changeA(&A);
  printf("modi. A = %d\n", A);
  /*************************/
  int *P = NULL;
  printf("orig. P = %p\n", P);
  changeP(&P);
  printf("modi. P = %p\n", P);
  free(P);
  return EXIT_SUCCESS;
}

Dit hielp me om te voorkomen dat de waarde van de aanwijzer werd geretourneerd wanneer de aanwijzer werd gewijzigd door de aangeroepen functie (gebruikt in een enkelvoudig gekoppelde lijst).

OUD (slecht):

int *func(int *P)
{
  ...
  return P;
}
int main(void)
{
  int *pointer;
  pointer = func(pointer);
  ...
}    

Nieuw (beter):

void func(int **pointer)
{
  ...
}
int main(void)
{
  int *pointer;
  func(&pointer);
  ...
}    

Antwoord 18

De meeste antwoorden hier zijn min of meer gerelateerd aan de programmering van de toepassing. Hier is een voorbeeld van embedded systemenprogrammering. Hieronder is bijvoorbeeld een fragment uit het referentiaalgebied van de Kinetis KL13-serie Microcontroller van NXP, dit codefragment wordt gebruikt om bootloader te draaien, die in ROM is, van firmware:


Om het adres van het entry Point te krijgen, leest de gebruiker toepassing het woord dat de aanwijzer bevat met de bootloader API-boom bij offset 0x1c van de vectortafel van de bootloader. De vectortabel wordt geplaatst aan de basis van het adresbereik van het bootloader, dat voor de ROM 0x1C00_0000 is. De API-boompointer is dus op adres 0x1c00_001C.

De bootloader API-boom is een structuur die aanwijzingen bevat naar andere structuren, die de functie en gegevensadressen voor de bootloader hebben. Het toegangspunt van de bootloader is altijd het eerste woord van de API-boom.

uint32_t runBootloaderAddress;
void (*runBootloader)(void * arg);
// Read the function address from the ROM API tree.
runBootloaderAddress = **(uint32_t **)(0x1c00001c);
runBootloader = (void (*)(void * arg))runBootloaderAddress;
// Start the bootloader.
runBootloader(NULL);

Other episodes