FreeBASIC – tipo di dato definito da noi

Nonostante i tipi di dato standard ci permettano di risolvere la maggior parte dei problemi che affrontiamo quando scriviamo un programma, può verificarsi il caso che si renda utile, se non addirittura necessario, utilizzare un tipo di dato creato ad hoc direttamente da noi.

Anche questo tipo di dato dovrà essere identificato da una etichetta che dovrà rispettare le stesse regole viste per le variabili.

Il tipo di dato creato dall’utente (user-defined type o UDT) può essere visto come una specie di array, con la particolarità che un UDT può contenere anche tipi di dato differenti. È proprio questa caratteristica a rendere utile un UDT.

Un UDT può contenere sia variabili che procedure che prendono il nome di membri (o elementi).

Utilizzo di un UDT attraverso una variabile

Per creare un UDT si utilizza l’istruzione type.

Per accedere ai singoli membri attraverso una variabile si utilizza l’operatore . (notazione puntata):

'definizione di un nostro tipo di dato
type automobile
  cavalli as integer
  cilindrata as integer
end type

'dimensionamento e creazione di una variabile
'del tipo appena definito
dim mia_auto as automobile

'valorizzazione dei membri
mia_auto.cavalli = 75
mia_auto.cilindrata= 999

'stampa dei valori
print "CV = "; mia_auto.cavalli
print "Cilindrata = "; mia_auto.cilindrata

Utilizzo di un UDT attraverso un puntatore alla sua variabile

Come abbiamo visto nell’articolo dedicato ai puntatori, anche per i tipi di dato definiti da noi (UDT) possiamo creare il relativo puntatore alla sua variabile.

La creazione di questo tipo di puntatore è simile a quella per un puntatore ad una variabile relativa a un tipo di dato standard. Ciò che cambia è la modalità di accesso ai singoli membri che dovrà avvenire attraverso l’operatore ->.

Riprendendo l’esempio precedente, aggiungiamo le seguenti righe di codice:

'creazione di un puntatore alla variabile
dim p as automobile pointer = @mia_auto

'valorizzazione dei membri attraverso il puntatore
p->cavalli = 90
p->cilindrata= 1500

'stampa dei valori
print "CV = "; p->cavalli
print "Cilindrata = "; p->cilindrata

Nel prossimo articolo inizieremo a parlare delle procedure.

A presto. 🙂

FreeBASIC – puntatore

Nel precedente articolo abbiamo parlato del contenitore array. Oggi faremo la conoscenza del puntatore.

Cominciamo con il darne una sintetica definizione:

Un puntatore è una variabile il cui valore rimanda ad uno specifico indirizzo di memoria.

Dichiarare un puntatore

È necessario che vi sia una diretta corrispondenza tra il tipo di dato del puntatore e il tipo di dato dell’elemento puntato.

La dichiarazione di un puntatore è simile a quella per una variabile con l’unica differenza che occorre inserire il suffisso pointer (o ptr).

Assegnare un indirizzo di memoria ad un puntatore

Per assegnare ad un puntatore l’indirizzo di memoria di un elemento si utilizza l’operatore @ (chiocciola) che sta per indirizzo di.

Accedere al dato dell’elemento puntato

Per accedere al dato dell’elemento puntato si utilizza l’operatore * (asterisco) che sta per valore di.

Esempio

dim altezza as integer = 180
dim puntatore_altezza as integer pointer = @altezza
*puntatore_altezza = 20
print "Il valore dell'altezza e': "; altezza

Questo esempio mostra chiaramente che:

Far riferimento direttamente alla variabile o al puntatore della variabile non fa alcuna differenza.

Manipolare gli indirizzi di memoria

Arrivati a questo punto è lecito porsi due domande:

  1. Ma che ce ne dobbiamo fare dei puntatori?
  2. Non sono sufficienti le variabili?

Il fatto è che in alcune circostanze può essere utile lavorare direttamente con gli indirizzi di memoria. Questo però è un argomento che rientra tra quelli avanzati e quindi ne parleremo più avanti. Un po’ di pazienza 😉

