Guida al C: parte 2 (Il resto del Pinguino)

Le variabili e le costanti in C

Per variabile si intende una porzione di memoria atta a contenere un determinato dato che potrà poi essere modificato nel corso dell’uso del programma. Essa può essere inizializzata all’atto della sua dichiarazione con un valore di default o può essere dichiarata senza alcun valore. In tal caso il linguaggio non garantisce un valore
iniziale utile. Ad esempio una variabile può essere dichiarata ed inizializzata in due modi diversi. Per doppio passaggio:

// dichiaro
int x;
// inizializzo
x = 14;

o direttamente:

// dichiaro ed inizializzo
int x = 14;

Una costante, invece, è una porzione di memoria che viene dichiarata per contenere un determinato tipo di dato e ne conserva il valore per tutta la durata dell’uso del programma senza mai poterne modificare il valore. Ogni variabile o costante nel C deve essere identificata da un nome al quale si farà poi riferimento nel corso dello svolgimento del programma per leggere o scriverci dati. Il nome delle variabili in C è arbitrario e può
avere lunghezza variabile però il compilatore si interesserà solo delle prime 8 lettere che lo compongono. Quindi anche se a prima vista un dato di nome “variabile1” pare diverso da “variabile2”, il compilatore restituirà errore poichè vedrà due variabili con lo stesso nome ovvero “variabil”. Il C è un linguaggio che basa la sua struttura sui dati. Dati diversi, infatti, equivalgono a cose diverse. Questo tipo di gestione permette di avere un buon uso della memoria perché ad ogni tipo di dato è dedicata la giusta quantità di memoria e in più dà la possibilità di usare o creare strutture ad hoc per le diverse esigenze di sviluppo. Il C gode delle presenza di 6 tipi di dati base:

char
int
long
short
float
double

Il char è il dato adibito alla gestione dei caratteri. Ogni volta che viene dichiarata una variabile di tipo char essa occupa una quantità di memoria pari a 8 bit (1 byte). Il char, nonostante sia effettivamente un tipo di dato numerico, permette la gestione del suo valore come una normale “lettera”. Un array (vettore) di char permette la virtualizzazione di una stringa mentre, in altri linguaggi, esiste ormai un tipo di dato string dedicato. Un esempio di dichiarazione di un dato char inizializzato è:

char carattare = ‘ a’ ;

E’ importante notare come definire il carattere ‘8’, ad esempio, è totalmente diverso dalla definizione di un valore intero 8. Un char che contiene il carattere ‘8’ in realtà avrà al suo intero il valore numerico che, in base alla codifica in uso, equivale a quel determinato carattere mentre una variabile numerica che contiene 8 avrà proprio quel valore preciso in memoria.

L’ int è il tipo adibito a contenere dati numerici di numeri interi o meglio definiti come numeri naturali. Tali numeri, pertanto, devono contenere numeri senza virgole, frazioni, parti immaginarie, etc… un dato di tipo int, all’atto della sua dichiarazione, occupa 16 bit (2 byte). I valori che un int può assumere vanno da -32.768 a +32.767 se si usa il segno (valori positivi o negativi) o va da 0 a 65535 per soli numeri interi. Un esempio di dichiarazione di un dato int inizializzato è:

int numIntero = 14;

Long può essere visto come un particolare int creato appositamente per ovviare al problema di uso di numeri più grandi di quelli che l’ int offre. Esso occupa una quantità di memoria pari a 32 bit (4 byte) ed offre la possibilità di usare valori che vanno da -2.147.483.648 a +2.147.483.647 per numeri con segno o da 0 a 4.294.967.295 per numeri privi del segno. Un esempio di dichiarazione di un dato long inizializzato è:

long numIntLong = 1000000000;

Lo short è un dato molto simile all’int ma che trova limitazioni più restrittive per quanto riguarda il suo valore massimo o minimo. Esso infatti occupa 8 bit (1 byte) e può perciò coprire un range che va da -127 a +128 pe numeri con segno mentre va da 0 a 256 per numeri senza segno.

I float e i double sono particolari tipi di dati che danno la posibilità di rappresentare i numeri in virgola mobile, ovvero i numeri reali. La differenza tra i due tipi di dati sta nella quantità di memoria utilizzata e pertanto nell’accuratezza con cui i numeri possono essere rappresentati. Un float, infatti, richiede una quantità di memoria pari a 32 bit (4 byte) mentre un double richiede 64 bit (8 byte). Un esempio di dichiarazione di un dato float e di un double inizializzato è:

double dblVirgola = 5.023;
float fltVirgola = 7.8;

SIGNED ed UNSIGNED

Come si è visto, ogni dato ha la possibilità di considerare l’insieme dei suoi valori limitato diversamente in base all’uso che se ne vuole fare. Ad esempio due dati, entrambi int, potranno assumere valori massimi diversi se uno è sempre positivo mentre l’altro assume sia valori positivi che negativi. E’ importante avere in mente tale concetto per poter avere a disposizione sempre il miglior tipo di dato per le nostre esigenze. Di default i dati in C vengon visti come signed e perciò è possibile usare il segno per definire il valore in maniera positiva o negativa. Qualora ci fosse bisogno di definire esplicitamente che un numero non necessita di una rappresentazione negativa allora si può dichiarare la viariabile in questo modo:

unsigned int Numero = 15;

altrimenti è possibile usare la sintassi:

signed int Numero = – 15;

che è equivalente a

int Numero = – 15;

COSTANTI

Il discorso trattato fin ora era studiato per l’uso delle variabili; tuttavia, tutte le regole di cui sopra valgono anche per quanto riguarda il trattamente di dati costanti. Bisogna comunque ricordare che essi mantengono il valore iniziale e perciò quanto detto vale solo all’atto della dichiarazione e mai durante lo svolgimento del programma. Un esempio di dichiarazione di una costante può essere:

const float PIGRECO = 3. 14;

Da notare la presenza della parola chiave const che definisce il dato come costante.

CASTING E CONCLUSIONE

Visto il tipo di gestione dei dati in C, esso viene definito come linguaggio a tipizzazione statica, ovvero dove i tipi di dati vengono definiti all’inizio e mantenuti pressoché
costanti per la durata del software. Tuttavia esiste una pratica definita casting mediante la quale è possibile cambiare il tipo di un dato per poterlo adattare ad una certa circostanza. Il casting si avvale della sintassi del tipo:

variabile1 = ( tipo) variabile2

Ad esempio per ottenere un int da un valore float è possibile seguire il seguente codice di esempio:

float fltNumero = 2. 36;
int intNumero;

intNumero = (int) fltNumero;

In questo modo intNumero assumerà valore 2, ovvero la parte intera del numero float.

Conoscere i dati in C significa essere a conoscenza delle fondamenta di un linguaggio ed è perciò importante studiare bene l’argomento prima di addentrarsi in argomenti più avanzati.

Guida al C: parte 1 (Il resto del Pinguino)

La programmazione è sicuramente una delle possibilità più affascinanti che l’informatica può offrirci. Essa ci permette di essere come un genitore che, mediante le proprie parole e le proprie indicazioni, insegna al suo bambino come deve comportarsi con le sollecitazioni che giungono dal mondo esterno. Saper programmare non vuol dire solo conoscere le parole chiave che ci permettono di gestire i nuovi elementi della tecnologia ma vuol dire dare al comportamento umano e naturale una struttura interpretativa algoritmica. Chiunque inizia a programmare lo fa per uno scopo preciso che può variare dalla conoscenza personale alla passione, dal lavoro all’utilità. Chi si avvicina a questo mondo lo fa attraverso il linguaggio che ritiene più idoneo per le proprie esigenza e fortunatamente il mondo informatico pulula di scelte tra le quali poter vagliare quella migliore. Esiste però un linguaggio che nel corso della storia informatica ha saputo ritagliarsi il posto di padre di tutti i linguaggi, ovviamente sto parlando del C. Il C è il linguaggio più snobbato dai neofiti ma diventa indispensabile nel momento in cui lo si impara a conoscere e ad usare. Esso gode di particolari costrutti e particolari elementi che lo rendono adatto a tutti i tipi di obiettivi software. Il fine del nostro percorso sarà quello di imparare le basi di questo linguaggio in modo che il programmatore, che si ritrova a consultare queste pagine, sappia dare forma alle propria idee grazie alla propria macchina ed a questo linguaggio.

Perché scegliere il C

I motivi per cui bisogna scegliere il C sono molteplici e tutti egualmente importanti. Sicuramente un punto a suo favore è il fatto che esso sia di tipo compilato. Un linguaggio compilato è un particolare linguaggio di programmazione il cui codice viene passato ad un software detto compilatore che provvede a trasformarlo in linguaggio macchina. In questo modo si avrà un codice che comunica quasi direttamente con l’hardware su cui gira rendendo il proprio programma finale più performante è meno avido di risorse poichè costruito ad hoc per la macchina su cui si vuole che esso funzioni. Una caratteristica di rilevante importanza del C sono gli strumenti che esso propone. Grazie a dati come i puntatori, ad esempio, sarà possibile manipolare le variabili richiamandole direttamente dall’indirizzo preciso in cui risiedono in memoria. Sarà possibile creare elementi che ricalchino la propria idea di dato ridefinendo quelli già esistenti o sarà possibile procurarsi della memoria per le proprie variabili anche lì dove apparentemente pare impossibile. Infine non bisogna dimenticare che il C è multipiattaforma; questo vuol dire che il codice scritto su un computer con sistema operativo GNU/Linux può essere eseguito senza problemi e senza alcuna modifica su sistemi Microsoft, Mac, Solaris e via discorrendo. Ovviamente, però, bisogna tener presente che se nel proprio codice si implementano delle chiamate dirette a dei comandi di sistema tale proprietà viene a decadere.

Cosa serve per programmare in C

Programmare in C è, oltre che un piacere, anche uno stile di vita. Il C è volutamente minimale e tale scelta la si riscontra anche nell’atto pratico della programmazione. Per scrivere del buon codice in C non c’è necessariamente bisogno di IDE sofisticati o altri fronzoli grafici, basta un semplice editor di testo (anche VI è perfetto! ), il caro buon vecchio amico terminale ed ovviamente un compilatore. Anche se si pensa che tanti programmi siano scomodi per scrivere del codice, ci si renderà subito conto che invece questi strumenti, leggeri, sono molto pratici e svolgono perfettamente il loro dovere a differenza di un unico programma che cerca di emularli tutti. Scegliete per bene i vostri strumenti e studiante i manuali di tali programmi per imparare ad usarli perfettamente e poter scrivere così il vostro codice senza alcun problema.

Serve davvero imparare il C?

Molti programmatori autodidatti tendono a descrivere il C come un linguaggio ormai obsoleto e sostengono che impararlo da zero è inutile e porta via semplicemente del tempo prezioso. Non c’è nulla di più sbagliato! Il C, proprio per la potenza dei suoi strumenti e la pulizia dei suoi costrutti, è un’ottima base dalla quale partire anche per iniziare a comprendere alcuni argomenti che sono importanti indipendentemente dal linguaggio che si usa. Bisogna tener presente, in più, che molti linguaggi definiti “più moderni” sono ottenuti o vengono creati partendo proprio dal C. Il tanto blasonato C++ o anche il Java, altri non sono che discendenti del linguaggio di cui ci stiamo occupando.

Si inizia

Dopo tanti preamboli è giunto finalmente il momento di gustare un po’ di codice che ci servirà per fare amicizia con il C. Come ogni guida che si rispetti anche noi inizieremo dal più classico degli “Hello World”; il nostro intento sarà proprio quello di stampare la celeberrima frase all’interno del nostro terminale. Apriamo il nostro editor e copiamo all’interno il seguente codice:


Salviamo il file con il nome esempio.c nella nostra Home. Rechiamoci nel terminale e compiliamo il programma digitando il comando

gcc esempio. c – o esempio

seguito dal tasto invio. Se il terminale non restituisce errori possiamo lanciare il nostro programma con il comando

./esempio

Analizziamo il codice:

# include

Quando compiliamo un codice in C, esso, esegue un passaggio preliminare, ovvero viene sottoposto al precompilatore. Questo strumento si occupa di svolgere delle particolari azioni utili prima che il codice venga analizzato dal compilatore. Tutto ciò che concerne il precompilatore, viene scritto sottoforma di normale codice con la sostanziale differenza, però, che ogni rigo è preceduto dal simbolo #.
Una delle funzioni fondamentali del precompilatore è quella di includere all’interno del nostro codice le funzioni necessarie affinché esso possa funzionare. Il C infatti gode di un core davvero leggero e si basa sulla propria modularità per includere il necessario nel momento in cui se ne sente il bisogno. Dovendo richiamare la funzione printf diciamo al precompilatore di includere (istruzione include ) la libreria standard stdio. h poiché è lì che si trova la nostra funzione. Studieremo le librerie più avanti in maniera più approfondita.
int main( )

Ogni programma scritto in C ha bisogno di una particolare funzione che viene vista come punto di partenza dal quale il software, una volta compilato, inizia a girare. Questa funzione deve essere sempre presente e deve chiamarsi obbligatoriamente main . Un listato privo della funzione main non viene compilato e restituisce errore all’atto della compilazione. Come possiamo notare la parola main è preceduta da int ed è seguita dalle due parentesi tonde. Il termine int è un indicatore di tipo e ci indica che ogni qualvolta usciamo dalla funzione main per un qualsiasi motivo (errore immissione dati da parte dell’utente, fine del programma, aborto essa ci dovrà restituire necessariamente un valore intero, sia negativo che positivo. Per convenzione tale valore è indicato con 0 se il programma termina in maniera naturale e valori diversi da 0 se si presentano altri errori.
Le parentesi, invece, servono a ricordare che main è una funzione e il loro scopo verrà precisato nelle future lezioni della guida.
printf( “Hello World! \n”) ;
La funzione printf è una delle funzioni basilari del linguaggio C. Essa ci permette di formattare particolarmente del testo e di stamparlo sull’output standard del sistema. Il suo uso è semplice ma non esclude funzionalità avanzate. In questo caso specifico abbiamo detto al linguaggio di stampare la stringa ” Hello World ” passandola come parametro alla funzione. Si noti il termine di escape \n che indica di andare a capo subito dopo la parola “World”. Avremo modo di approfondire sia l’uso di printf che dei termini speciali di escape.

Return 0;

Come detto in precedenza, la funzione main deve restituire un intero quando si ha la necessità di uscire. Nel nostro caso stiamo dicendo, con il nostro codice, che abbiamo finito di fare ciò che serviva all’interno del main e possiamo uscire senza problemi. Si noti che il valore di ritorno è stato scelto come 0 per quanto detto prima. Tuttavia il suo valore è arbitrario purchè conforme al tipo dichiarato prima della funzione main.

{}

Le parentesi graffe svolgono nel C una funzione importante. Esse servono per delimitare graficamente (e non solo) l’inizio e la fine delle funzioni o dei blocchi logici. Omettere le parentesi provoca errore nella compilazione. Bisogna ricordarsi, pertanto, di controllare il codice in modo che ci siano tante parentesi graffe aperte quante ce ne sono chiuse. Si noti infine che al termine delle istruzioni viene posto un punto e virgola. Esso è fondamentale e non deve essere mai dimenticato poiché indica al sistema che l’istruzione (o il blocco di istruzioni) termina in quel punto. Ovviamente il lettore finale potrà benissimo usare comodamente il proprio sistema o un IDE se ritiene che esso possa facilitare l’apprendimento. Si tenga presente però che eventuali errori potrebbero essere dovuti al software di cui si dispone e non alla bontà del codice che si scrive.

Didattica C: typedef


Typedef è una di quelle funzioni del C che a prima vista appare insensata ma che nel corso della propria programmazione si rivela essere lo strumentino di cui non si fa più a meno.
Typedef è una particolare istruzione che permette di assegnare una nuova etichetta ad un tipo già esistente o ad uno creato mediante struttura o enumerazione. La sua versatilità ci permette di creare variabili del tipo definito, dichiarandole con la sintassi che si userebbe per i dati di tipo standard.

Immaginiamo, ad esempio, di voler rinominare il tipo di dati int con INTERO poichè siamo italiani e ci piace così e di voler creare una variabile di nome numero proprio di tipo int inizializzata al valore 5. Ciò è possibile semplicemente con il codice:

typedef int INTERO;
INTERO numero = 5;

L’utilizzo di tale istruzione è possibile anche con strutture o gruppi enumerativi.
Un esempio di codice può essere:

// definizione struttura
struct mia_struttura{ … valori…. };
// definizione del nuovo tipo
typedef struct mia_struttura STRUTTURA;
// creazione nuova struttura
STRUTTURA new_struttura;

o

// definizione enumerazione
enum mio_enum { … valori da definire …};
// definizione del nuovo tipo
typedef enum mio_enum ENUMERAZIONE
// creazione nuova enumerazione
ENUMERAZIONE new_enum;

Questa istruzione non richiede particolari trattamenti poichè è abbastanza semplice da utilizzare e da comprendere. È da precisare che typedef può essere utilizzato anche nello stesso momento in cui si crea, ad esempio una nuova struttura o enumerazione.

Evidenzio, come sicuramente si sarà già notato, che le etichette attribuite mediante typedef sono scritte in maiuscolo. Questa non è una regola o un’esigenza del codice ma è una prassi tra i programmatori per permettere ai nuovi tipi di dati di essere subito distinguibili da quelli standard in modo da rendere il codice comprensibile.