EOS

Corso C avanzato su Raspberry PI: Visibilità e vita delle variabili

L'undicesima lezione del corso avanzato sul linguaggio C per Raspberry Pi esplora gli ambiti di utilizzo delle variabili. Queste si possono creare per essere utilizzate solo in una funzione o, al contrario, possono avere una visibilità molto più ampia. Esploriamo le varie tecniche descrivendo, anche, alcuni metodi per la rapida conversione di tipo.

Introduzione

Negli esempi proposti nelle precedenti puntate abbiamo utilizzato molte variabili, di diversi tipi, ma non abbiamo mai focalizzato sul loro ciclo di vita. Allo scopo occorre distinguere i due seguenti concetti:

  • life time: è, appunto, il tempo di vita di ogni variabile nel quale essa rimane in memoria durante l'esecuzione del programma;
  • scope: chiamata anche visibilità della variabile, è la parte del programma in cui essa risulta visibile e utilizzabile. Le variabili possono essere locali e globali.

Le variabili locali

Probabilmente le più utilizzate, le variabili locali sono quelle dichiarate all'interno di una funzione o di un blocco. Esse, dunque, possono essere accessibili, ossia utilizzabili, solo dentro l'ambito di definizione. Quando la funzione termina, le variabili in esse dichiarate sono distrutte.

Iniziamo con un semplice esempio.

#include "stdio.h"
int main() {
    int n; /* Questa e' una variabile locale nella funzione main */
    n = 55;
    printf("Siamo nella funzione main() e la variabile 'n' vale %d\n",n);
    miafunzione();
    return 0;
}
int miafunzione() {
   int k;  /* Questa e' una variabile locale nella funzione miafunzione */
   k = 2018;
   printf("Siamo nella funzione miafunzione() e la variabile 'k' vale %d\n",k);
   return 0;
}

Nel programma di cui sopra esiste la funzione main() e la funzione miafunzione(). In ognuna di esse sono dichiarate, inizializzate e visualizzate, rispettivamente, le variabili "n" e "k". Esse sono totalmente indipendenti tra loro e non si interferiscono reciprocamente in alcun modo. Abitano, praticamente, in due stanze diverse di una casa, come ben evidenziato in figura 1.

Figura 1: due variabili locali non si disturbano a vicenda

Figura 1: due variabili locali non si disturbano a vicenda

L'output del programma è il seguente:

Siamo nella funzione main() e la variabile 'n' vale 55
Siamo nella funzione miafunzione() e la variabile 'k' vale 2018
L'indipendenza delle variabili locali nei vari ambiti è così elevata che, addirittura, è possibile attribuire lo stesso nome a esse. Non vi è alcun conflitto. Tale possibilità, però, non deve portare a confusione mentale per il programmatore. Il listato qui sotto mostra come sia permesso usare lo stesso nome a diverse entità, purché siano state dichiarate in diversi blocchi o funzioni. Si tratta, a tutti gli effetti, di tre variabili diverse.
#include "stdio.h"
int main() {
    int n; /* Questa e' una variabile locale nella funzione main */
    n = 55;
    printf("Siamo nella funzione main() e la variabile 'n' vale %d\n",n);
    miafunzione();
    mianuovafunzione();
    return 0;
}
int miafunzione() {
   int n;  /* Questa e' una variabile locale nella funzione miafunzione */
   n = 2018;
   printf("Siamo nella funzione miafunzione() e la variabile 'n' vale %d\n",n);
   return 0;
}
int mianuovafunzione() {
   int n;  /* Questa e' una variabile locale nella funzione mianuovafunzione */
   n = 23456;
   printf("Siamo nella funzione mianuovafunzione() e la variabile 'n' vale %d\n",n);
   return 0;
}

L'output del programma è il seguente:

Siamo nella funzione main() e la variabile 'n' vale 55
Siamo nella funzione miafunzione() e la variabile 'n' vale 2018
Siamo nella funzione mianuovafunzione() e la variabile 'n' vale 23456

 