Sino a questo momento abbiamo sempre parlato dei tipi di dato standard, nel prossimo articolo inizieremo a lavorare con i tipi di dato definiti da noi.

FreeBASIC – array

Dopo aver imparato il concetto di variabile e il suo utilizzo, vediamo ora un nuovo costrutto tipico dei linguaggi di programmazione.

Immaginiamo di dover lavorare con decine o centinaia di valori tutti dello stesso tipo di dato standard o di un tipo di dato deninito da noi. Farlo dichiarando per ogni valore una variabile sarebbe un compito alquanto stancante. In questi casi si ricorre all’uso degli array.

Definizione

Possiamo quindi definire un array come un:

contenitore indicizzato – mono o multidimendionale – di elementi tutti dello stesso tipo.

Indice di un elemento

Per accedere in lettura o in scrittura ad uno specifico elemento di questo insieme, si ricorre al suo relativo indice che non è altro che un valore di tipo integer che identifica la posizione dell’elemento all’interno dell’array.

L’indice va racchiuso all’interno di due parentesi tonde:

'dimensiona un array di 3 valori stringa
dim array_semaforo(1 to 3) as string

'assegna i valori
array_semaforo(1) = "rosso"
array_semaforo(2) = "giallo"
array_semaforo(3) = "verde"

'stampa i valori dell'array
print "Il semaforo ha i seguenti colori:"
for i as integer = 1 to 3
  print array_semaforo(i)
next

Nota sul dimensionamento di un array

Se nella fase di dimensionamento di un array non viene specificato il limite inferiore, questo assumerà il valore di default 0 (zero).

Dimensione di un array

Un array ha quindi una dimensione rappresentata dal numero degli elementi che contiene. Questa dimensione può essere fissa o variabile. Questa particolarità discrimina quindi due diversi tipi di array:

  1. fixed-length arrays
  2. variable-length arrays

La dimensione di un array è data dalla differenza tra il valore massimo dell’indice, ovvero il limite superiore (upper bound), e il suo valore minimo, ovvero il limite inferiore (lower bound) + 1.

Per ottenere il limite superiore e quello inferiore possiamo utilizzare le seguenti due istruzioni:

limite_superiore = ubound(mio_array)
limite_inferiore = lbound(mio_array)

La dimensione del nostro array sarà quindi:

dimensione = ubound(mio_array) - lbound(mio_array) + 1

Indice valido

Un valore valido per l’indice di un array deve soddisfare questa caratteristica:

lower bound ≤ indice valido ≤ upper bound

Differenze tra gli array fissi e variabili

Gli array con dimensione fissa hanno una maggiore efficienza rispetto a quelli con dimensione variabile in quanto il compilatore assegna loro una porzione di memoria statica che è più velocemente accessibile della memoria dinamica.

È quindi proprio l’allocazione statica della memoria che rende la dimensione di un array immutabile.

Agli array con dimensione variabile, invece, il compilatore assegna dinamicamente la memoria necessaria durante la stessa esecuzione del programma (ovvero a run-time). È proprio l’allocazione dinamica della memoria che permette di ampliare o ridurre le dimensioni di un array a scapito però della efficienza di lavoro.

Quindi se dobbiamo creare un insieme di valori omogenei che sappiamo a priori non varieranno mai come numero di elementi, allora ci converrà scegliere un array di tipo fisso, altrimenti ne creeremo uno di tipo variabile.

Come creare un array di tipo fisso o di tipo variabile

Per creare un array di tipo fisso è necessario che i suoi limiti siano dichiarati attraverso delle costanti o dei letterali:

'creo un array di tipo fisso con 10 elementi
const limite_superiore = 10
dim array_fisso(1 to limite_superiore) as string

Per creare invece un array di tipo variabile possiamo in un caso dichiarare i suoi limiti attraverso delle variabili, oppure crearne uno vuoto e poi utilizzare l’istruzione redim per ridimensionarlo:

'I caso: creo un array di tipo variabile con 10 elementi
dim as integer lim_inf, lim_sup
lim_inf = 1
lim_sup = 10
dim array1(lim_inf to lim_sup) as string

'II caso: creo un array vuoto e poi lo ridimensiono
dim array2() as string
redim array2(1 to 10) 'non è necessario specificare il tipo

