[C] Lezione 3

In questa lezione vedremo:

- Cosa sono le strutture e le unioni;

- Cos'è un typedef;

- Come dichiarare una funzione;

- La differenza fra variabile globale e locale;

- Cosa sono i puntatori;

- Cos'è una variabile statica.

Strutture e unioni

  

Le strutture sono tipi di dati che possono essere creati a piacimento.

 

#include <stdio.h>

 

struct PUNTO3D { // Si usa struct per creare strutture

    float x, y, z; // La struttura occupa 4 + 4 + 4 = 12 byte

} Punto1; // Istanza

int main() {

    struct PUNTO3D Punto2, // Dichiara delle istanze PUNTO3D

                   Punto3;

  

    Punto1.x = 4.0f;

    Punto1.y = 16.0f;

    Punto1.z = 45.524f; // Assegna i valori ai campi dell'istanza

  

    Punto2 = {1.0f, 2.0f, 3.0f};

 

    Punto3 = Punto2;

 

    return 0;

}

 

Le unioni si dichiarano come le strutture. Sono come variabili che possono essere di più tipi, oppure come strutture con un valore unico in memoria. Un esempio per chiarire:

 

#include <stdio.h>

 

union CONVERTER // Si usa union per creare unioni

{

    unsigned char c[4];

    float f; // L'unione occupa 4 byte (e non 8)

};

int main()

{

    union CONVERTER Conv;

 

    Conv.f = 16.4f;

 

    printf("%f -> %c, %c, %c, %c", Conv.f, Conv.c[0], Conv.c[1], Conv.c[2], Conv.c[3]);

 

    return 0;

}

 

Questo esempio converte i dati di un float in dati di 4 char. Ciò succede perché, a differenza della struct, la union contiene solo un valore in memoria. Nota: questo esempio è considerato unspecified behaviour in C++, e pertanto non è consigliato il suo utilizzo. In realtà l'esempio precedente vuole solo dimostrare un uso curioso delle unioni.

 

 

Definire un tipo

 

Si usa typedef per creare un nuovo tipo. In realtà, typedef crea degli alias, ovvero, delle copie di tipi che quando vengono usate richiamano l'originale.

 

#define <stdio.h>

  

struct _PERSONA {

    char Nome[64], Cognome[64];

    int Eta;

    char Impiego[64], Stato[64], Citta[64], Via[64];

    int CodiceCivico;

};

  

typedef long long int64; // Tutte le volte che si userà int64 sarà

                         // come usare long long.

typedef struct _PERSONA PERSONA; // Tutte le volte che si userà

                                 // PERSONA sarà come usare struct

                                 // _PERSONA.

int main() {

    int64 NumeroPersone = 4;

    PERSONA Persone[NumeroPersone];

  

    // [...]

  

    return 0;

} 

 

 

Dichiarare una funzione

 

In C le funzioni sono dei blocchi di codice che eseguono determinate istruzioni. Si usano per evitare ripetizioni nel codice o per strutturarlo. Come le variabili, anche le funzioni possono avere un tipo (int, float) in base al valore che si vuol far restituire; una funzione di tipo void invece non restituisce alcun valore. return è un'istruzione che ci permette di uscire dalla funzione immediatamente restituendo il valore specificato. Questo può essere sfruttato, ad esempio, per verificare che la funzione si sia svolta con successo oppure per ottenere il risultato di un'operazione complessa.

 

void LaMiaFunzione() {

    // Istruzioni varie

    return;

}

 

Una funzione può ricevere un input, e in questo caso tra parentesi è necessario scrivere il tipo e il nome di ogni parametro. Tutte le volte che si richiamerà quella funzione sarà necessario specificare tutti i parametri tra parentesi.

 

void StampaInt(int variabile) {

    printf("%d", variabile);

 

    return;

}

int main() {

    int a, b, c, d;

    a = 0, b = ++a, c = (a++) + (b++);

    d = a + b * 2 + c * 3;

 

    StampaInt(d);

 

    return 0;

}

 

Esempio di una funzione che restituisce un valore (output):

 

double PotDiDue(double Base) {  

    return (Base * Base);

}

int main() {

    double a;

    a = 13.5;

 

    printf("%g", PotDiDue(a));

 

    return 0;

}

 

 

Variabili locali e globali

 