L'esempio che segue chiarirà ancora meglio il concetto, circa l'allocazione e la deallocazione delle variabili in memoria. Il listato esegue, sequenzialmente, alcuni passaggi:

  • nella funzione main():
    • dichiara la variabile "n";
    • le assegna il valore 23;
    • stampa il suo indirizzo e il suo contenuto: 0xbe9b3f94 e 23;
    • invoca la funzione funzione1();
      • dichiara la variabile "n";
      • le assegna il valore 1967;
      • stampa il suo indirizzo e il suo contenuto: 0xbe9b3f84 e 1967;
    • invoca la funzione funzione2();
      • dichiara la variabile "n";
      • le assegna il valore 12345;
      • stampa il suo indirizzo e il suo contenuto: 0xbe9b3f84 e 12345;
    • invoca la funzione funzione3();
      • dichiara la variabile "n";
      • le assegna il valore 69;
      • stampa il suo indirizzo e il suo contenuto: 0xbe9b3f84 e 69.
#include "stdio.h"
int funzionel();
int funzione2();
int funzione3();
int main() {
    int n;
    n=23;
    printf("Indirizzo: %p %d\n",&n,n);
    funzione1();
    funzione2();
    funzione3();
    return 0;
}
int funzione1() {
    int n;
    n=1967;
    printf("Indirizzo: %p %d\n",&n,n);
    return 0;
}
int funzione2() {
    int n;
    n=12345;
    printf("Indirizzo: %p %d\n",&n,n);
    return 0;
}
int funzione3() {
    int n;
    n=69;
    printf("Indirizzo: %p %d\n",&n,n);
    return 0;
}

Come è possibile che le variabili "n" della seconda, terza e quarta funzione sono sempre memorizzate allo stesso indirizzo? Per via del riutilizzo della memoria. Nella funzione main(), infatti, è riservato dello spazio per la variabile "n", all'indirizzo 0xbe9b3f94. Quindi, nella funzione funzione1() è riservato un ulteriore spazio per un'altra variabile "n", all'indirizzo 0xbe9b3f84, dal momento che la prima variabile "è ancora in vita". Quando la funzione1() ha termine, la variabile all'indirizzo 0xbe9b3f84 viene "distrutta" e il relativo indirizzo è reso disponibile per gli usi successivi. Questo è il motivo per cui la variabile "n" della funzione2() e della funzione3() sono allocate sempre nel medesimo posto (vedi figura 2).

Figura 2: le variabili possono essere distrutte e lo spazio in memoria è reso nuovamente disponibile

Figura 2: le variabili possono essere distrutte e lo spazio in memoria è reso nuovamente disponibile

Non è possibile, in modo normale, accedere e gestire una variabile locale, da una funzione all'altra. Il successivo esempio, illustrato chiaramente in figura 3, mostra come un tentativo di accesso "fuori stanza" causi un errore di compilazione.

Figura 3: non è possibile accedere alle variabili locali di altri blocchi o funzioni

Figura 3: non è possibile accedere alle variabili locali di altri blocchi o funzioni

[...]

ATTENZIONE: quello che hai appena letto è solo un estratto, l'Articolo Tecnico completo è composto da ben 2282 parole ed è riservato agli abbonati MAKER. Con l'Abbonamento avrai anche accesso a tutti gli altri Articoli Tecnici MAKER e potrai fare il download (PDF) dell'EOS-Book del mese. ABBONATI ORA, è semplice e sicuro.

Abbonati alle riviste di elettronica

7 Commenti

  1. Giovanni Di Maria Giovanni Di Maria 6 giugno 2019
  2. Giordana Francesca Brescia Giordana Francesca Brescia 6 giugno 2019
  3. Giovanni Di Maria Giovanni Di Maria 7 giugno 2019
  4. Alessandro Alessandro 12 giugno 2019
  5. SeggeTauli 21 giugno 2019
    • Alessandro Alessandro 21 giugno 2019
  6. SeggeTauli 24 giugno 2019

Scrivi un commento

EOS