utilizzo di doppi puntatori e n puntatori?

2

Ho familiarità con i puntatori C di base. Volevo solo chiedere qual è l'uso effettivo dei puntatori doppi o per quello che riguarda n puntatore?

#include<stdio.h>
int main()
{
    int n = 10 , *ptr , **ptr_ptr ;
    ptr = &n;
    ptr_ptr = &ptr;
    printf("%d", **ptr_ptr);
    return(0);
}

**ptr_ptr stampa 10 ma come?

    
posta instinct 26.06.2014 - 16:34
fonte

4 risposte

6

ptr_ptr è un puntatore a un puntatore a un int e punta a ptr . ptr è un puntatore a un int e punta a n . n è un int, che è stato impostato su 10.

Un prefisso '*' è l'operatore di riferimento, che significa "la cosa indicata da". Quindi **ptr_ptr valuta *ptr che valuta n , che è 10. Se aiuta, considera **ptr_ptr come equivalente a *(*ptr_ptr) .

A cosa serve? Due possibili usi sono

  1. Passaggio di un puntatore come parametro a una funzione, in cui si desidera che la funzione sia in grado di cambiare il puntatore a un altro.
  2. Passaggio di una serie di puntatori a una funzione, come nel classico int main(int argc, char **argv) .

Non ho mai riscontrato alcun bisogno di un int ***ptr_ptr_ptr , ma è valido C.

    
risposta data 26.06.2014 - 17:32
fonte
2

L'indiretta multipla si verifica generalmente nei seguenti scenari: scrittura su un parametro del tipo di puntatore e creazione di un array N-dimensionale per pezzo.

Scrittura su un parametro del tipo di puntatore

Per qualsiasi parametro di funzione di tipo T , se vuoi che la funzione modifichi il valore del parametro e abbia il nuovo valore riflesso nel chiamante, devi passare un puntatore:

void foo( T *ptr )
{
  *ptr = new_value(); // writes new value to thing pointed to by ptr
}

void bar( void )
{
  T var;
  foo( &var ); // writes new value to var
}

L'espressione *ptr in foo fa riferimento allo stesso oggetto in memoria come var in bar , quindi scrivere a *ptr equivale a scrivere a var .

Se T è un tipo di puntatore Q * , si finisce con un puntatore a un puntatore:

void foo( Q **ptr )
{
  *ptr = new_value(); // writes new value to thing pointed to by ptr
}

void bar( void )
{
  Q *var;
  foo( &var ); // writes new value to var
}

In questo secondo caso, si desidera modificare il valore del puntatore memorizzato in var . Ancora una volta, sia *ptr che var si riferiscono alla stessa posizione di memoria, ma questa volta, var contiene un valore puntatore. Puoi sostituire Q con R * , dando

void foo( R ***ptr )
{
  *ptr = new_value(); // writes new value to thing pointed to by ptr
}

void bar( void )
{
  R **var;
  foo( &var ); // writes new value to var
}

La semantica è la stessa in tutti e tre i casi; scriviamo un nuovo valore in var attraverso l'espressione *ptr ; è solo il tipo di var è un tipo di puntatore, il che significa che il tipo di ptr deve essere un tipo di puntatore con un ulteriore livello di riferimento indiretto.

Costruire un array N-dimensionale per pezzo

Supponiamo di voler allocare una matrice N x M di T , ma non si conoscono i valori di N o M in fase di compilazione. Un approccio è quello di dichiarare un puntatore a un puntatore, quindi allocare una serie di puntatori ad esso:

T **arr; // arr will point to an NxM array of T
size_t n, m;
/**
 * get values for n and m
 */
arr = malloc( sizeof *arr * n ); // allocate N objects of type T *
if ( arr )
{
  for ( size_t i = 0; i < n; i++ )
  {
    arr[i] = malloc( sizeof *arr[i] * m ); // allocate M objects of type T
    if ( arr[i] )
    {
      for ( size_t j = 0; i < m; i++ )
      {
        arr[i][j] = some_value();
      }
    }
  }
}

Lo stesso meccanismo generale può essere applicato per matrici di dimensioni maggiori:

T ***arr3;
...
arr3 = malloc( sizeof *arr3 * n );
...
  arr3[i] = malloc( sizeof *arr3[i] * m );
  ...
    arr3[i][j] = malloc( sizeof *arr3[i][j] * k );

Ci sono alcuni altri scenari in cui vedi più riferimenti indiretti, ma quelli sono i due principali.

In pratica è raro vedere più di tre livelli di riferimento indiretto, però.

    
risposta data 28.06.2014 - 01:41
fonte
2

Un puntatore al puntatore indica semplicemente che si dispone di un indirizzo di una posizione di memoria in cui è memorizzato un altro puntatore. Dereferisci due volte per ottenere il valore digitato finale.

int i = 123;
int* pi = &i;
int ** ppi = &pi;
int j = **ppi;

Ci sono un numero sorprendente di situazioni in cui è necessario utilizzare una di queste bestie.

  1. Come argomento di output di una chiamata di funzione, passa un puntatore al puntatore che desideri venga stampato o modificato.
  2. Accesso a una matrice di stringhe con terminazione null C; ogni stringa è essa stessa un puntatore. L'argomento argc per main sembra simile.
  3. 'Jagged' o array N-dimensionale, memorizzati come array di puntatore ai dati. I dati saranno generalmente assegnati con malloc ().
  4. Un 'handle' per un blocco di memoria, quindi il proprietario del blocco può spostarlo senza che l'utente del blocco debba sapere.
  5. Manipolazione di una struttura che contiene contatori, come elenchi collegati, alberi e così via. È praticamente impossibile inserire un elemento in un elenco collegato senza di essi.

È importante mantenere questi concetti separati. Sebbene tutti sembrano utilizzare lo stesso costrutto, in realtà lo scopo di fondo è molto diverso. E sono sicuro che ci sono altri che mi sono persi.

Non fornirò più codice. C'è un esempio eccellente con diagrammi e codice qui: link .

    
risposta data 28.06.2014 - 08:59
fonte
1

Uno dei vantaggi dell'essere vecchio e grigio è che ricordiamo cose che accaddero prima che nascessero i giovani.

L'originale Apple Macintosh (nato nel 1984) era MOLTO corto di RAM. Ha fatto allocazione dinamica della memoria, e doveva fornire una coalescenza facile di blocchi liberi, per creare blocchi liberi più grandi, per evitare i problemi di frammentazione. Per supportare questo, il sistema operativo allocato un blocco di memoria JUST per i puntatori ai blocchi allocati, e quindi l'allocatore di memoria, invece di restituire un puntatore al blocco appena assegnato, inserire il puntatore nel blocco puntatore e restituire un "handle" , un puntatore a quel puntatore. Ciò ha permesso al sistema operativo di spostare i blocchi allocati intorno, senza preavviso, e aggiornare il puntatore nel blocco del puntatore. L'handle dell'utente, che puntava sul blocco del puntatore, non è mai stato modificato.

Ciò consentiva di spostare un blocco allocato tra due blocchi liberi e di combinare i due blocchi liberi in un unico blocco libero.

Ora, dovevi essere consapevole del fatto che stava succedendo. Dovevi sapere di non mettere in cache il secondo puntatore da qualche parte. Il Mac originale utilizzava il multitasking cooperativo, quindi si poteva supporre che i blocchi non si muovessero mentre si lavorava su di essi, ma bisognava supporre che, una volta riavviato il processore dopo averlo ceduto, NESSUNO dei blocchi che si conoscevano sarebbe stato Dove erano. Tuttavia, le tue maniglie non sono mai cambiate, quindi è stato OK. Finché hai usato le maniglie ovunque, eri al sicuro.

    
risposta data 18.07.2018 - 19:48
fonte

Leggi altre domande sui tag