Puntatori in C: cos'è il puntatore nella programmazione C? Tipi

⚡ Riepilogo intelligente

In C, i puntatori sono variabili che memorizzano l'indirizzo di memoria di un'altra variabile. Permettono l'allocazione dinamica della memoria, la gestione efficiente di array e stringhe e la creazione di strutture dati complesse, utilizzando gli operatori di dereferenziazione e di accesso per leggere o modificare i valori.

  • ???? Definizione: Un puntatore contiene l'indirizzo di memoria di un'altra variabile.
  • 🔧 Operatori: L'operatore & restituisce un indirizzo; l'operatore * lo dereferenzia.
  • 🧷 Dichiarazione: Scrivi data_type *name, quindi inizializza con l'indirizzo di una variabile.
  • 🏷️ tipi: I puntatori nulli, void e wild si comportano in modo diverso e richiedono attenzione.
  • Aritmetica: L'incremento di un puntatore lo sposta della dimensione del suo tipo base.
  • 🤖 Assistenza AI: Gli strumenti di intelligenza artificiale individuano e risolvono i problemi di segmentazione e segnalano i puntatori pendenti durante la fase di revisione.

Puntatori in C

Che cos'è un puntatore in C?

A pointer In C, un puntatore è una variabile che memorizza l'indirizzo di un'altra variabile. Un puntatore può anche fare riferimento a un altro puntatore o a una funzione. Un puntatore può essere incrementato o decrementato per puntare alla posizione di memoria successiva o precedente. Lo scopo di un puntatore è quello di risparmiare spazio di memoria e ottenere tempi di esecuzione più rapidi.

Come utilizzare i puntatori in C

Se dichiariamo una variabile v di tipo int, v memorizza un valore. Ogni variabile, oltre al suo valore, ha anche un indirizzo, ovvero la sua posizione in memoria. L'indirizzo può essere recuperato anteponendo una e commerciale (&) al nome della variabile. Se si stampa l'indirizzo di una variabile, questo apparirà come un numero casuale che può variare da un'esecuzione all'altra.

Anziché memorizzare un valore, un puntatore memorizza l'indirizzo di una variabile. Ad esempio:

int *y = &v;
VARIABILE PUNTATORE
A APPREZZIAMO memorizzato in a detto indirizzo di archiviazione/memoria. A variabile che punta a l'indirizzo di archiviazione/memoria di un altro variabile.

Dichiarazione di un puntatore

Come le variabili, i puntatori in Programmazione C. Devono essere dichiarati prima di poter essere utilizzati. La dichiarazione di un puntatore ha la seguente forma:

data_type *pointer_variable_name;

Qui, tipo di dati è il tipo base del puntatore e indica il tipo della variabile a cui il puntatore punta. L'asterisco (*), lo stesso simbolo usato per la moltiplicazione, è l'operatore di indirezione che dichiara un puntatore. Alcune dichiarazioni di puntatore valide sono:

int    *ptr_thing;       /* pointer to an integer */
int    *ptr1, thing;     /* ptr1 points to an int; thing is an int */
double *ptr2;           /* pointer to a double */
float  *ptr3;           /* pointer to a float */
char   *ch1;            /* pointer to a character */

Inizializza un puntatore

Dopo aver dichiarato un puntatore, lo inizializziamo con l'indirizzo di una variabile. Se un puntatore non è inizializzato e viene utilizzato, i risultati possono essere imprevedibili e potenzialmente disastrosi. Per ottenere l'indirizzo di una variabile, si utilizza l'operatore e commerciale (&) prima del nome della variabile:

pointer = &variable;

Di seguito è riportato un semplice programma che illustra il funzionamento di un puntatore:

#include <stdio.h>
int main()
{
   int a = 10;     // variable declaration
   int *p;         // pointer variable declaration
   p = &a;        // store address of a in pointer p
   printf("Address stored in p is: %x\n", p);  // access the address
   printf("Value stored in p is: %d\n", *p);    // access the value
   return 0;
}

Produzione:

Address stored in p is: 60ff08
Value stored in p is: 10
Operator Significato
* Serve a due scopi: dichiara un puntatore e restituisce il valore della variabile a cui fa riferimento (dereferenziazione).
& Serve a un solo scopo: restituisce l'indirizzo di una variabile.

Tipi di puntatori in C

Puntatore nullo

Creiamo un puntatore nullo assegnandogli il valore NULL durante la dichiarazione. Questo è utile quando al puntatore non è ancora stato assegnato alcun indirizzo. Un puntatore nullo contiene sempre il valore 0:

#include <stdio.h>
int main()
{
   int *p = NULL;     // null pointer
   printf("The value inside variable p is: %x", p);
   return 0;
}

Puntatore del Vuoto

Un puntatore void, detto anche puntatore generico, non ha un tipo di dato standard. Viene creato utilizzando la parola chiave void e può memorizzare l'indirizzo di qualsiasi variabile:

#include <stdio.h>
int main()
{
   void *p = NULL;    // void pointer
   printf("The size of pointer is: %d\n", sizeof(p));
   return 0;
}

Puntatore selvatico

Un puntatore viene definito "puntatore selvaggio" se non è inizializzato con alcun valore. Questi puntatori non sono efficienti perché potrebbero puntare a una posizione di memoria sconosciuta, causando problemi e l'arresto anomalo del programma. È fondamentale prestare sempre attenzione ai puntatori selvaggi.

#include <stdio.h>
int main()
{
   int *p;          // wild pointer
   printf("%d", *p);
   return 0;
}

Altri tipi di puntatori in C includono il puntatore pendente, il puntatore complesso, il puntatore vicino, il puntatore lontano e il puntatore enorme.

Puntatori di accesso diretto e indiretto

In C, esistono due modi equivalenti per accedere e manipolare il contenuto di una variabile. L'accesso diretto utilizza direttamente il nome della variabile, mentre l'accesso indiretto utilizza un puntatore alla variabile:

#include <stdio.h>
int var = 1;        /* declare and initialize an int variable */
int *ptr;           /* declare a pointer to int */
int main(void)
{
   ptr = &var;       /* point ptr to var */
   printf("\nDirect access, var = %d", var);
   printf("\nIndirect access, var = %d", *ptr);
   *ptr = 48;        /* change var through the pointer */
   printf("\nIndirect access, var = %d", *ptr);
   return 0;
}

Aritmetica dei puntatori in C

Quando si lavora con i puntatori, è necessario osservare le seguenti regole di priorità. Gli operatori * e & hanno la stessa priorità degli operatori unari (negazione !, incremento ++, decremento –). Nella stessa espressione, gli operatori unari *, &, !, ++ e — vengono valutati da destra a sinistra. Se un puntatore P punta a una variabile X, allora *P può essere utilizzato ovunque sia possibile scrivere X. Le seguenti espressioni sono equivalenti:

Espressione Espressione equivalente
Y = *P + 1 Y = X + 1
*P = *P + 10 X = X + 10
*P += 2 X += 2
++*P ++X
(*P)++ X++

Nell'ultimo caso, sono necessarie le parentesi: poiché gli operatori unari * e ++ vengono valutati da destra a sinistra, senza parentesi verrebbe incrementato il puntatore P, non l'oggetto a cui punta. Le operazioni aritmetiche disponibili sui puntatori sono assegnazione, incremento e decremento, e addizione o sottrazione.tracimpostando un offset intero, che sposta il puntatore di un numero di elementi pari a tale valore (ogni passo corrisponde alla dimensione in byte del tipo base).

Puntatori e array in C con esempi

Tradizionalmente, si accede agli elementi di un array tramite un indice, ma i puntatori semplificano ulteriormente questa operazione. Un puntatore impostato sul nome dell'array punta al primo elemento:

#include <stdio.h>
int main()
{
   int a[5] = {1, 2, 3, 4, 5};   // array initialization
   int *p;                       // pointer declaration
   p = a;                       // p points to the first element; same as &a[0]
   for (int i = 0; i < 5; i++)
   {
      printf("%d\n", *p);            // print element
      p++;                      // move to the next element
   }
   return 0;
}

