[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 --->