Fino ad ora abbiamo usato solo variabili locali, che solo quelle dichiarate all'interno di una funzione. Queste possono essere usate solo nella funzione o nell'ambito in cui sono state dichiarate. Le variabili globali invece vengono dichiarate fuori dalle funzioni, e possono essere usate in tutte le funzioni dichiarate successivamente.

 

#include <stdio.h>

 

int Globale; // Esempio di variabile globale

  

int main() {

    int Locale; // Esempio di variabile locale

  

    return 0;

}

 

 

 

I puntatori

 

I puntatori sono gli indirizzi in memoria che contengono il valore di una variabile. Conoscendo l'indirizzo della variabile è possibile assegnarle un valore. Ogni variabile, quando viene dichiarata, il suo valore viene salvato in un indirizzo. L'indirizzo può essere espresso sottoforma di un numero intero (ottale, decimale o esadecimale). Si dichiarano specificando un asterisco * prima del nome del puntatore e dopo aver specificato il tipo, che deve essere lo stesso della variabile a cui punta.

 

float f;   // Dichiara la variabile f

float *p;  // Dichiara il puntatore p

p = &f;    // Assegna l'indirizzo di f al puntatore p

*p = 4.0f; // Assegna il valore 4.0f all'indirizzo di p, quindi

           // assegna quel valore a f.

 

(& si usa per espandere l'indirizzo di una variabile, ovvero un puntatore ad essa)

Possiamo notare dunque che *p è il contrario di &p. &p ci dice l'indirizzo sapendo il nome della variabile, *p ci dice il valore sapendo l'indirizzo (che è p). Dovete comunque immaginare che con l'ultima assegnazione, p non cambia valore (l'indirizzo è lo stesso) mentre invece è f ad assumere il valore 4.0f.

 

E' possibile utilizzare i puntatori per permettere alle funzioni di cambiare valori puntati da un loro parametro. Esempio:

 

#include <stdio.h>

#include <math.h> // File d'intestazione per funzioni matematiche

 

typedef struct _PUNTO3D {

    double x, y, z;

} PUNTO3D;

 

int CalcolaDist(PUNTO3D p1, PUNTO3D p2, double *dOut) {

    double dx = p1.x - p2.x;

    double dy = p1.y - p2.y;

    double dz = p1.z - p2.z;

    *dOut = sqrt(dx*dx + dy*dy + dz*dz);

    // sqrt calcola la radice quadrata

    return 0;

}

 

int main()

{

    PUNTO3D a, b;

    double d;

 

    while (1) // 1 = vero

    {

        printf("Scrivere la coordinata X del primo punto: ");

        scanf("%lf", &a.x);

 

        printf("Scrivere la coordinata Y del primo punto: ");

        scanf("%lf", &a.y);

 

        printf("Scrivere la coordinata Z del primo punto: ");

        scanf("%lf", &a.z);

 

        printf("\nScrivere la coordinata X del secondo punto: ");

        scanf("%lf", &b.x);

 

        printf("Scrivere la coordinata Y del secondo punto: ");

        scanf("%lf", &b.y);

 

        printf("Scrivere la coordinata Z del secondo punto: ");

        scanf("%lf", &b.z);

 

        CalcolaDist(a, b, &d);

 

        printf("\nLa distanza tra il primo e il secondo punto è %lf\n\n", d);

    }

}

 

 

Variabili statiche

 

Notiamo che in una funzione se si dichiara una variabile, questa viene dichiarata tutte le volte che la funzione si ripete, e quindi anche l'eventuale assegnazione. Se non si vuole che ciò accada, sarebbe necessario per forza creare una variabile globale. Per fortuna, c'è l'istruzione static, che si posiziona prima di una dichiarazione (con eventuale assegnazione). Questo permette alla variabile di essere utilizzata sono all'interno di un determinato ambito, ma al tempo stesso viene trattata come una variabile globale.

 

#include <stdio.h>

  

int Incr(void) {

    static int i = 0; // Questa assegnazione avviene solo la prima volta

  

    printf("%d\n", i);

  

    return i++; // Il valore sarà mantenuto all'invocazione successiva

}

 

int main() {

    while (Incr() <= 100); 

    return 0;

}

 

Output:

 

0

1

2

3

...

100

 

Se non ci fosse stato static, l'esecuzione del programma non avrebbe avuto fine:

 

0

0

0

0

...

<--- Torna alla lezione 2   |   Passa alla lezione 4 --->