Nota: quando utilizziamo l’istruzione redim per ridimensionare gli array di tipo variabile non è necessario specificare di nuovo il tipo di dato degli elementi.

Il ridimensionamento di un array di tipo variabile causa la perdita dei dati contenuti precedentemente in esso. Per ovviare a questo occorre utilizzare l’istruzione preserve:

redim preserve array2(1 to 10)

Creare un array multidimensionale

Se abbiamo necessità di creare un array a più dimensioni non faremo altro che aggiungere nuovi limiti nella fase di creazione o di ridimensionamento.

Per esempio potremmo avere la necessità di rappresentare una tabella con 10 righe e 5 colonne:

dim mia_tabella(1 to 10, 1 to 5) as [type]

Bene. Ora che abbiamo imparato a lavorare con gli array, siamo pronti per fare la conoscenza nel prossimo articolo di un nuovo amico: il puntatore.

FreeBASIC – variabile

Nel precedente articolo abbiamo discusso del concetto informatico di costante e di come utilizzarla nel nostro codice.

È arrivato quindi il momento di parlare della variabile cominciando a darne una sintetica definizione:

Una variabile è una etichetta che rimanda a dei dati registrati in una qualsiasi porzione di memoria che possono essere modificati in qualsiasi momento.

Dichiarazione

La prima cosa da fare quando vogliamo utilizzare una variabile nel nostro codice è dichiararla attraverso l’istruzione dim:

dim mia_variabile as string

Occorre però fare attenzione che i nomi delle nostre variabili rispettino due semplici regole sintattiche:

  1. i nomi possono contenere soltanto lettere maiuscole o minuscole, numeri e il carattere _ (trattino basso, underscore);
  2. i nomi non devono contenere degli spazi.

Come abbiamo già avuto modo di vedere nell’articolo dedicato specificatamente alla dichiarazione, questa assegna uno specifico tipo alla nostra variabile; ciò comporta che dovremo utilizzarla in maniera coerente nel nostro codice: ovvero se abbiamo dichiarato che la variabile è di tipo testo (string), non potremo utilizzarla direttamente in una espressione aritmetica, a meno che non operiamo intenzionalmente una sua conversione.

Ambito di utilizzo

A seconda del punto dove dichiariamo una variabile nel codice che stiamo scrivendo, questa avrà un suo specifico ambito di utilizzo (in inglese scope) che potrà essere:

  • limitato ad una procedura (subroutine o funzione);
  • limitato ad un modulo;
  • oppure utilizzabile ovunque all’interno dei moduli che compongono il nostro programma utilizzando la parola-chiave shared.

Bene, arrivati a questo punto siamo pronti per fare la conoscenza nel prossimo articolo di un tipo di variabile molto speciale: il vettore/matrice (più comunemente noto con il termine inglese array).

FreeBASIC – costante

In questo articolo faremo la conoscenza delle costanti.

In determinate circostanze è molto utile nella programmazione ricorrere all’uso di una o più costanti.

Per esempio se in più punti del nostro codice dobbiamo utilizzare il valore del Pi greco, è bene definire una costante, non avrebbe senso infatti definire una variabile in quanto il Pi greco è una nota costante matematica:

const pi_greco = 3.14159 ''la dichiarazione del tipo è opzionale

Una volta definita una costante non sarà più possibile cambiarne il valore nel codice, pena l’incorrere in un errore in fase di compilazione.

La cosa bella delle costanti è che hanno un ambito globale (global scope): ovvero possiamo utilizzare il loro nome, e quindi il loro valore, anche tra moduli diversi dello stesso programma.

Ovviamente siamo liberi di definire costanti numeriche, booleane o stringa:

const sconto as integer = 20
const stato as boolean = true
const etichetta as string = "SCONTO"

Se non viene dichiarato esplicitamente il tipo di costante, questo viene comunque inferito dal compilatore.

Nel prossimo articolo parleremo delle variabili.

FreeBASIC – conversione del tipo di variabile

Occhio agli errori

Quando lavoriamo su un programma può presentarsi la necessità di fare delle operazioni tra tipi di variabili differenti.

Questo tipo di operazioni possono presentare dei rischi: per esempio assegnando ad una variabile numerica di tipo short (i cui limiti di memorizzazione vanno da -32768 a +32767) il risultato della somma di due variabili di tipo integer che hanno una capacità di memorizzazione molto più ampia, si può ottenere un risultato sbagliato:

dim a as integer
dim b as integer
dim c as short 'limite: da -32768 a +32767

a = 32766
b= 1
c = a + b
print c 'viene stampato a video il valore corretto 32767

a = 32767
b= 1
c = a + b
print c 'viene stampato a video il valore errato -32768

In questo caso il compilatore FreeBASIC non ci avverte neanche del problema. Quindi occorre assolutamente stare attenti.

Modalità di conversione

Esistono due modalità di conversione di tipo:

  • conversione implicita (coercion)
  • conversione esplicita (conversion)

Modalità implicita

In una espressione con più argomenti, o nelle procedure (nel passaggio degli argomenti o nel ritorno dei risultati), il compilatore FreeBASIC provvede ad operare una serie di conversioni automatiche di tipo di dato secondo i seguenti criteri:

  1. tutte le variabili numeriche con capacità più piccola di quella del tipo integer vengono convertite nel tipo integer;
  2. più in generale, le variabili numeriche vengono convertite nello stesso tipo di dato della variabile con più capacità (vedi Tabella dei limiti dei tipi di dato standard);
  3. se in una espressione compaiono variabili di tipo single, queste vengono convertite in double.

Modalità esplicita

Come programmatori possiamo anche forzare la conversione di tipo chiedendo esplicitamente al compilatore FreeBASIC – per esempio con l’istruzione cast – di operare una determinata conversione.

Oltre all’istruzione cast, esistono diversi operatori e procedure di conversione di tipi di dato che sono predefinite in FreeBASIC. Per esempio:

'funzione str
'converte una espressione numerica in una stringa
dim a as integer
dim b as integer
dim tot as string
a = 3
b = 2
tot = str(a + b)
print tot

oppure:

'funzione val
'converte una stringa in un valore numerico a virgola mobile (double)
dim a as string
dim b as integer
a = "1"
b = 4 + val(a)
print b 'stampa a video 5

A rileggerci nel prossimo articolo dove parleremo delle costanti.

FreeBASIC – letterali

Innanzitutto, cosa intendiamo con il termine letterale?

Un letterale è un valore di tipo numerico (0-9) o booleano (true/false), oppure una stringa di caratteri che il programmatore scrive direttamente nel codice e su cui il compilatore FreeBASIC non fa alcun tipo di elaborazione/calcolo.

Utilizzo dei letterali

Un valore letterale può essere assegnato ad una qualsiasi variabile (o costante) coerentemente al suo tipo (vedi tabella dei tipi standard):

dim as integer mio_numero = 7
dim as boolean mio_valore = true
dim as string mio_testo = "ciao"

Oppure può essere utilizzato direttamente in una espressione:

dim as integer somma
somma = 4 + 3

Un utilizzo più avanzato è come parametro in una procedura. Questo argomento lo tratteremo in uno dei prossimi articoli.

Letterali numerici interi

In FreeBASIC possiamo scrivire un letterale numerico intero (integer, byte, ecc.) in quattro differenti forme:

Forma decimale

Si utilizzano le cifre decimali (0-9) e per i numeri negativi si antepone il simbolo (-):

dim as integer a = 8200364
dim as byte b = -121

Forma esadecimale

Si antepone “&h” alle cifre esadecimali (0-F):

dim as integer a = &h2F
dim as byte b = &h3

Forma ottale

Si antepone “&o” alle cifre ottali (0-7):

dim as integer a = &o34
dim as byte b = &o4

Forma binaria

Si antepone “&b” alle cifre binarie (0-1):

dim as integer a = &b01110
dim as byte b = &b101

Letterali numerici in virgola mobile

Un letterale numerico in virgola mobile è specificato tramite le cifre (0-9) e il simbolo del punto (.) come separatore dei decimali. Per i numeri negativi si antepone il simbolo (). Opzionalmente può avere un esponente:

dim as single a = -3.54
dim as double b = 4576.83457287
dim as double c = 43.42e-4 'elevamento con esponente negativo

Letterali stringa

I letterali stringa sono sequenze di caratteri racchiusi tra doppie virgolette:

dim as string s = "pippo"

I letterali stringa possono contenere le cosiddette sequenze di escape anteponendo alle prime doppie virgolette il simbolo (!):

dim as string s = !"prima riga\nseconda riga"

Anteponento invece il simbolo ($) si forza il compilatore a considerare la sequenza come priva di caratteri di escape:

dim as string d = $"c:\temp"

Letterali booleani

Questi letterali rappresentano i due valori di verità vero/falso:

dim as boolean a = true
dim as boolean b = false

Bene. Per adesso ci fermiamo qui.

Nel prossimo articolo parleremo di conversione del tipo di variabile.

FreeBASIC – commenti

Un commento è una parte di testo inserita nel nostro codice che viene ignorata dal compilatore FreeBASIC.

Commentare il proprio codice è necessario, e farlo bene è molto importante.

Perché è necessario commentare il codice?

Nel momento in cui scriviamo una parte magari abbastanza complessa del nostro codice, è molto probabile che il suo funzionamento ci appaia chiaro ed evidente in quanto lo abbiamo concepito e analizzato in maniera approfondita proprio in quei minuti in cui siamo alla tastiera del nostro computer. Che bisogno c’è quindi di inserire un commento?

Il fatto è che, a distanza di tempo, qualora dovessimo rimettere mano a quella parte del codice, quello che ci era così chiaro ed evidente diventa oscuro e intricato. Facciamo fatica a ricordare la logica sottesa a certi passaggi e il perché di certe scelte.

Ecco allora che un commento sintetico è molto di aiuto nel recupero dei ragionamenti che avevamo a suo tempo sviluppato per arrivare alla scrittura di una specifica parte del codice.

Figuriamoci poi se a metterci le mani dovesse essere qualche altro sfortunato programmatore.

Caratteristiche di un buon commento

Come non commentare è sbagliato, così è un errore commentare tutto:

Se un certo passaggio è autoesplicativo, non c’è alcun bisogno di commentarlo.

Commentiamo quindi soltanto quei passaggi che possono presentare una certa complessità o che magari rappresentano dei punti chiave del nostro codice.

Tipi di commento

Il compilatore FreeBASIC riconosce diversi tipi di commento.

Commento su una sola linea

Per scrivere un commento su una sola linea, lo si fa precedere dal simbolo apice ():

'Questo è un commento su una sola linea.
print "ciao" 'Anche questo è un commento su una sola linea.

oppure al posto dell’apice si può utilizzare la parola rem.

Nota: inserendo un doppio apice al posto del singolo apice , si evita che il compilatore FreeBASIC analizzi comunque il commento alla ricerca, per esempio, di qualche meta-comando quale per esempio #include (i meta-comandi possono infatti essere scritti all’interno di un commento). In pratica tutto ciò che segue il doppio apice viene totalmente ignorato dal compilatore.

Commento su più linee

Per scrivere un commento su più linee lo si racchiude tra i simboli /’ … ‘/:

/'Questo è un commento
su più linee'/

Nel mondo della Tecnologia dell’Informazione (Information Technology) la lingua franca è l’inglese, è bene quindi che ci sforziamo di scrivere i commenti, i nomi delle variabili e delle funzioni in questa lingua.

A rileggerci al prossimo articolo. 🙂

FreeBASIC – librerie

In ambito informatico esiste un modo di dire che più o meno recita così:

Dobbiamo sempre sforzarci di evitare di reinventare la ruota.

Tradotto significa che se qualcuno ha già scritto del software per svolgere un determinato compito e l’ha messo a disposizione della comunità degli sviluppatori tramite una libreria, è buona pratica utilizzarlo all’interno del proprio progetto senza mettersi a riscriverlo per proprio conto da zero.

