Meglio BASIC o C?

I due linguaggi per la programmazione più diffusi al mondo sono il Basic ed il C. Esaminiamo pregi e difetti di entrambi al fine di scegliere ed utilizzare quello più adatto alle proprie esigenze.

Non esiste un linguaggio di programmazione “perfetto” in assoluto, quello che elimina tutto i problemi legati alla velocità, criticità e semplicità di un progetto. Ciò che regala un linguaggio di programmazione non è presente in un altro. In parole povere non c’è un linguaggio di programmazione più potente al mondo che può sostituirne un altro. L’articolo mostra vantaggi e svantaggi di entrambi mettendo a fuoco limiti o accezioni lasciando poi al lettore la libertà di scegliere con cognizione di causa e con le idee chiare.

Figura 1: schema elettrico della prova di velocità (prova numero 1).

Figura 1: schema elettrico della prova di velocità (prova numero 1).

Il Basic

Il BASIC (Beginner’s All-purpose  Symbolic Instruction Code) è un linguaggio potente usato come strumento per i programmatori principianti, che è stato sviluppato a Dartmouth College nel 1964, sotto la direzione di J. Kemeny e T. Kurtz. Venne implementato con lo scopo di fornire un linguaggio veramente molto semplice da imparare e facile da tradurre. Per ulteriori informazioni sul Basic si può consultare Internet.

Figura 2: grafico del tempo impiegato (prova numero 1).

Figura 2: grafico del tempo impiegato (prova numero 1).

Il C

Fu ideato nei Bell Laboratories della AT&T nel 1972 da Dennis Ritchie come evoluzione del linguaggio B di Ken Thompson usato per la scrittura dei primi sistemi operativi UNIX. Lo stesso Thompson nel 1970 si era a sua volta ispirato al linguaggio BCPL di Martin Richards, anch’esso pensato per scrivere sistemi operativi e software di sistema. La definizione formale si ha nel 1978 a cura di B. W. Kernighan e D. M. Ritchie. Nel 1983 iniziò il lavoro di definizione di uno standard da parte dell’American National Standards Institute, che rilasciò nel 1990 lo Standard ANSI C (ISO C89). Il C è tecnicamente un linguaggio di programmazione ad alto livello. Tuttavia, poiché esso mantiene evidenti relazioni semantiche con il  linguaggio macchina e l’assembler, risulta molto meno astratto. Per questo motivo, talvolta viene anche identificato con la locuzione di linguaggio di medio livello, se non addirittura (in modo certamente improprio) come macro-assembly, o assembly portabile.  Il C è diffuso per la sua efficienza, e si è imposto come linguaggio di riferimento per la realizzazione di software di sistema su gran parte delle piattaforme hardware moderne. La standardizzazione del linguaggio (da parte dell’ANSI prima e dell’ISO poi) garantisce la portabilità dei programmi scritti in C (standard, spesso detto ANSI C) su qualsiasi piattaforma. Oltre che per il software di sistema,  il C è stato a lungo il linguaggio dominante in tutta una serie di altri domini applicativi nei quali era richiesta alta efficienza. Esempi tipici sono le telecomunicazioni, il controllo di processi industriali, il software real-time e, nel nostro specifico, la scrittura e la gestione di sistemi per microcontrollori.  Il linguaggio C (come qualsiasi altro linguaggio di programmazione) consente la realizzazione di programmi adoperando un insieme di “parole chiave” (keywords). Lo standard ANSI ha definito il seguente insieme di parole chiave: asm, break, case, catch, char, continue, default, delete, do, double, else, extern, float, for, goto, if, inline, int, long, register, return, short, signed, sizeof, static, struct, switch, typedef, union, unsigned, void, la maggior parte delle quali sono utilizzate nei compilatori per MCU.

Figura 3: spazio memoria occupata (prova numero 1).

Figura 3: spazio memoria occupata (prova numero 1).

Paragoniamo i linguaggi

Come ogni cosa esistono i sostenitori  di una corrente e quella opposta. Nel nostro caso non possiamo parlare di vera e propria opposizione di linguaggi, ma fondamentalmente essi sono abbastanza dissimili specialmente  nella programmazione e produzione di eseguibili per Personal Computer. La differenza invece nelle applicazioni per MCU è molto meno contrastata e, spesso, un linguaggio vale l’altro. L’articolo non effettuerà una comparazione netta tra i due linguaggi, al fine di poter sentenziare il migliore ed il peggiore. Al contrario esaminerà  i pregi e difetti di ciascuno di essi, prendendo a misura vari parametri importanti e fornendo utili consigli. Sarà infine il lettore a tirare le somme e scegliere, anche in base alle proprie conoscenze ed esigenze , il linguaggio  che fa per lui.

Figura 4: schema elettrico (prova numero 2).

Figura 4: schema elettrico (prova numero 2).

Esaminiamo il Basic

Prendiamo adesso spunto da quanto detto prima e andiamo ad esaminare le proprietà generali e caratteristiche di questo semplice ma potente linguaggio, in maniera molto generale. E’ indubbia la facilità di apprendimento e la semplicità con cui si scrivono i programmi con tale linguaggio. In fondo esso è nato proprio con lo scopo di essere facile ed orientato ai principianti. Naturalmente, e parliamo in senso generale, ogni rosa ha le sue spine. Fondamentalmente l’eseguibile creato con il Basic è più lento di quello creato con il C. Anche le sue dimensioni fisiche (occupazione in memoria di massa o Flash) sono solitamente più grosse. Questo perché il compilatore di solito inserisce, nel codice generato, il suo run-time e ciò contribuisce ad aumentare ancora di più lo spazio utilizzato. Nel caso di processori con limitata memoria, come il Pic 16f84, questo fatto potrebbe costituire qualche problema. Naturalmente la colpa di tutto questo non è da ricercare nel linguaggio bensì nel compilatore utilizzato. Un cattivo compilatore potrebbe far deprezzare al massimo le eccellenti caratteristiche di un linguaggio. Quindi scegliete sempre un buon prodotto, considerando la sua efficienza. Il Basic, si sa, è meno potente del C, ma risulta molto semplice da utilizzare in quanto la sua semantica è molto vicina a quella dell’uomo. Costituisce quindi il classico linguaggio ad “alto livello” che ben si interfaccia al lessico umano di tutti i giorni. Solitamente  il basic è utile per la gestione e la creazione di applicazioni medio-piccole. Se questa è destinata a crescere a dismisura il Basic potrebbe non essere sufficiente a gestire l’intero progetto. In questo senso è infatti utile parlare di riuso del codice, oppure di rilocazione, per le quali esistono linguaggi di programmazione migliori.

Esaminiamo il C

A questo punto osserviamo  il linguaggio C. La regola generale è che col C si può fare globalmente quello che è possibile fare con l’assembler, cioè tutto. In effetti il linguaggio,  considerato  ad alto livello da un punto di vista concettuale, permette la creazione di un eseguibile compatto, pulito e veloce, quasi come se fosse stato preparato nel linguaggio Assembler. Da un punto di vista procedurale si tratta del linguaggio più “logico” in assoluto. Occorre però rispettarlo a fondo, in quanto anche un piccolo errore di analisi, trascrizione o procedurale causa sicuramente il crash fisico del sistema. Un compilatore C, oltre al codice di programma vero e proprio, aggiunge sempre una porzione di codice che serve per preparare e inizializzare l’ambiente di lavoro e per la gestione degli interrupt, nonché i dati e le variabili. Per questo motivo un programma, anche di minime dimensioni e di limitate righe, una volta compilato risulta più grosso dello stesso programma fatto in assembler. Questa porzione aggiuntiva di codice è abbastanza palese sui piccoli programmi, ma non influisce assolutamente sui tempi di esecuzione, poiché esso viene eseguito solo al lancio del programma. Allo stato attuale, la maggior parte dei compilatori C per micro non creano codice compatto, così come lo creano i compilatori per microprocessore.  Il linguaggio C ha tutte le carte in suo favore: con esso si ha in mano il potere di fare qualsiasi cosa. Solo l’Assembler ha questo privilegio e il C, per i risultati che può raggiungere è paragonabile al linguaggio Assembler. In altri termini con il C si può fare veramente di tutto. Basti pensare che tutti i sistemi operativi e tutte le implementazioni sono scritte in C, Linux e Windows sono scritti in C. Questo linguaggio permette di programmare a tutti i livelli e per questo motivo è chiamato “dressed assembler” (assembler vestito). Se si programmare in Basic il programmatore, egli sa programmare in Basic, ma se si conosce il  C, allora il programmatore sa programmare!

