[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}; // Non funziona con tutti i compilatori

  

    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 memora. Un esempio per chiarire:

 

#include <stdio.h>

 

union CONVERTER { // Si usa union per creare unioni

    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.

 

 

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 . E' simile a un #define, ma vale solo per i tipi e viene li crea soltanto durante l'esecuzione.

 

#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 semplicemente per renderlo ordinato (e come i cicli, rendono l'esecuzione più veloce), sarebbe poi impensabile fare un programma di diecimila righe tutto nella funzione main. Come le variabili, anche le funzioni possono avere un tipo (int, float), una funzione di tipo void invece non ritorna alcun valore. return è un'istruzione che ci permette di uscire dalla funzione immediatamente, ma il più delle volte viene usato per ritornare un valore (per verificare che la funzione si sia svolta con successo e/o 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;

}

 

Una funzione può anche ritornare 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 in cui sono state dichiarate. Le variabili globali invece vengono dichiarate prima delle funzioni, e possono essere usate in tutte le funzioni.

 

#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. Sapendo l'indirizzo della variabile, anche se non si sa il suo nome, è 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)

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 assume alcun valore, mentre invece è f ad assumere il valore 4.0f.

 

E' possibile utilizzare i puntatori nelle funzioni invece di ritornare un valore. Possiamo notare che il puntatore viene utilizzando come input ma anche per dare l'output. Esempio:

 

#include <stdio.h>

#include <math.h> // Header per funzioni matematiche

 

typedef struct _PUNTO3D {

    double x, y, z;

} PUNTO3D;

 

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

    *dOut = pow(pow(p1.x - p2.x, 2.0) + pow(p1.y - p2.y, 2.0) + pow(p1.z - p2.z, 2.0), 0.5); // pow() calcola una potenza

    return 0;

}

 

int main() {

    PUNTO3D a, b;

    double d;

 

    while (1) { // 1 = vero, quindi la condizione è sempre vera.

        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), e fa in modo che ciò venga eseguita solo la prima volta che la funzione viene richiamata.

 

#include <stdio.h>

  

int Incr(void) {

    static int i = -1;

  

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

  

    return 0;

}

 

int main() {

    while (1) Incr(); 

    return 0;

}

 

Output:

 

0

1

2

3

...

 

Se non ci fosse stato static:

 

0

0

0

0

...

 

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