In FreeBASIC sono disponibili molti files di intestazione che ci permettono di utilizzare agevolmente delle funzioni presenti sia in librerie esterne incluse in FreeBASIC, sia in librerie scritte in FreeBASIC dagli stessi membri della Comunità FreeBASIC.

Le librerie possono essere di due tipi: statiche o dinamiche (DLL).

Nel precedente articolo abbiamo visto come includere i files di intestazione in un file sorgente (o modulo).

Nota: alcune librerie utilizzabili non sono distribuite insieme al compilatore FreeBASIC. Occorre quindi scaricarle e leggere la documentazione di base per poterle utilizzare. È probabile invece che le maggiori librerie standard siano già presenti nel proprio computer.

Files di intestazione di uso frequente

Ci sono alcuni files di intestazione specifici per il compilatore FreeBASIC che capiterà spesso di dover includere nei propri progetti. Ecco di seguito una breve lista:

  • datetime.bi (per lavorare con le date e le ore)
  • dir.bi (per lavorare con le cartelle)
  • file.bi (per lavorare con i files)
  • fbgfx.bi (per lavorare con la grafica)
  • string.bi (per lavorare con la formattazione delle stringhe)

Nel prossimo articolo vedremo come creare una libreria statica.

FreeBASIC – tipi di files

Vi avverto: questo sarà un articolo abbastanza noioso, ma, allo stesso tempo, anche indispensabile per affrontare con più consapevolezza la programmazione in FreeBASIC.

Cominciamo.

Dovete sapere che in FreeBASIC ci sono tre tipologie di files. Due delle quali abbastanza scontate, ma la terza… che dire… è un po’ strana.

I. File sorgente (o modulo)

Questi files contengono il nostro codice sorgente e hanno estensione .bas. Un semplice programma può essere scritto interamente anche in un solo file, ma se il programma che stiamo sviluppando è di una certa complessità, allora è preferibile organizzarlo su più files sorgente.

II. File eseguibile

Il processo di compilazione crea un file eseguibile contenente sia il codice oggetto ricavato a partire da uno o più files di codice sorgente, che eventuali collegamenti a una o più librerie. Negli ambienti Windows questi files hanno estensione .exe.

III. File di intestazione

Eccoci arrivati al terzo tipo di file, quello strano.

Abbiamo detto che quando ci troviamo a dover sviluppare un programma complesso è bene organizzarlo in più files di codice sorgente. Ebbene, in questi casi, è spesso conveniente anche scrivere un particolare tipo di file sorgente che viene appunto chiamato file di intestazione e che ha come estensione .bi. Questo particolare file contiene generalmente soltanto alcuni specifici tipi di informazioni, quali, per esempio:

  • dichiarazioni di variabili o costanti
  • definizioni di funzioni

La presenza di questo tipo di file aiuta i programmatori ad avere una visione complessiva delle variabili, delle costanti, delle funzioni, ecc., che possono essere utilizzate senza dover prendere visione ogni volta di tutti i files sorgente.

Incastrare tutti i pezzi come in un puzzle

Per utilizzare più files sorgenti o più files di intestazione occorre includerli nel file sorgente che si sta scrivendo attraverso la direttiva #include che informa il compilatore FreeBASIC sul percorso e sul nome della risorsa da includere durante il processo di compilazione.

Nota: un file di intestazione può a sua volta includere altri files di intestazione.

Avendo a che fare con un grande progetto contenente molti files sorgente (magari non tutti scritti da noi), e magari anche più files di intestazione, potrebbe capitare il caso che più files contengano una direttiva di inclusione per la stessa risorsa. Onde evitare un lavoro inutile al compilatore è bene utilizzare la direttiva #include in questa sua variante:

#include once "percorso e nome della risorsa"

Riguardo alla scrittura del percorso, ovvero della sequenza delle cartelle sino ad arrivare a quella che contiene la nostra risorsa, è bello sapere che:

Il compilatore FreeBASIC converte in automatico i simboli di separazione delle cartelle (la barra / negli ambienti Linux, la barra rovesciata \ negli ambienti Windows) in base al sistema operativo dove viene utilizzato.

Evviva! Ce l’abbiamo fatta.

La prossima settimana daremo uno sguardo nel magico mondo delle librerie. 🙂