Cosa bisogna considerare

Al fine di un risultato ottimale, il  programmatore dovrebbe considerare tanti parametri operativi e di risultato, per i quali scegliere  il proprio linguaggio ottimale. Tali caratteristiche  possono essere raggruppate, a grandi linee, nelle seguenti classi:

Caratteristiche organizzative

» Costi di compilatori;

» Costi di documentazione;

» Reperibilità  dei programmi.

Caratteristiche di programmazione

» Grado di complessità del linguaggio;

» Tempi di sviluppo e analisi;

» Facilità dell’ambiente di sviluppo e dell’interfaccia;

» Grado di bontà dell’editor del codice;

» Tempi di compilazione.

Caratteristiche dell’eseguibile ottenuto

» Compattezza dell’eseguibile;

» Velocità  dell’eseguibile;

» Affidabilità  del codice eseguito in memoria.

Come si vede, dunque, le caratteristiche sopra elencate prendono in considerazione sia l’aspetto “umano” del programmatore, sia l’aspetto più fisico e tecnico del microcontrollore. E’ quindi opportuno scegliere da che parte stare: privilegiare l’aspetto umano e preferire una programmazione semplice e veloce, a discapito della validità del prodotto finale (eseguibile), ovvero scegliere di sacrificarsi un po’,  decidendo di intraprendere una programmazione un tantino più laboriosa e faticosa ma con risultati finali brillanti, oppure ancora optare per una strada intermedia, in cui entrambe le caratteristiche siano messe su un livello alla pari.

Compilatori per Microcontrollori

Esaminiamo adesso il punto di vista dei microcontrollori, che poi è l’argomento che più interessa. Il mercato è pieno di compilatori per linguaggi Basic e C (ed altri numerosi linguaggi), sia commerciali e non. Sta al programmatore scegliere quello che contribuisce a soddisfare tutte le sue esigenze. Naturalmente i  prodotti commerciali forniscono un supporto aggiuntivo e un risultato migliore in termini di ottimizzazione, velocità e corredo di librerie. Anche il mercato del freeware comunque offre alcuni compilatori degni di nota. I paragoni  tra i linguaggi saranno effettuati scrivendo del codice “puro” senza l’ausilio di funzioni particolari e utilizzo di librerie proprietarie, come la gestione degli LCD o della comunicazione RS232. In pratica le prove effettuate considerano il linguaggio  base, utilizzando solo la capacità intrinseca del linguaggio puro, poiché i moduli aggiuntivi variano da prodotto a prodotto. Ripetiamo che l’articolo evidenzia solo la filosofia dei due linguaggi di programmazione e non vuole essere un paragone tra i vari compilatori esistenti, benché nella trattazione dell’argomento saranno utilizzati solo alcuni di essi. Come “cavie” sono stati impiegati i seguenti compilatori le cui versioni sono ormai datate, ma il lettore può fare delle prova usando altri prodotti liberi e non, che il mercato mette a disposizione:

Per il linguaggio Basic

» MikroBasic vers. 7.0

» PicBasic  Pro vers. 3

Per il linguaggio C

» MikroC vers. 8.2

» CC5X vers. 3.3H

Prova 1: piccola  gara di velocità

Vediamo come si comportano i compilatori in questione con un piccolo listato che prevede un loop iterativo, all’interno del quale avviene una semplice assegnazione di una variabile di memoria.  I listati sono estremamente semplici e non è necessario alcun commento. Si presuppone però la conoscenza dei due linguaggi. Si ricorda che un buon test di velocità non si limita solo a valutare l’esecuzione temporale di un ciclo (come stiamo facendo noi) ma racchiude in sé tanti altri tipi di prove più complesse. Il comportamento dei programmi (visibili nei listati di tabella 1) prevede le seguenti funzioni: dichiara le variabili e le porte, spegne la porta PORTB (con dei diodi led per il controllo), intraprende un “lungo” ciclo composto da 60.000 passi, nei quali assegna ripetutamente  il valore 44 alla rispettiva variabile, al termine del quale si accende per intero la PORTB, comprensiva di 8 segnali digitali. Per uniformare le caratteristiche del programma e dell’ambiente di memoria, ovviamente, lo spazio occupato delle variabile è stato il medesimo per tutti i compilatori.

Programma in MikroBasic
program prova2
dim k as word
dim a as byte
TRISB=0
PORTB=0
for k=1 to 60000
       a=44
next k
PORTB=255
end.

Programma in PicBasic Pro
k var word
a var byte
TRISB=0
PORTB=0
for k=1 to 60000
       a=44
next k
PORTB=255
end

Programma in MikroC
void main() {
       unsigned int k;
       char a;
       TRISB=0;
       PORTB=0;
       for(k=1;k<=60000;k++)
               a=44;
       PORTB=255;
}

Programma in CC5X
void main() {
        uns16 k;
        char a;
        TRISB=0;
        PORTB=0;
        for(k=1;k<=60000;k++)
               a=44;
        PORTB=255;
}

Tabella 1: Test di velocità tra Basic e C.
Tabella 1: Test di velocità tra Basic e C.

Prova 1: i risultati

I programmi sono stati eseguiti sia direttamente su microcontrollore Pic 16F84 che su simulatore software ed i risultati sono stati, naturalmente,  i medesimi. Dopo l’avvio del software ed il termine del ciclo di loop i diodi led si sono illuminati dopo i seguenti tempi (in ordine crescente):

» Il programma scritto in MikroC ha impiegato 720 mS;

» Il  programma scritto in CC5X ha impiegato 720 ms;

» Il programma scritto in MikroBasic ha impiegato 1260 mS;

» Il programma scritto in PicBasic ha impiegato 2650 mS.

Segno inconfutabile che la velocità di esecuzione maggiore appartiene ai compilatori per linguaggio C. In ogni caso, ricordiamo, stiamo paragonando routines che utilizzano dati numerici interi e comandi standard del linguaggio, quindi nulla di pesante e particolare. Dobbiamo ricordare però che nelle applicazioni a microcontrollore, raramente necessita una velocità particolarmente elevata, anzi la maggior parte delle volte è opportuno inserire dei piccoli ritardi, proprio per abbassare la velocità operativa del micro. Un altro dato che si evince è quello della grandezza dell’eseguibile prodotto. Anche in questo campo il linguaggio C fa da padrone, permettendo un minore consumo di RAM che in questi casi è davvero preziosa.

Prova 2: un progetto più complicato

Il  seguente esempio è costituito da un prototipo un tantino più sofisticato. Questo per studiare meglio come si comportano i due linguaggi a gestire varie situazioni, come condizioni, cicli e salti con dizionati e non. Nell’esempio seguente non valuteremo la velocità operativa, in quanto l’applicazione sarà costituita essenzialmente da automatismi e comandi impostati dall’utente che, come si sa, impiega tempi umani e lenti a compiere le operazioni di gestione.

Il progetto

Si tratta di un circuito composto da 4 pulsanti e da quattro diodi led. Inizialmente i diodi led sono tutti spenti. Il comportamento dei diodi led, alle pressioni dei quattro tasti, è il seguente:

» Se si preme il tasto n. 1 tutti i Led si SPENGONO;

» Se si preme il tasto n. 2 tutti i Led si ACCENDONO;

» Se si preme il tasto n. 3 si accendono i due Led INTERNI;

» Se si preme il tasto n. 4 si accendono i due Led ESTERNI.

Il prototipo ha la funzione di esaminare il comportamento dei due linguaggi di programmazione dal punto di vista di gestione della memoria e non quello della velocità, dal momento che gli eventi sono determinati dall’azione dell’uomo (pressione dei pulsanti), nonché le differenze semantiche dei quattro listati, abbastanza somiglianti tra loro.