Aggiungere un numero a un puntatore lo sposta di tanti elementi in avanti. Se p punta all'indirizzo di memoria 0, allora p + 1 fa sì che il puntatore punti all'indirizzo di memoria 1.

Puntatori e stringhe C con esempi

Una stringa è un array di oggetti char che termina con un carattere nullo '\0'. Possiamo manipolare le stringhe usando i puntatori:

#include <stdio.h>
#include <string.h>
int main()
{
   char str[] = "Hello Guru99!";
   char *p;
   p = str;
   printf("First character is: %c\n", *p);
   p = p + 1;
   printf("Next character is: %c\n", *p);
   p = str;                     // reset the pointer
   for (int i = 0; i < strlen(str); i++)
   {
      printf("%c\n", *p);
      p++;
   }
   return 0;
}

Un altro modo per gestire le stringhe è tramite un array di puntatori:

#include <stdio.h>
int main()
{
   char *materials[] = {"iron", "copper", "gold"};
   for (int i = 0; i < 3; i++)
      printf("%s\n", materials[i]);
   return 0;
}

Vantaggi dei puntatori in C

  • I puntatori sono utili per accedere direttamente alle posizioni di memoria.
  • Forniscono un modo efficiente per accedere agli elementi di un array.
  • Vengono utilizzati per l'allocazione e la deallocazione dinamica della memoria.
  • Vengono utilizzati per formare strutture dati complesse come liste concatenate, grafi e alberi.

Svantaggi dei puntatori in C

  • I puntatori sono un po' complessi da capire.
  • Possono causare errori come guasti di segmentazione o accesso a una posizione di memoria non prevista.
  • Assegnare un valore errato a un puntatore può causare il danneggiamento della memoria.
  • I puntatori sono anche responsabili di perdite di memoria se la memoria non viene liberata.
  • È responsabilità del programmatore gestire i puntatori con attenzione.

DOMANDE FREQUENTI

Un puntatore pendente punta a una porzione di memoria che è stata liberata o che è fuori dall'ambito di validità. Per evitarlo, imposta il puntatore a NULL immediatamente dopo aver chiamato `free` e verifica che non sia NULL prima dell'utilizzo.

La dereferenziazione di un puntatore NULL causa un errore di segmentazione e l'arresto anomalo del programma in fase di esecuzione. Verificare sempre che un puntatore non sia NULL prima di utilizzare l'operatore * per leggere o scrivere.

Un puntatore doppio, dichiarato come int **pp, memorizza l'indirizzo di un altro puntatore. Viene utilizzato per array bidimensionali dinamici e per modificare un argomento puntatore all'interno di una funzione.

Un puntatore memorizza un indirizzo di memoria e gli indirizzi hanno la stessa larghezza su una data piattaforma, in genere 4 byte su 32 bit e 8 byte su 64 bit, indipendentemente dal tipo a cui punta.

Un puntatore a funzione memorizza l'indirizzo di una funzione in modo che possa essere chiamata dinamicamente. È ampiamente utilizzato per le callback e per la creazione di tabelle di funzioni selezionate in fase di esecuzione.

Funzioni come malloc e calloc restituiscono un puntatore alla memoria allocata sull'heap in fase di esecuzione. Si accede a tale memoria tramite il puntatore e la si rilascia con free per evitare perdite di memoria.

Sì. Gli assistenti basati sull'IA leggono un errore di segmentazione e il codice circostante, quindi spiegano la causa probabile, come ad esempio un puntatore non inizializzato o liberato, e suggeriscono una soluzione da esaminare.

Gli analizzatori basati sull'intelligenza artificiale, insieme a strumenti come Valgrind, segnalano la memoria che non viene mai liberata e i puntatori utilizzati dopo la sua liberazione. Automatizzano la revisione e mettono in evidenza schemi rischiosi prima che il codice venga rilasciato.

Riassumi questo post con: