La quinta lezione del corso avanzato sul linguaggio C verte sulla creazione di librerie, contenenti le proprie funzioni da richiamare all'occorrenza, senza doverle riscrivere da capo. Con tale possibilità è possibile modularizzare al massimo la codifica e consentire ad altre persone di utilizzare le proprie librerie anche senza la necessità di visionare praticamente il codice.
Una scatola chiusa
Una libreria è un insieme di funzioni scritte, in questo caso, in linguaggio C. Esse sono utilizzate quando delle particolari funzioni sono richiamate spesso nei propri programmi. Il linguaggio C dispone già di esse per gli usi più generali, tuttavia il programmatore può crearne in aggiunta, in modo da standardizzare le procedure. In questa maniera egli non si deve più preoccupare più di preparare una determinata procedura o funzione, ma si dedicherà solo al suo utilizzo.
Ovviamente, se la libreria contiene errori o bug, si pone davanti a esso una drammatica situazione che deve cercare di risolvere nel minor tempo possibile, specialmente se le sue applicazioni sono distribuite a un vasto pubblico di utenti.
Programmi e librerie
Creare una libreria è molto differente dal realizzare programmi e software. La libreria, infatti, non contiene intere applicazioni ma semplicemente una o più funzioni che risolvono un determinato problema. Tali funzioni possono anche essere estremamente complesse e più lunghe, in termini di codice, di un normale programma. E' possibile, dunque, distribuire la propria libreria ai programmatori, che la possono utilizzare, sicuramente con tanto vantaggio, per le proprie applicazioni.
Convenzioni
Al fine di mantenere vivo lo standard procedurale occorre seguire alcune regole nell'attribuire il giusto nome a una libreria:
- il nome di una libreria deve sempre iniziare con "lib";
- se il nome termina per ".a" vuol dire che si intende utilizzare una libreria statica, come vuole la tradizione. Il codice eseguibile la conterrà per effetto del linking statico;
- se il nome termina per ".so" vuol dire che si intende utilizzare una libreria dinamica.
Iniziamo subito con i Numeri Primi
Prima di creare una libreria è opportuno verificare che le funzioni che saranno in essa contenute funzionino bene e siano a prova di bomba. Realizziamo, dunque, una funzione che restituisce "1" se il parametro a essa passato è un numero primo, altrimenti "0" se si tratta di un numero composto. La seguente codifica, dunque, non utilizza librerie personalizzate e la procedura di sviluppo e di compilazione è quella standard, ossia programma e funzione contenuti in un unico file sorgente..
#include "stdio.h" #include "math.h" int isprime(unsigned long n); int main() { unsigned long n; n=23; printf("Primalita' del numero %ld: %d\n",n,isprime(n)); n=500009; printf("Primalita' del numero %ld: %d\n",n,isprime(n)); n=9876543; printf("Primalita' del numero %ld: %d\n",n,isprime(n)); n=9876553; printf("Primalita' del numero %ld: %d\n",n,isprime(n)); return 0; } int isprime(unsigned long n) { int test=1; unsigned long k,limite; limite=sqrt(n); for(k=2;k<=limite;k++) if(n % k == 0) { test=0; break; } return test; }
La funzione "isprime" accetta un parametro numerico del quale si vuole conoscere la primalità. Un ciclo con limite pari alla sua radice quadrata cerca di trovare eventuali divisori. Prima della funzione "main()" è riportato il prototipo della funzione "isprime()", in modo da avvertire il compilatore della sua presenza. Come vedremo dopo, con l'utilizzo di una libreria, tale incombenza non sarà più necessaria. A seconda dell'esito, la variabile "test" conterrà il valore "1" per informare che il parametro è un numero primo, oppure "0" per attestare che esso possiede almeno un divisore ed è, pertanto, composto. Per effettuare la compilazione del programma è necessario scrivere il codice in un file di testo, con suffisso finale ".c" e procedere alla compilazione con il seguente comando da terminale:
$ sudo gcc -Wall primi.c -lm
L'opzione -lm avverte il compilatore di cercare le librerie matematiche. L'esecuzione del file binario, invece, è possibile grazie a questo altro comando:
$ sudo ./a.out
che produrrà, a video, la schermata di cui alla figura 1.
Creiamo una libreria statica
Constatata la bontà del funzionamento della funzione UDF "isprime()", è possibile passare direttamente alla creazione della libreria, in modo da non dover riscrivere ogni volta, nei nostri sorgenti, il suo codice. Il contenuto della libreria è incluso, a ogni compilazione, anche nel programma eseguibile, per cui non occorre distribuirla all'utente finale. Se si compilano, ad esempio, 50 applicazioni con l'uso della libreria statica, essi conterranno anche la stessa copia di libreria, producendo degli eseguibili molto più pesanti e di grandi dimensioni. Per lo scopo si dovranno seguire i seguenti passi.
Creazione del file header
Si tratta di un file ".h" nel quale sono riportati tutti i prototipi delle funzioni da noi create. Non si dovranno, dunque, ripetere nel sorgente principale. Il nostro file header, nell'esempio, è stato memorizzato con il nome "primalita.h" e ha il seguente contenuto:
/* Libreria per la gestione dei Numeri Primi by Giovanni Di Maria */ int isprime(unsigned long n); int nextprime(unsigned long n); int prevprime(unsigned long n); int isprobabprime(unsigned long n);
Creazione del sorgente di libreria
Si crei, quindi, il file sorgente che conterrà esclusivamente le funzioni UDF da usare nei nostri programmi. Quello dell'esempio si chiama "miefunzioni.c". Il contenuto è il seguente:
#include "math.h" int isprime(unsigned long n) { int test=1; unsigned long k,limite; limite=sqrt(n); for(k=2;k<=limite;k++) if(n % k == 0) { test=0; break; } return test; }
Creazione del sorgente del programma generale
Si tratta del listato generale, che è uguale a quello del precedente capitolo ma senza, ovviamente, la codifica della funzione UDF. Il suo nome è "primi.c" e il suo contenuto è sotto riportato. Come si nota, all'inizio del listato c'è la chiamata al file d'intestazione con il comando di preprocessore #include "primalita.h".
#include "stdio.h" #include "primalita.h" int main() { unsigned long n; n=23; printf("Primalita' del numero %ld: %d\n",n,isprime(n)); n=500009; printf("Primalita' del numero %ld: %d\n",n,isprime(n)); n=9876543; printf("Primalita' del numero %ld: %d\n",n,isprime(n)); n=9876553; printf("Primalita' del numero %ld: %d\n",n,isprime(n)); return 0; }
Eseguiamo la sola compilazione del sorgente di libreria
Tale procedura non crea alcun file eseguibile ma "si limita" alla produzione di un file oggetto, utile nelle successive operazioni. A tale scopo si invochi il comando "gcc" con l'opzione "-c", come evidenziato nel comando di terminale sotto riportato:
$ sudo gcc -Wall -c miefunzioni.c
La compilazione produrrà, in output, il file miefunzioni.o, come visibile anche in figura 2.
[...]
ATTENZIONE: quello che hai appena letto è solo un estratto, l'Articolo Tecnico completo è composto da ben 2226 parole ed è riservato agli ABBONATI. Con l'Abbonamento avrai anche accesso a tutti gli altri Articoli Tecnici che potrai leggere in formato PDF per un anno. ABBONATI ORA, è semplice e sicuro.
Spesso si segue anche questo metodo per non far visionare il codice sorgente di alcuni algoritmi particolari agli utilizzatori. Si distribuisce la libreria binaria e il listato è celato. Anche se….. esiste il Reverse Engineering…….
Egregio Dr. De Maria, scusi, sono un principiante hobbista di 80 anni (nato nel 1938). Ho letto il suo articolo, mi è piaciuto. Complimenti.
Volevo farmi costruire un piccolo computer con i Raspberry (almeno 8) per avere una capacità di calcolo maggiore del mio PC con Intel 7 e 16 gTT+1 6 giga di memoria. Mi diletto di ricerche di sorting su stringhe numeriche per studi statistici (NO BITCOIN !!). Un mio vecchio programma gira molto lento, per avere una risposta impiega spesso 15… 20 minuti.. perciò vado alla ricerca di un modo più veloce. Mi può dare un’idea? Grazie Villamaina Giuseppe 081628864- da poco abbonato a EMC Dove posso trovare qualcosa su come costruire questo PC con Raspberry, oppure un’altra soluzione ? GRAZIE E BUON ANNO !!!
Salve Egr. Sig. Villamaina.
Buon anno.
Grazie dei complimenti
La sua ricerca e’ molto interessante e anche io
eseguo diverse ricerche, sia su eventi numerici che altro.
Per eseguire ottime ricerce in tempi computazionali brevi, non
sempre la potenza del PC e’ sufficiente ma occorre che l’Algoritmo
sia molto efficiente.
In altri termini, se la strategia di ricerca e’ buona, probabilmente
sarebbe piu’ veloce su un vecchio processore 8086 che non su
un normale algoritmo a “forza bruta” in un 20 cores.
Un altro punto da considerare e’ il linguaggio di programmazione
da adottare. E’ meglio usare un linguaggio molto veloce, come
il linguaggio C, il D o l’assembler. Da questi e’ possibile
ottenere performaces anche del 1000%-10000% rispetto ad altri
linguaggi ad alto livello.
Adesso Lei mi chiede un consiglio…. non conoscendo la sua ricerca
e il nocciolo delle Sue sperimentazioni, non sapri proprio
cosa consigliarLe, se vuole puo’ scrivermi alla email
[email protected]
Cordiali saluti e complimenti per la sua esperienza.
Giovanni