Prova 2: i listati

I quattro listati di tabella 2 sono simili tra loro. Il fulcro della procedura è rappresentato da un ciclo infinito, nel quale vengono controllati costantemente  i quattro tasti. A seconda di quale tasto viene premuto, si determinerà una diversa illuminazione dei diodi Led. Sono state eliminate deliberatamente artifizi vari per l’illuminazione dei Led, utilizzando operazioni di logica booleana.

Programma in MikroBasic
program prova2
PORTB=0
TRISB=%00001111
while true
if portb.0=1 then
portb.4=0
portb.5=0
portb.6=0
portb.7=0
end if
if portb.1=1 then
portb.4=1
portb.5=1
portb.6=1
portb.7=1
end if
if portb.2=1 then
portb.4=0
portb.5=1
portb.6=1
portb.7=0
end if
if portb.3=1 then
portb.4=1
portb.5=0
portb.6=0
portb.7=1
end if
wend
end.

Programma in PicBasic Pro
PORTB=0
TRISB=%00001111
while true
if portb.0=1 then
portb.4=0
portb.5=0
portb.6=0
portb.7=0
end if
if portb.1=1 then
portb.4=1
portb.5=1
portb.6=1
portb.7=1
end if
if portb.2=1 then
portb.4=0
portb.5=1
portb.6=1
portb.7=0
end if
if portb.3=1 then
portb.4=1
portb.5=0
portb.6=0
portb.7=1
end if
wend
end.

Programma in MikroC
void main() {
PORTB=0;
TRISB=0b00001111;
while(1==1) {
if(portb.F0==1) {
portb.F4=0;
portb.F5=0;
portb.F6=0;
portb.F7=0;
}
if(portb.F1==1) {
portb.F4=1;
portb.F5=1;
portb.F6=1;
portb.F7=1;
}
if(portb.F2==1) {
portb.F4=0;
portb.F5=1;
portb.F6=1;
portb.F7=0;
}
if(portb.F3==1) {
portb.F4=1;
portb.F5=0;
portb.F6=0;
portb.F7=1;
}
}
}

Programma in CC5X
void main() {
PORTB=0;
TRISB=0b00001111;
while(1==1) {
if(PORTB.0==1) {
PORTB.4=0;
PORTB.5=0;
PORTB.6=0;
PORTB.7=0;
}
if(PORTB.1==1) {
PORTB.4=1;
PORTB.5=1;
PORTB.6=1;
PORTB.7=1;
}
if(PORTB.2==1) {
PORTB.4=0;
PORTB.5=1;
PORTB.6=1;
PORTB.7=0;
}
if(PORTB.3==1) {
PORTB.4=1;
PORTB.5=0;
PORTB.6=0;
PORTB.7=1;
}
}
}
Tabella 2: test di velocità tra Basic e C.

Prova 2: i risultati

I programmi sono abbastanza simili tra lo stesso linguaggio e le modifiche necessarie si limitano al minimo. Questo perché non si utilizzano le librerie proprietarie di ciascun compilatore. La portabilità del linguaggio C è eccezionale e massima e praticamente un listato ha bisogno solo di minime modifiche per potersi adattare ad un altro compilatore. Anche l’occupazione e l’ottimizzazione del codice è a favore di questo linguaggio.

Conclusioni

