Benvenuti a questa ottava lezione del corso di programmazione C.
Arrivati a questo momento, sappiamo fare molte cose e potremmo già risolvere svariati problemi.
Oggi iniziamo a vedere alcuni argomenti che non sono direttamente connessi con la programmazione, ma possono sicuramente essere utili: le librerie.
In seguito vedremo le strutture dati, argomento che non conoscevamo la lezione scorsa e per questo non abbiamo messo la data nel file di output dell’esercizio relativo al ponte radio.
Sommario
1 Le librerie
Nelle ultime lezioni, abbiamo spesso usato due funzioni che ci aiutavano nel riservare memoria ed aprire i file.
Per ogni listato nuovo, è stato necessario, di volta in volta riportare per esteso queste funzioni. In generale però sebbene l’argomento di interesse fosse altro, è stato necessario comunque accludere questi file.
Se siamo sicuri che queste routine funzionino correttamente, una possibile soluzione potrebbe essere quella di metterle in particolari archivi chiamati librerie.
Ricordate quando abbiamo usato le funzioni matematiche? Qualcuno nel tempo le ha sviluppate e testate ed accluse al linguaggio C. Noi abbiamo solo dovuto includere un header e compilare con una particolare opzione, ma non abbiamo dovuto riportare il codice che implementava le funzioni che abbiamo usato. Adesso cercheremo di fare qualcosa di analogo: creeremo la nostra libreria di utilities.
Fig1 – Fasi per la generazione di un eseguibile a partire dai sorgenti C
Prima di tutto, in Fig1 sono state riportate le varie fasi necessarie per passare da un sorgente C ad un eseguibile. Fino ad ora non ce ne siamo mai resi conto perché il gcc faceva tutto nascondendo tutti questi passi. Del preprocessore ne avevamo parlato, adesso possiamo vedere anche il resto.
Della figura, la parte da mettere in evidenza è lo Step4 in cui si aggiungono le librerie che non fanno parte del codice sorgente. Sebbene siano stati indicati i nomi dei vari comandi che consentono di passare da una fase all’altra, in pratica sarà sempre il gcc che si occuperà di svolgere queste fasi invocando per noi cpp, as ed ld.
Di librerie in C ve ne sono di due tipi: le statiche (static) e le condivide (shared). Di seguito le analizzeremo, vedremo come si costruiscono e come si usano, dopo proveremo a confrontarle per vederne le loro caratteristiche.
1.a Librerie statiche (static library)
Il punto di partenza sono i sorgenti che si vogliono mettere nella libreria. Avevamo detto che si trattava delle due funzioni relative alla malloc ed alla fopen che per comodità riportiamo di seguito:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
/********************************************************** ** FILE *fopen_chk(char *fileName, char *mode) ** Scopo: ** Aprire un file eventualmente controllando, se lettura, ** se esiste e segnalando il problema ** Parametri di ingresso: ** fileName: ** Il nome del file da aprire ** mode: ** La modalità in cui il file deve essere aperto ** Parametri di uscita: ** fp: ** E' il puntatore al file aperto ** ** Autore: arkkimede ** Versione: 1.0 ** Maggio 2021 *********************************************************/ #include <utilities.h> FILE *fopen_chk(char *fileName, char *mode) { FILE *fp; char *outr, *outa, *outw; outr = strstr(mode,"r"); outa = strstr(mode,"a"); outw = strstr(mode,"w"); fp = fopen(fileName,mode); if((outr != NULL) || (outa != NULL)) { if(fp == NULL) { printf("Il file %s not esiste\n",fileName); printf("Esecuzione interrotta 1@fopen_chk\n"); exit(1); } } else if( outw != NULL ) { if(fp == NULL) { printf("Errore aprendo il file %s\n",fileName); printf("Esecuzione interrotta 2@fopen_chk\n"); exit(2); } } else { printf("Il file è stato aperto con un metodo sconosciuto\n"); printf("Esecuzione interrotta 3@fopen_chk\n"); exit(3); } return (fp); } |
—————————————
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
/********************************************************** ** malloc_chk(size_t size, char *funcName, int line) ** Scopo: ** Riservare un blocco di memoria, controllando ** se la funzione malloc restituisce un puntatore ** NULL. Qualora tale evento accadesse, viene segnalato ** il sorgente e la riga in cui il problema è accaduto. ** Parametri di ingresso: ** size: ** Rappresenta la quantità di memoria richiesta ** funcName: ** E' il nome del sorgente in cui è stata invocata ** la funzione ** line: ** E' la linea in cui è accaduto l'errore ** Parametri di uscita: ** ptr: ** E' un puntatore void che punta al blocco richiesto ** e che dovrà essere "castato" al tipo necessario ** ** Autore: arkkimede ** Versione: 1.0 ** Maggio 2021 *********************************************************/ #include <utilities.h> void *malloc_chk(size_t size, char *funcName, int line) { void *ptr; ptr = malloc(size); if(ptr == NULL) { printf("Errore nell'allocazione di memoria.\n"); printf("Quantità di meoria richiesta: %zu\n",size); printf("Sorgente dove è accaduto l'errore %s\n",funcName); printf("Lina del codice sorgente ove è accaduto l'errore: %d\n",line); printf("Esecuzione interrotta\n"); exit(1); } else return ptr; } |
—————————————
1 2 3 4 5 6 7 8 9 10 |
#ifndef UTILITIES_ #define UTILITIES_ #include <stdio.h> #include <stdlib.h> #include <string.h> FILE *fopen_chk(char *fileName, char *mode); void *malloc_chk(size_t size, char *funcName, int line); #endif |
- Abbiamo ripreso la buona norma di commentare il codice che ultimamente avevamo abbandonato
- E’ presente un header che contiene la dichiarazione delle due funzioni ed i .h necessari alle istruzioni conenute nei due sorgenti
- In utilities.h è presente un costrutto #ifndef..#define..#endif. Lo scopo dell’utilizzo di queste istruzioni apparirà più chiaro quando vederemo come separare tutto il sorgente in tanti piccoli file. Serve in sostanza ad evitare di includere più volte il medesimo header, causando il problema della ri-dichiarazione delle medesime funzioni o variabili. Questo può accadere, come detto, quando si usano tanti piccoli sorgenti che per esempio devono aprire file o riservare memoria ed ognuno di essi include il medesimo .h, causando appunto la ri-dichiarazione multipla delle medesime funzioni.
Per costruire la libreria prima di tutto è necessario ottenere i file .o
Mandare in esecuzione allora i seguenti comandi:
gcc -c -Wall -O3 -I . fopen_chk.c
gcc -c -Wall -O3 -I . malloc_chk.c
- -c comunica al compilatore che deve limitarsi a creare il file .o
- -Wall abilita tutti i messaggi di warning
- -O3 attua tutti i livelli di ottimizzazione
- -I . indica di cercare gli eventuali file di header anche nella directory corrente
A questo punto dovreste avere nella cartella in cui siete fopen_chk.o e malloc_chk.o .
L’ultimo passo per ottenere la nostra libreria adesso è:
ar -rcs libutilities.a fopen_chk.o malloc_chk.o
- ar è il comando che crea la libreria
- r comunica di inserire tutti i .o nella libreria ed eventualmente sostituire quelli vecchi con i nuovi
- c dice di costruire la libreria se non esiste già
- s la indicizza, per consentire una compilazione molto più efficiente.
Se volessimo conoscere i moduli che sono stati inseriti nella libreria, sarebbe sufficiente eseguite il comando
ar -t libutilitis.a
e si otterrebbe la lista delle funzioni contenute.
A questo punto vediamo come usare la nostra libutilities.a. Per questo scopo consideriamo il sorgente esempio5_6.c, dove per la prima volta è stata usata la malloc_chk. Modifichiamo il sorgente come mostrato in seguito:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
#include <stdio.h> #include <stdlib.h> #include <utilities.h> #define GRUPPI_VEICOLI 3 #define ERROR_ID 1 #define ERROR_RUOTE 2 #define ERROR_ALTEZZA 3 #define ERROR_COSTO 4 int main() { int *ruoteVeicoli; float *altezzaVeicoli; char *idVeicoli; double *costoVeicoli; idVeicoli = (char *)malloc_chk(sizeof(char) * GRUPPI_VEICOLI, __FILE__, __LINE__); idVeicoli[0] = 8; idVeicoli[1] = 13; idVeicoli[2] = 17; ruoteVeicoli = (int *)malloc_chk(sizeof(int) * GRUPPI_VEICOLI, __FILE__, __LINE__); ruoteVeicoli[0] = 2; ruoteVeicoli[1] = 3; ruoteVeicoli[2] = 4; altezzaVeicoli = (float *)malloc_chk(sizeof(float) * GRUPPI_VEICOLI, __FILE__, __LINE__); altezzaVeicoli[0] = 1.; altezzaVeicoli[1] = 1.3; altezzaVeicoli[2] = 1.8; costoVeicoli = (double *)malloc_chk(sizeof(double) * GRUPPI_VEICOLI, __FILE__, __LINE__); costoVeicoli[0] = 1500.; costoVeicoli[1] = 1900.; costoVeicoli[2] = 15000.; printf("I %d gruppi di veicoli hanno le seguenti caratteristiche:\n",GRUPPI_VEICOLI); for (int i=0; i<GRUPPI_VEICOLI; i++) { printf("id: %2d ",idVeicoli[i]); printf("numero di ruote %d ",ruoteVeicoli[i]); printf("altezza pari a %3.2f m ",altezzaVeicoli[i]); printf("costo pari a %5.0lf €\n",costoVeicoli[i]); } free(idVeicoli); free(ruoteVeicoli); free(altezzaVeicoli); free(costoVeicoli); return 0; } |
Quindi abbiamo eliminato la funzione ed incluso utilities.h
Adesso per ottenere l’eseguibili è sufficiente eseguire il comando:
gcc esempio5_6.c -I . -L . -lutilities -o esempio5_6
- -I . indica di cercare eventuale .h non trovato anche nella cartella corrente
- -L . simile al precedente indica di cercare un’eventuale libreria in locale
- -lutilities è la notazione standard per includere una libreria
- -o indica il nome dell’eseguibile
Tutto ciò però implica che sia la libreria sia utilities.h dobbiamo copiarle sulla nostra macchina ovunque vogliamo usarla. Un’alternativa è quella di usare la filosofia utilizzata dal gcc (o qualsiasi altro compilatore) quando compila un sorgente: esistono infatti delle cartelle standard in cui vengono cercati header e librerie se non trovate. A tal proposito esistono le directory /usr/local/include ed /usr/local/lib. Nella prima si può mettere utilities.h e nella seconda libutilities.a, ricordandosi che essendo cartelle di sistema bisognerà essere root o nel caso del Raspberry Pi usare sudo ossia:
sudo mv utilities.h /usr/local/include
sudo mv libutilities.a /usr/local/lib
A questo punto il comando di compilazione diventa:
gcc esempio6_5.c -lutilites -o esempio6_5
e tutte le volte che vorremo utilizzare quelle funzioni non dovremo che aggiungere al sorgente utilities.h e compilare come mostrato. Come confronto futuro verifichiamo che l’eseguibile (sulla Raspberry Pi) è 8236 byte
1.b Le librerie condivide (shared library)
Si chiamano condivise ma potrebbero anche essere chiamate dinamiche infatti, queste non fanno parte dell’eseguibile come nel caso delle statiche, ma sono caricate a run time. Verificheremo infatti che l’eseguibile compilato con una libreria statica è più grande di quello compilato con una libreria condivisa.
Il punto di partenza è il medesimo e necessita anche qui utilities.h e pur se non presente in locale, il compilatore lo troverà in una delle cartelle canoniche. I file .o questa volta si ottengono in modo leggermente differente ossia:
gcc -c -Wall -O3 -fPIC fopen_chk.c
gcc -c -Wall -O3 -fPIC malloc_chk.c
Rispetto alla versione statica adesso c’è l’opzione -fPIC che è l’acronimo inglese Position Indipendent Code. Questo significa che verrà creato del codice che utilizzerà degli indirizzi relativi, piuttosto che assoluti, in quanto la libreria, potrebbe essere caricata varie volte ed in condizioni differenti, quindi necessita di questa flessibilità.
Fatto ciò, per creare la shared library dobbiamo eseguire il comando
gcc -Wall -O3 -shared -o libsutilities.so fopen_chk.o malloc_chk.o
Per distinguerla da quella precedentemente creata, abbiamo modificato il nome in libsutilities.so
Supponiamo di sposare anche questa in /usr/local/lib e proviamo a compilare esempio5_6.c:
gcc esempio5_6.c -lsutilities -o esempio5_6
(se avessimo mantenuto la libreria in locale, avremmo dovuto anche usare l’opzione -L .).
A questo punto se mandiamo in esecuzione l’eseguibile otteniamo il seguente errore:
./esempio5_6: error while loading shared libraries: libsutilities.so: cannot open shared object file: No such file or directory
questo perché le librerie condivise dipendono dalla variabile di ambiente LD_LIBRARY_PATH.
Per risolvere il problema basta aggiungere, nella home directory al file .bashrc la seguente istruzione:
export LD_LIBRARY_PATH=/usr/local/lib
A questo punto mandando in esecuzione tale file con:
. .bashrc
(se testiamo il contenuto della variabile d’ambiente
echo $LD_LIBRARY_PATH
si ottiene come risposta
/usr/local/lib)
A questo punto mandando in esecuzione l’eseguibile esempio5_6 questo girerà tranquillamente. Questo eseguibile è grande 8140 byte leggermente più piccolo di quello ottenuto con la libreria statica (le funzioni sono solo 2 il che giustifica la minima differenza; nel caso di un’intera libreria contenente centinaia di funzioni, le differenze possono essere notevoli).
1c Confronto tra Librerie statiche e condivise
Proviamo a riportare per sommi capi le caratteristiche di entrambe
STATICHE
- Funzioni e variabili sono risolte durante la compilazione e copiate dal compilatore nell’eseguibile.
- Questo è in grado di girare da solo, non necessita di altro
- Storicamente le librerie erano solo statiche
- La congiunzione tra programma e libreria effettuata dal linker
- Sebbene l’eseguibile risultante sia più veloce (rispetto all’altro caso), bisogna pagare il prezzo di caricare tutto il programma in memoria e questo tempo potrebbe essere considerevole nel caso di grossi progetti
- Se la libreria è cambiata, per ottenere l’eseguibile nella nuova versione, bisogna ricompilare
CONDIVISE
- Funzioni e variabili sono risolte a run time aggiungendo al codice solo il loro indirizzo
- Queste funzioni sono in una particolare parte della memoria e tutti i programmi vi possono accedere senza avere delle duplicazioni
- In questo caso la congiunzione tra programma e librerie è effettuata dal sistema operativo
- Le dimensioni dell’eseguibile sono ridotte in quanto non comprendono le librerie
- La velocità di esecuzione è leggermente inferiore rispetto al caso precedente (non è un unico blocco monolitico come nel caso precedente) ma non c’è la perdita di tempo nel caricare il codice in memoria perché le librerie condivise sono già in memoria
- Se si cambia la libreria, non sarà necessario ricompilare il codice per ottenerne una versione aggiornata.
1d Piccola digressione linguistica
Causa la predominanza in questo campo del mondo americano, i termini utilizzati in italiano, sono stati un po’ snaturati.
In inglese Library coincide con la nostra biblioteca mentre Book shop è la nostra libreria.
Ciò nonostante continuiamo a chiamare Librerie quelle che invece dovremmo chiamare Biblioteche: pazienza 🙂
2 Le struct
Le struct sono collezioni di variabili raggruppate sotto lo stesso nome. Questa condizione a volte può essere molto utile e semplifica la vita dei programmatori.
Nell’esempio seguente ne vediamo un esempio
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
/*********************** esempio8_1.c Utilizzo delle struct arkkimede maggio 2021 ***********************/ #include <stdio.h> #include <stdlib.h> #define STR_LEN 512 struct Alunno { char nome[STR_LEN]; char cognome[STR_LEN]; int eta; int classe; // 1,2,3,4 e 5 float mediaScolastica; }; int main() { struct Alunno uno,due,tre; sprintf(uno.nome,"Giovanni"); sprintf(uno.cognome,"Verdi"); uno.eta = 8; uno.classe = 2; uno.mediaScolastica = 7.35; sprintf(due.nome,"Mario"); sprintf(due.cognome,"Rossi"); due.eta = 9; due.classe = 3; due.mediaScolastica = 8.35; sprintf(tre.nome,"Angelo"); sprintf(tre.cognome,"Bianchi"); tre.eta = 10; tre.classe = 4; tre.mediaScolastica = 6.12; printf("Le caratteristiche dello studente uno sono:\n"); printf("Nome: %s\n",uno.nome); printf("Cognome: %s\n",uno.cognome); printf("Età: %d\n",uno.eta); printf("Classe: %d\n",uno.classe); printf("Media: %.2f\n",uno.mediaScolastica); printf("=======================\n\n"); printf("Le caratteristiche dello studente due sono:\n"); printf("Nome: %s\n",due.nome); printf("Cognome: %s\n",due.cognome); printf("Età: %d\n",due.eta); printf("Classe: %d\n",due.classe); printf("Media: %.2f\n",due.mediaScolastica); printf("=======================\n\n"); printf("Le caratteristiche dello studente tre sono:\n"); printf("Nome: %s\n",tre.nome); printf("Cognome: %s\n",tre.cognome); printf("Età: %d\n",tre.eta); printf("Classe: %d\n",tre.classe); printf("Media: %.2f\n",tre.mediaScolastica); printf("=======================\n\n"); return 0; } /************************** Output: Le caratteristiche dello studente uno sono: Nome: Giovanni Cognome: Verdi Età: 8 Classe: 2 Media: 7.35 ======================= Le caratteristiche dello studente due sono: Nome: Mario Cognome: Rossi Età: 9 Classe: 3 Media: 8.35 ======================= Le caratteristiche dello studente tre sono: Nome: Angelo Cognome: Bianchi Età: 10 Classe: 4 Media: 6.12 ======================= **************************/ |
- Prima si dichiara la struct e si associa un nome
- Tra parentesi graffe si inseriscono le variabili (dopo la graffa chiusa ricordarsi il punto e virgola)
- Poi si usa struct nomeStruct come un nuovo tipo (come fosse int o float) e si dichiarano delle variabili
- Per accedere ai vari campi della struct, si usa il nome della variabile più il punto ed il campo desiderato
Volendo, tramite l’istruzione typedef, è possibile proprio definire un nuovo tipo e si potrà evitare di usare la parola chiave struct della dichiarazione. Vediamo un esempio…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
/*********************** esempio8_2.c Utilizzo di typedef e struct arkkimede maggio 2021 ***********************/ #include <stdio.h> #include <stdlib.h> #include <math.h> #define SQ( x ) ( (x) * (x) ) typedef struct { float x; float y; float z; }Punto3d; typedef struct { Punto3d a; Punto3d b; float lunghezza; }Segmento; int main() { Punto3d p1, p2; Punto3d p3, p4; Segmento s1, s2; p1.x = 2.3; p1.y = 1.5; p1.z = 0.6; p2.x = 3.5; p2.y = 4.2; p2.z = -3.5; p3.x = -0.6; p3.y = -1.9; p3.z = 6.2; p4.x = 0.01; p4.y = 4.7; p4.z = -1.2; s1.a = p1; s1.b = p2; s2.a = p3; s2.b = p4; s1.lunghezza = sqrtf( SQ(s1.a.x - s1.b.x) + SQ(s1.a.y - s1.b.y) + SQ(s1.a.z - s1.b.z) ); s2.lunghezza = sqrtf( SQ(s2.a.x - s2.b.x) + SQ(s2.a.y - s2.b.y) + SQ(s2.a.z - s2.b.z) ); printf("Lunghezza di s1 = %lf \n",s1.lunghezza); printf("Lunghezza di s2 = %lf \n",s2.lunghezza); return 0; } /************************** Output: Lunghezza di s1 = 5.053711 Lunghezza di s2 = 9.934389 **************************/ |
- Sono stati definiti due nuovi tipi: Punto3d e Segmento
- Due campi di segmento sono costituiti da Punto3d
- Abbiamo usato la radice quadrata (sqrtf) quindi abbiamo usato l’header math.h e compilato con l’opzione -lm
- Abbiamo definito una macro SQ per calcolare il quadrato di un numero
Nella lezione dei File avevamo omesso di mettere la data anticipando che era un argomento legato alle strutture: vediamo allora come si può ottenere la data in un programma C.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
/************************** ** esenpio8_3.c ** ** Come calcolare la data ** arkkimede ** maggio 2021 ***************************/ // Introduciamo la struttura tm e tutti i suoi campi // struct tm { // int tm_sec; /* secondi, da 0 a 59 */ // int tm_min; /* minuti, da 0 a 59 */ // int tm_hour; /* ore, da 0 a 23 */ // int tm_mday; /* giorno del mese, da 1 a 31 */ // int tm_mon; /* mese, da 0 a 11 */ // int tm_year; /* anno, a partire dal 1900 */ // int tm_wday; /* giorno della settimana, da 0 a 6 */ // int tm_yday; /* giorno dell'anno, da 0 a 365 */ // int tm_isdst; /* eventuale ora legale */ // }; #include <stdio.h> #include <time.h> int main() { time_t t; struct tm tm; /* L'istruzione seguente restituisce il numero di secondi trascorsi dalla sequente momento: 1970-01-01 00:00:00 +0000 (UTC) */ t = time(NULL); /* L'istruzione localtime trasforma i secondi calcolati precedentemente nei vari campi della struttura tm */ tm = *localtime(&t); /* Stampiamo tutti i campi della struttura */ printf("Anno = %d \n",tm.tm_year + 1900); printf("Mese = %d \n",tm.tm_mon + 1); printf("Giorno = %d \n",tm.tm_mday); printf("Ore = %d \n",tm.tm_hour); printf("Minuti = %d \n",tm.tm_min); printf("Secondi = %d \n",tm.tm_sec); printf("Giorno settimana = %d \n",tm.tm_wday+1); printf("Giorno anno = %d \n",tm.tm_yday+1); printf("Ora legale = %d \n",tm.tm_isdst); return 0; } /********************* ** Output fornirà i dati relativi a quando ** questo codice girerà ** Adesso i dati sono: ** Anno = 2021 ** Mese = 5 ** Giorno = 22 ** Ore = 22 ** Minuti = 45 ** Secondi = 24 ** Giorno settimana = 7 ** Giorno anno = 142 ** Ora legale = 1 ****************************/ |
Osserviamo gli aspetti salienti
- Come riportato nei vari campi della struttura, per ottenere l’anno corrente è necessario aggiungere 1900
- Ai giorni che partono a 0 è stato aggiunto 1
- L’ora legale è da intendere come un booleano per cui 1 è vero 0 no.
3 Riassunto
- Sono state riportate le varie fasi della compilazione e generazione di un eseguibile
- Sono state introdotte le librerie statiche e condivise
- Abbiamo visto il vantaggio di utilizzare librerie, ossia utilizzare funzioni consolidate senza introdurle ogni volta nel codice
- Sono state mostrate le procedure e le configurazioni necessarie per costruire sia le librerie statiche sia le condivise
- Sono stati indicati, a livello di filesystem, dove vanno collocate queste librerie
- Si è visto come compilare con le librerie esempi presi da lezioni precedenti
- Sono state introdotte le strutture e mostrate come usarle
- Infine abbiamo visto la struct tm e come usarla per ottenere la data corrente ed altre informazioni
Qui finisce anche questa ottava lezione.
Se ne avete voglia, riprendete l’esercizio proposto nella lezione precedente ed al file dei risultati aggiungete la data.
Qui il forum di supporto al corso.
Se vuoi restare aggiornato, seguici anche sui nostri social: Facebook, Twitter, Youtube
Se vuoi anche trovare prodotti e accessori Raspberry Pi in offerta, seguici anche su Telegram !!
Ricordate che la prossima lezione sarà pubblicata il prossimo 8 giugno