Con questa piccola rassegna di comparazioni abbiamo voluto illustrare le differenze sostanziali tra i due linguaggi, che sono più rimarcate nelle applicazioni a microprocessore che a microcontrollore. Concludiamo l’argomento non decretando “il vincitore” tra i due linguaggi esaminato ma a fornire, secondo noi più utilmente, alcune considerazioni che il lettore può seguire per farsi un’idea o per prendere spunti per la futura scelta. La lettura infatti di queste conclusioni è, a nostro avviso, molto importante. L’accostamento ad uno standard è sempre vantaggioso, vedi ad esempio l’uso delle librerie ANSI per il linguaggio  C. I compilatori  mettono a disposizione molte librerie “preconfezionate” per vari scopi, come ad esempio la gestione LCD, il bus CAN, il protocollo I2C o altre. Non è detto che esse siano le migliori: l’utente può riscrivere ex novo queste routine, magari con un codice più compatto ed efficiente. Per quanto riguarda le operazioni pure del compilatore, conviene utilizzare quelle già offerte, in quanto le operazioni matematiche di base, come la gestione delle variabili, fanno parte integrante del linguaggio, quindi, per fare un esempio, va benissimo definire una variabile come intera (16 bit) e poi usare il comando di divisione messo a disposizione dal linguaggio, anche perché è molto difficile riuscire a fare meglio delle routine usate dal compilatore, che sono già scritte in assembler e ottimizzate al massimo. Per le routine di calcolo e per le funzioni matematiche avanzate come quelle per il calcolo trigonometrico o logaritmico, è opportuno stare molto attenti con i compilatori economici, che solitamente non sono molto evolute ed ottimizzate. In generale non conviene eseguire un test comparativo tra linguaggi scrivendo programmi semplici, e non prendere come termine di paragone solo le dimensioni dell’eseguibile, quando si parla di linguaggi diversi. Come detto prima infatti le differenze si notano quando il programma  comincia a diventare piuttosto complesso. Parlando di velocità di esecuzione, essa può essere ottenuta anche con un eseguibile di dimensioni maggiori, infatti ci sono delle applicazioni nelle quali evitando le subroutines e creando codici molto estesi (quindi a discapito della lunghezza e chiarezza del codice) si riesce ad ottenere un incremento notevole della velocità. Tutto sta a sfruttare nel modo migliore la capacità di memoria e le risorse del micro utilizzato.  Il linguaggio  C indubbiamente è quello che promette e mantiene una certa compatibilità tra i compilatori. Eseguendo infatti le varie prove, tra il MikroC ed il CC5X non è stato praticamente modificato nulla (vedi sorgenti relativi) mentre per il Basic sono state necessarie svariati adattamenti di codice. Nel mercato dei microcontrollori esistono svariate edizioni di compilatori. Si possono trovare facilmente compilatori Basic e C scadenti come compilatori molto performanti e di ottima qualità. La struttura del linguag gio e la sua semantica è tuttavia la stessa. In conclusione ci sentiamo di dare un consiglio solo: utilizzate lo strumento che conoscete meglio. In questa maniera potete “spremere” a fondo il linguaggio,  ottenendo da esso il massimo. Studiare tuttavia i due linguaggi non è una perdita di tempo, anzi permette una piena conoscenza dei limiti, difetti e pregi di entrambi. Scrivete un codice corretto, chiaro e valido. E’ inutile utilizzare il linguaggio C per ottenere maggiore velocità e poi scrivere algoritmi totalmente confusi e ineseguibili. Meglio in questi casi pretendere la massima semplicità e la maggiore chiarezza. Un codice pulito è sempre efficiente. Cercate di ottimizzare  il codice, l’uso di funzioni e di variabili. E’ infatti lo scopo del risultato che modella il  linguaggio, e non viceversa. Se poi siete patiti della massima velocità di esecuzione, di codice super ottimizzato e che occupi pochissima memoria, se volete fare proprio “tutto” con il linguaggio di programmazione, allora ….utilizzate l’Assembler. Del resto se avete scelto il Basic vuol dire che volete ottenere buoni risultati senza fatica. Se poi vi occorrono velocità operative strabilianti, risoluzioni massime ed elaborazione di miliardi e miliardi di operazioni al secondo, allora abbandonate i microcontrollori e passate ai microprocessori. Se infine dovete produrre dei lavori professionali a prova di bomba, utilizzate il linguaggio  che conoscete come le vostre tasche. Solo così infatti non esisteranno segreti per voi e per il vostro microcontrollore.

 

 

2 Commenti

  1. Riccardo Ventrella Riccardo Ventrella 2 settembre 2018
  2. Riccardo Ventrella Riccardo Ventrella 2 settembre 2018

Scrivi un commento

EOS-Academy