Академический Документы
Профессиональный Документы
Культура Документы
Quindi se io ho 10 euro in tasca, e se una Mela costa 1 euro, quante Mele potro’ comprare ?
Semplice, 10.
Scomponiamo il problema in modo “informatico”
DATI IN INGRESSO ( Dati Input)
HO 10 EURO
UNA MELA COSTA 1 EURO
Possiamo inoltre dire che i DATI hanno un VALORE (10, 1) ed un TIPO (euro, mele) ed occupano
uno SPAZIO
Bene, in informatica abbiano la stessa identica problematica: I DATI HANNO UN TIPO, HANNO
UN VALORE ed OCCUPANO UNO SPAZIO.
TIPI DI DATI
I tipi di dati con cui abbiamo a che fare durante la realizzazione di un programma sono:
Esempi di dati NUMERICI, sono l’età (40), il costo di un tazza di Caffe’ (0.85), la temperatura
invernale in Montagna (-12)
Esempi di dati di TIPO CARATTERE, il mio nome “Francesco”, la sezione della classe scolastica
alle elementari “C”
Esempi di dati tipo LOGICO sono, ho gli occhi azzurri ? NO. (SI/NO) o (VERO/FALSO) ossia
dati che possono assumere solo uno dei due valori (SI/NO – VERO/FALSO)
VALORI
I DATI possono contenere dei valori COSTANTI (es. il mio nome, il colore dei miei occhi, il
valore di PI GRECO) o VARIABILI (l’età, il costo della tazzina di caffe’).
SPAZIO OCCUPATO
Ogni DATO che deve essere gestito dal programma occupa uno spazio delle memoria RAM del
nostro Computer, la quantità di spazio occupata dal dato dipende dal TIPO DI DATO e non dal suo
VALORE.
Immaginiamo di avere una scatola delle scarpe, vuota.
Se la riempio di Mele, supponiamo, ne riesco a far stare 8
Se la riempio di Ciliegie, ne riesco a far stare 1000
Ovviamente posso mischiare le cose, e mettere nella scatola un po’ di Mele e di Ciliegie.
Bene, la MEMORIA RAM del nostro amato Computer è la SCATOLA DELLE SCARPE.
Piu’ la scatola è grossa piu’ riesco a farci stare dentro della roba.
Addirittura, in alcuni casi, la scatola deve essere “un minimo” grande, altrimenti non riusciro’ a
farci stare dentro neppure un solo oggetto (esempio UN ANGURIA!).
Ogni qual volta metto qualcosa nella scatola, lo spazio che ho a disposizione diminuisce, in base
allo spazio occupato dal nuovo oggetto appena inserito.
In casi estremi, arrivo a riempire la scatola (provocando un enorme rallentamento delle operazioni
del computer sino al blocco…).
Quando noi dobbiamo indicare quanto spazio abbiamo a casa nostra in una stanza, lo facciamo ad
esempio dicendo, 80 metri quadrati (non certo dicendo, ci stanno tre sedie, un divano e un tavolo).
Usiamo quindi un sistema che è accessibile a tutti e che tutti sono in grado di capire, usiamo una
unita’ di misura.
Per dire quanta è grossa la scatola delle scarpe del mio Mac in questo momento ho bisogno di una
unità di misura per esprimere tale valore (non posso certo usare i metri quadrati, peccato sarebbe
stato interessante… dire il mio Mac ha 4500 Mq ed il tuo ?).
Quindi quando usando il mio programma di videoscrittura preferito, scrivo CIAO significa che ho
usato 4 BYTES della mia scatola delle scarpe (RAM) del mio computer.
MULTIPLI
1024 BYTES => 1 KILOBYTES -> 1 Kb Mille
1024 Kb => 1 MEGABYTES -> 1 Mb Milioni
1024 Mb => 1 GIGABYTES -> 1 Gb Miliardi
SOTTOMULTIPLI
8 Bits = 1 BYTE
Se il nostro Computer ha 2 Gb Ram, significa che la scatola è grossa circa 2 miliardi di caratteri, ne
avete di spazio per scrivere…:)
Per capire quanta RAM ha il nostro computer, Mela e Informazioni su questo Mac.
Per i piu’ curiosi : lanciate Terminale (Applicazioni/Utilità) e digitare il comando top, vedrete
quanta memoria ha il vostro computer e quanta memoria utilizzano le varie applicazioni in
esecuzione.
Per oggi è tutto, vi saluto, vi ringrazio per il responso che avete dato alla mia iniziativa e spero di
essere stato sufficientemente chiaro nella spiegazione.
Dalla prossima puntata, inizieremo a fare qualcosa con XCODE, quindi, per chi ha voglia , tempo
ecc. scaricatelo dal sito della Apple.
Il programmatore che c'è in noi - Lezione 2 - DATI
In realta’ le cose sono un po’ piu’ complicate, il motivo di tale complicazione è dovuta al fatto che
la memoria Ram (la nostra famosa scatola delle scarpe) non è grande all’infinito e quindi piu’
riusciamo a ottimizzarne lo spazio meglio e’.
Bene, avete vinto al superenalotto e quindi cambiare casa, andate in un supermercato e prendete un
po’ di scatoloni di diverse dimensioni.
A questo punto, a casa, dovete mettere la vostra roba, nei vari scatoloni. Cosa fate ? Cercate di
ottimizzare l’uso delle scatole a disposizione riempiendole con gli oggetti piu’ indicati o per dirlo al
contrario scegliete lo scatolone adatto al tipo di oggetto che dovra’ contenere.
Ad esempio, non metterete un solo libro in uno scatolone enorme, oppure non strapperete un libro
pur di farlo entrare in uno scatolone troppo piccolo.
Bene, anche per la nostra Memoria Ram (e disco fisso ecc.) dobbiamo fare necessariamente una
ottimizzazione.
Questa ottimizzazione è possibile decidendo il tipo di dato idoneo per memorizzarne il futuro valore
, se ad esempio devo memorizzare l’eta’ di una persona, mi sarà sufficiente usare un tipo di dati che
consenta di contenere un valore tra 0 e 120, di conseguenza si tratta di un numero INTERO, in
questo caso SENZA SEGNO (non ha senso dire che una persona ha -5 anni…)
Se invece devo memorizzare quanti secondi sono passati da quando sono nato, ho bisogno di uno
scatolone piu’ grosso (in quanto il numero da memorizzare sara’ notevolmente piu’ grande).
Per farla breve, se devo memorizzare il costo del pieno della mia auto avro’ necessita di indicare un
valore REALE ossia con i numeri decimali (es. 63,58 ).
Tutto questo in “informatichese” si traduce in Tipi di Dati base i quali occupano una determinata
dimensione nella nostra Ram ma potranno contenere solo determinati valori sia come tipo
(carattere, intero, reale, booleano) sia come LIMITI (ossia il valore minimo che posso far stare in
quello scatolone ed il valore massimo).
Il tipo di dati piu’ semplice è il char, che puo’ contenere un solo CARATTERE o un numero che
a seconda se con o senza segno, potra’ essere compreso tra -128 e +127 oppure tra 0 e 255
Questo significa che se devo memorizzare l’eta’ di una persona potrei farlo usando un dato di tipo
char in quanto il valore è compreso tra -128 e 127, è chiaro che se una persona ha 130 anni (beato
lui…) non potremmo memorizzare la sua eta’.
Se ad esempio devo memorizzare l’inziale del mio nome, allora va bene un char perche’ e’ un
carattere (nel mio caso la F)
Davanti al nome del tipo di dato, puo’ comparire un “modificatore” del tipo, che e’ una ulteriore
parola da scrivere prima del nome del tipo e specifica ulteriori informazioni per il tipo di dato, ad
esempio se e’ senza segno (unsigned) o con segno (signed).
Quindi
e cosi’ via.
Modificatori
Nome Descrizione
signed Il numero intero è con segno
unsigned In numero interno è senza segno
long Il numero è molto grande (lungo)
short Il numero è piu’ piccolo del normale (corto)
Esempi
unsigned char = vuol dire che puo’ contenere un valore tra 0 e 255, ma non piu’ i valori negativi
unsigned long int = vuol dire un numero intero senza segno molto grande
Nel progetto allegato a questa puntata, trovate un programma con cui iniziare a giocare con queste
nozioni.
I piu’ curiosi, possono implementare il programmino che ho fatto, ad esempio, facendo calcolare la
dimensione di tipi di dati composti con i modificatori sopra descritti.
Compare
E selezioniamo COCOA APPLICATION
ed otteniamo
Dall’ispettore, è possibile modificare tutta una serie di
impostazioni ciccando nella COMBOBOX, tra cui ad esempio la
dimensione della finestra usando la voce Size della combobox.
E lo lascio cadere…
Come al solito, provvedo a posizionarlo ed eventualmente a
ridimensionarlo.
Do invio
Ed eccomi accontentato.
Ora ho nel MainMenu.nib anche un oggetto che si chiama
ilControllore che è il mio oggetto appena creato.
In modo da ottenere
Dato che in questo tutorial voglio gestire solo i primi due
(char max e char min) rifaccio nuovamente l’operazione.
Quindi, colleghiamoli…
Perche’ questo ?
Per ricavare tale valore, non faccio altro che usare i valori
costanti definiti dal file limits.h (che potete cercare sul
vostro Mac.)
#include <ppc/_limits.h>
#ifndef CLK_TCK
#define CLK_TCK __DARWIN_CLK_TCK /* ticks per second */
#endif
/*
* According to ANSI (section 2.2.4.2), the values below must be usable by
* #if preprocessing directives. Additionally, the expression must have the
* same type as would an expression that is an object of the corresponding
* type converted according to the integral promotions. The subtraction for
* INT_MIN and LONG_MIN is so the value is not unsigned; 2147483648 is an
* unsigned int for 32-bit two's complement ANSI compilers (section 3.1.3.2).
* These numbers work for pcc as well. The UINT_MAX and ULONG_MAX values
* are written as hex so that GCC will be quiet about large integer constants.
*/
#define SCHAR_MAX 127 /* min value for a signed char */
#define SCHAR_MIN (-128) /* max value for a signed char */
….
#import "ilControllore.h"
@implementation ilControllore
-(void) awakeFromNib{
NSString *unaStringa;
[limiteCharMax setStringValue:unaStringa];
[limiteCharMin setStringValue:unaStringa];
}
@end
Francesco Germinara
Info@germinara.it
www.germinara.it
Il programmatore che c'è in noi - Lezione 3 – XCODE Tutorial
Compare
E selezioniamo COCOA APPLICATION
ed otteniamo
Dall’ispettore, è possibile modificare tutta una serie di
impostazioni ciccando nella COMBOBOX, tra cui ad esempio la
dimensione della finestra usando la voce Size della combobox.
E lo lascio cadere…
Come al solito, provvedo a posizionarlo ed eventualmente a
ridimensionarlo.
Do invio
Ed eccomi accontentato.
Ora ho nel MainMenu.nib anche un oggetto che si chiama
ilControllore che è il mio oggetto appena creato.
In modo da ottenere
Dato che in questo tutorial voglio gestire solo i primi due
(char max e char min) rifaccio nuovamente l’operazione.
Quindi, colleghiamoli…
Perche’ questo ?
Per ricavare tale valore, non faccio altro che usare i valori
costanti definiti dal file limits.h (che potete cercare sul
vostro Mac.)
#include <ppc/_limits.h>
#ifndef CLK_TCK
#define CLK_TCK __DARWIN_CLK_TCK /* ticks per second */
#endif
/*
* According to ANSI (section 2.2.4.2), the values below must be usable by
* #if preprocessing directives. Additionally, the expression must have the
* same type as would an expression that is an object of the corresponding
* type converted according to the integral promotions. The subtraction for
* INT_MIN and LONG_MIN is so the value is not unsigned; 2147483648 is an
* unsigned int for 32-bit two's complement ANSI compilers (section 3.1.3.2).
* These numbers work for pcc as well. The UINT_MAX and ULONG_MAX values
* are written as hex so that GCC will be quiet about large integer constants.
*/
#define SCHAR_MAX 127 /* min value for a signed char */
#define SCHAR_MIN (-128) /* max value for a signed char */
….
#import "ilControllore.h"
@implementation ilControllore
-(void) awakeFromNib{
NSString *unaStringa;
[limiteCharMax setStringValue:unaStringa];
[limiteCharMin setStringValue:unaStringa];
}
@end
Francesco Germinara
Info@germinara.it
www.germinara.it
Il programmatore che c'è in noi - Lezione 4 – Variabili
NOTA:
Non esiste un criterio universalmente riconosciuto per
scrivere il nome della variabile.
unsigned char
indica il tipo di dato della nostra variabile
nAnniPapa
indica il nome della nostra variabile
=
indica l’operazione di assegnamento di un valore ad una
variabile
45
indica il valore che intendo assegnare alla variabile
;
indica la fine di una istruzione in linguaggio
C/C++/ObjectiveC
//
indica l’inizio di un commento. Da questo punto in poi tutto
quello che e’ scritto sulla riga e’ da considerarsi un
commento
Nella definizione iniziale di variabile ho detto che il
valore di una variabile puo’ cambiare durante l’esecuzione di
un programma, semplicemente basta assegnare ogni volta che si
vuole un nuovo valore usando l’operatore = (assegnazione).
Esempio
oppure
nAnniPapa = nAnniMamma+5;
//Assegnazione dati
nNumeroDiMele=100;
Saluti.
Il programmatore che c’e’ in noi – Lezione 5 – Introduzione ai Puntatori
Quando noi dichiariamo una variabile di un certo tipo di dato, non facciamo altro che “dire”
(tramite l’istruzione di programma) al compilatore, “mi riservi una certa quantita’ di memoria RAM
del computer”
La quantita’ di memoria che ci viene “riservata” dipende dal tipo di dato che la variabile stessa
dovra’ contenere e di conseguenza dal suo tipo dato specificato nella sua dichiarazione.
Tanto per capirci, se io dichiaro una variabile di tipo int il compilatore mi assegnera’ 4 bytes di
memoria Ram (questo lo possiamo verificare con l’utilizzo dell’operatore sizeof() come avevamo
fatto in una delle prime lezioni).
Esempio
Ma dove sono dislocati questi quattro bytes nella nostra memoria RAM ?
O per dirlo in termine informatico, quale e’ l’indirizzo nella nostra memoria RAM della variabile ?
Il programma, quando sara’ eseguito riservera’ lo spazio di memoria richiesto, come indicato dalla
nostra istruzione ma lo riservera’ in una zona di memoria RAM libera e a sua completa discrezione,
di conseguenza solo quando il programma sara’ in esecuzione saremo in grado si conoscere la
locazione di memoria (indirizzo) dove iniziano i BYTES che ci sono stati riservati.
Inoltre ad ogni riavvio del programma le locazioni dei nostri BYTES saranno quasi sicuramente
cambiate. La cosa certa e’ che i BYTES saranno CONSECUTIVI.
Prima di rispondere a tale domanda, realizziamo un semplice (spero) schema per riepilogare la
situazione.
Supponiamo che la nostra memoria RAM si presenti tutta libera ed in questa forma…
Indirizzo Byte
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 … 512MB
---------------------------------------------------------------
| | | | | | | | | | | | | | | |
---------------------------------------------------------------
Dopo l’esecuzione del programma, quando viene eseguita la nostra istruzione int
nNumeroRivista=0 la situazione cambia e supponiamo sia diventata questa
Indirizzo Byte
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 … 512MB
---------------------------------------------------------------
| | | | | | | | | | |0 |0 | 0 |0 | |
---------------------------------------------------------------
nNumeroRivista
come detto in precedenza, non siamo in grado di prevedere dove si troveranno le locazioni di
memoria occupate dai nostri 4 BYTES
Quindi, in questo caso, la risposta alla domanda precedente e’: la variabile nNumeroRivista e’ stata
posizionata (=allocata) alla locazione di memoria con indirizzo di partenza pari a 10.
Quindi per sapere realmente la locazione di memoria a cui e stata “posizionata” la nostra variabile
devo scrivere una istruzione di questo tipo
Spero che sino a qui, tutto sia sufficientemente chiaro… Se volete fare una pausa, e prendere un
caffe’, questo e’ il momento…
Le cose, si complicano…
1) Complicazione
Tutti gli indirizzi di memoria del computer sono indicati con un valore ESADECIMALE (HEX)
Noi normalmente siamo abituati a usare i numeri con base DECIMALE, ossia, contiamo da 0 a
9 e quando arriviamo a 10, incrementiamo le decine e ripartiamo con le unita’ 0 a 9 ecc. si dice
che la base e’ 10 (decimale appunto).
Quando invece si conta in esadecimale (indicato con Hex, h o 0X o 0x) la base e’ 16 e quindi si
conta da 0 a F (0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F) .
int indirizzoVariabile=0;
Questa riga viene compilata con un Warning del compilatore che recita pressappoco cosi’ “warning:
assignment makes integer from pointer without a cast”
Per memorizzare un INDIRIZZO devo avere una variabile di TIPO PUNTATORE ad TIPO DI
DATO DELLA VARIABILE DELLA QUALE MI INTERESSA L’INDIRIZZO.
NOTA
Quando io dichiaro una variabile di tipo int, ad esempio, la inizializzo al valore 0 (zero).
Quando la variabile e’ di tipo puntatore a quale indirizzo la inizializzo quando la dichiaro ?
La risposta e’ a NULLA, a NIENTE, a nessun indirizzo.
Per indicare questa condizione, in C e C++ si usa la parola chiave null mentre in ObjectiveC
si usa nil.
Per vedere il valore di indirizzo del puntatore, se usiamo la NSLog() gia’ incontrata in altre
occasioni, dovremo scrivere in questo modo
Da notare l’uso del simbolo %p per convertire il dato da puntatore (indirizzo) a stringa (caratteri).
In allegato trovate un programma di esempio con quanto trattato in questa parte e qualcosetta in
piu’ che sara’ trattato prossimamente.
Per i piu’ intraprendenti… nella finestra dell’applicazione e’ contemplata anche la conversione del
valore della variabile in valore binario, ma non e’ ancora implemenata … chi vuole puo’ farlo e
postare la propria soluzione.
La volta scorsa abbiamo dato la definizione di una variabile puntatore, di seguito riassunta
“Un puntatore e’ una variabile che contiene l’indirizzo di una locazione di memoria”
Vi arriva una busta chiusa, l’aprite, e trovate un bigliettino con un messaggio del tipo “presentati in
via Verdi,n.8”
Andate in “via Verdi,n.8” e ci trovate una villa bifamiliare.
Semplice, la busta e’ una variabile di tipo puntatore, infatti contiene l’indirizzo di un altro oggetto
(la villa bifamiliare) non l’oggetto stesso (ci vorrebbe una busta enorme…)
Se nella stessa busta, sostituisco il bigliettino, con un altro su cui c’e’ scritto “presentati in via Rossi
n.25”, significa che ho cambiato il valore alla mia variabile di tipo puntatore (ossia ho cambiato
l’indirizzo dell’oggetto a cui essa punta).
Supponiamo ora che io vi consegni la busta e vi dica anche, andare alla villa successiva a quella
indicata nella busta. Come vi comportate ?
Dipende.
Dipende dal tipo di oggetto a cui punta il puntatore, quindi se l’indirizzo indicato nella nostra busta
e’ relativo ad una villa bifamiliare, quando dico di andare all’indirizzo successivo, andro alla
successiva villa bifamiliare, quindi alla N. 10 di via Verdi.
Se invece, l’indirizzo indicato nella busta, e’ relativo (=punta) ad una villa singola, allora la villa
successiva e’ quella al N° 9 di via Verdi.
Questo e’ esattamente quello che avviene quando noi effettuiamo delle operazioni aritmetiche con i
puntatori.
Per concludere quella similitudine, tra puntatori e buste con indirizzi… vi segnalo un ulteriore
interessante particolare, la busta stessa e’ un oggetto; di conseguenza potrei avere una ulteriore
busta (variabile puntatore) che contiene l’indirizzo dove trovare una busta che contiene a sua volta
l’indirizzo della villa.
In effetti posso avere un puntatore che punta ad un puntatore che punta ad un puntatore … ecc.
Abbiamo gia’ incontrato dei particolari caratteri che hanno uno specifico significato proprio per
consentire di lavorare con le variabili di tipo puntatore.
Lo stesso simbolo * si usa per l’operatore che e’ complementare al quello & e serve per ottenere il
valore contenuto nell’oggetto indicato dall’indirizzo specificato.
Esempio:
//dichiaro le variabili
int nNumeroCivico=0; //variabile di tipo int
int* pUnNumeroCivico=nil; //variabile puntatore ad un int
int nUnAltroNumeroCivico=0; //variabile di tipo int
Gli operatori aritmerici sono dei particolari simboli che ci consentono di effettuare delle operazioni
aritmetiche sui numeri.
In linguaggio C/C++ e Objetive C gli operatori aritmetici sono
- Sottrazione
+ Addizione
* Moltiplicazione
/ Divisione
% Modulo (resto di una divisione tra numeri interi)
-- Decremento
++ Incremento
Esempi
int nAnnoNascita=1966;
int nAnnoCorrente=2006;
int nEta=0;
bIsDispari = numero % 2; //Uso operatore modulo %, per sapere se un numero e’ dispari o meno,
l’operatore modulo restituisce il resto della divisione tra interi, quindi se il numero e’ pari il resto
della divisione per 2 vale 0 altrimenti vale 1.
int nContatore=0;
nContatore++;
NOTE
In C/C++ e Objetive C posso usare anche gli operatori “composti” ossia operatore di assegnazione e
operatore aritmetico
Esempio:
nContatore+=5; //uso l’operatore composto per assegnare a nContatore il valore di nContatore + 5
nContatore-=3; //uso l’operatore composto per assegnare a nContatore il valore di nContatore - 3
nContatore*=3; //uso l’operatore composto per assegnare a nContatore il valore di nContatore * 3
nContatore/=3; //uso l’operatore composto per assegnare a nContatore il valore di nContatore / 3
Gli operatori di incremento e decremento possono essere posto davanti (prefisso) o dietro
(postfisso) la variabile, la differenza e’ nella valutazione della valore della variabile.
Nel caso in cui siano posti davanti, prima si applica l’operatore (ossia si incrementa o decrementa il
valore) e poi si valuta il valore della variabile, nel caso opposto, prima viene valutato il valore della
variabile e poi si procede ad applicare l’operatore.
Esempio:
int nContatore=0;
int nValore=0;
//Se scrivo
nValore =++nContatore; // nValore = Vale 1, nContatore Vale 1 (prima viene incrementato il valore
di nContatore da 0 a 1, quindi il valore viene assegnato alla variabile nValore)
Gli operatori possono essere unary,binary o ternary a seconda se hanno bisogno di un solo
operando, di due o di tre.
Gli operatori aritmetici unary sono – (cambio segno) ++ (incremento) -- (decremento) mentre gli
altri sono binary.
Per concludere il discorso sugli operatori arimetici, occorre dire che esiste una PRECEDENZA tra
operatori, ossia, se in una espressione sono presenti piu’ operatori, essi vengono valutati in base alla
loro precedenza. E’ possibile (anzi, vivamente consigliato…) utilizzare le parentesi tonde () per
MODIFICARE la precedenza degli operatori o per meglio chiarire le intenzioni che abbiamo per la
valutazione dell’espressione.
Alta
++ --
- (unary cambio segno)
*/%
+-
Bassa
Gli operatori allo stesso livello sono valutati dal compilatore partendo da sinistra verso destra.
Esempi:
int nValore=0;
int nPrimoNumero=5;
int nSecondoNumero=2;
int nTerzoNumero=4;
//Dato che ci sono le parentesi, la precedenza e’ modificata e quindi viene valutata in questo modo
//Prima viene incrementato il valore di nSecondoNumero (da 2 a 3)
//Poi ad esso viene sottratto il valore di nTerzoNumero (4) quindi 3 – 4 = -1
//Infine viene valutata l’espressione nPrimoNumero * -1 = 5 * -1 = -5
Quindi, l’espressione che ho usato negli esempi precedenti, dovrebbe essere scritta in un modo piu’
leggibile, simile a questo
int nValore=0;
int nPrimoNumero=5;
int nSecondoNumero=2;
int nTerzoNumero=4;
++nSecondoNumero;
Bene, siamo ora in grado di parlare della Aritmetica dei Puntatori, come indicato nel titolo di questa
lezione…
int nEta=10,nOldEta=0; //Dichiarazione multipla di variabili dello stesso tipo NOTARE l’utilizzo
del simbolo , (virgola) e’ un altro operatore del linguaggio C/C++ e ObjectiveC, che serve per
separare una lista di istruzioni
Se ora uso l’applicazione calcolatrice, e effettuo la sottrazione tra i due valori (sono esadecimali)
ottengo 0x4 ossia 4 in decimale. QUATTRO ? Il valore del contenuto nella mia variabile puntatore
si e’ incrementata di un colpo solo di 4 non di 1 come probabilmente pensavamo.
Vi ricordate l’esempio fatto qualche riga piu’ in alto delle villette bifamiliari, bene, con l’istruzione
++ abbiamo detto alla nostra variabile puntatore di puntare all’indirizzo successivo ad una variabile
di tipo INT (dato che le variabili INT occupano 4 bytes in memoria, lo avevamo gia’ verificato con
l’uso dell’operatore sizeof() ricordate?) e’ quindi corretto che il valore dell’indirizzo si sia
incrementato di 4 bytes. Ecco perche’ e’ importante sapere a che tipo di variabile punta una
variabile puntatore.
Se invece dell’istruzione
pIndirizzoDiEta++;
scrivo
pIndirizzoDiEta+=4; //Il nuovo indirizzo sara’ 4 locazioni successive all’indirizzo di partenza
//Dato che pIndirizzoDiEta e’ un puntatore che punta ad un int e che un int occupa 4 bytes in
//memoria, con l’istruzione precedente mi sono spostato di 4 * 4 bytes = 16 Bytes.
Se invece dichiaro un puntatore ad un char, dato che un char occupa un solo byte, mi sarei spostato
di 4 bytes e non di 16.
Esempio
int nEta=10;
char *pIndirizzoUnByte=nil; //Variabile puntatore a char
int* pIndirizzoDiEta=nil; //Variabile puntatore a int
NOTA
L’istruzione
pIndirizzoUnByte = pIndirizzoDiEta;
genera un warning del compilatore, in quanto si stanno assegnando dati di tipi diversi (un valore di
puntatore ad interi viene assegnato ad un puntatore a char). E’ comunque una operazione lecita, per
evitare la segnalazione occorre utilizzare un “CAST” di tipo, ossia una operazione di tipo di dato
(nel nostro caso da tipo puntatore ad intero a puntatore a char).
L’istruzione nella forma corretta e’ quindi
pIndirizzoUnByte = (char*) pIndirizzoDiEta; //uso del cast (char*) per convertire il tipo di dato
NOTA
Occorre usare le parentesi, altrimenti causa precedenza degli operatori, invece di incrementare il
valore puntato dall’indirizzo incremento l’indirizzo stesso, come visto in precedenza.
*pIndirizzoDiEta++; //Incrementa l’indirizzo e poi ricava il contenuto a cui punta il nuovo indirizzo
Concludo con un rapido riepilogo di quanto visto.
Saluti, e a presto.
Il programmatore che c’e’ in noi – Lezione 7 – Arrays
Quante volte avete aperto e chiuso un cassetto di una cassettiera ? Molte, credo.
Bene, una cassettiera altro non e’ che un array di cassetti.
Un array altro non e’ che un INSIEME DI ELEMENTI DELLO STESSO TIPO INDIVIDUATI
DA UNO STESSO NOME. Per accedere ad un determinato elemento dell’array usiamo un indice.
Nel esempio della nostra cassettiera, diro’ apri il primo cassetto, il secondo ecc.
[1° cassetto]
[2° cassetto]
Gli arrays possono avere piu’ di una dimensione, di conseguenza, per accedere ad un determinato
elemento si usano indici per ciascuna dimensione.
Se la nostra cassettiera e’ formata da tre colonne di cassetti, disposti su due righe, in tutto avremo 3
x 2 cassetti = 6 cassetti.
Per accedere ad un determinato cassetto, dovro’ indicare il numero del cassetto ed in numero della
riga, esempio apri il primo cassetto della prima riga, apri il secondo cassetto della seconda riga.
Quindi possiamo avere arrays monodimensionali (una sola colonna o una sola riga), bidimensionali
(una riga ed una colonna) e multidimensionali.
Nella maggior parte delle applicazioni, ci troviamo a lavorare con arrays monodimensionali e
bidimensionali
Nel linguaggio di programmazione C/C++ e ObjectiveC l’indice del primo elemento e’ ZERO e
tutti gli elementi dell’arrays occupano LOCAZIONI DI MEMORIA CONSECUTIVE nella Ram
del nostro Mac. Il primo elemento corrisponde all’indirizzo di memoria piu’ basso, l’ultimo a quello
piu’ alto.
Singola dimensione
TYPO_VARIABILE NOME_VARIABILE [NUMERO_ELEMENTI];
Due dimensioni
TYPO_VARIABILE NOME_VARIABILE [NUMERO_RIGHE] [NUMERO_COLONNE];
Operazioni sugli arrays
int nEta=0;
nEta = nEtaPersone[5]; //nEta, vale 37 legge il valore del 6° elemento dell’array e lo assegna a nEta
MOLTO IMPORTANTE
1) Prestate molta attenzione al valore degli indici degli array, il compilatore non e’ in grado di
segnalare un uso errato del indice. Quindi se io ho un array di 50 caratteri, ed uso un indice che
supera il valore di 49, il compilatore non mi segnala il problema, ma quando l’applicazione sara’ in
esecuzione, potrebbe provocare dei malfunzionamenti all’applicazione stessa e farla chiudere in
maniera inaspettata.
2) La sola dichiarazione delle variabili di tipo arrays non comporta la loro inizializzazione, quindi il
valore in esso contenuto e’ sconosciuto. E’ sempre opportuno INIZIALIZZARE i valori degli
arrary, semplicemente assegnando un valore ad ogni singolo elemento dell’array.
L’inizializzazione puo’ essere fatta nella dichiarazione stessa o mediante uso dell’operatore di
assegnazione (=) .
Esempio di inizializzazione di una array nello stesso momento della sua dichiarazione
Come visibile dall’esempio, per inizializzare i valori, devo usare l’operatore di assegnazione (=)
quindi aprire un blocco usando il simbolo { specificare i singoli valori per ogni indice del nostro
array, separandoli da una virgola ed infine chiudere il blocco con }
Dato che si tratta di un array di char, i valori da usare per l’inizializzazione devono essere delle
costanti di tipo char e si indicano con il valore di carattere racchiuso tra apici (non VIRGOLETTE,
ma APICI, lo stesso simbolo dell’apostrofo)
Per inizializzare arrays bidimensionali, seguo la stessa regola, raggruppando ulteriormente ciascun
gruppo di valori con un inizio e fine blocco ( { } ) per ciascuna riga.
BOOL bBattagliaNavale[3][4]=
{
{0,0,0,0}, //inizializzo la prima riga
{0,0,0,0}, //inizializzo la seconda riga
{0,0,0,56} //inizializzo la terza riga
};
Nelle lezioni precedendi abbiamo visto l’uso dei puntatori e della relativa aritmetica per accedere
alle variabili. Dato che gli array di cui stiamo parlando sono collezioni di variabili dello stesso tipo,
posso accedere ai dati di un array (quindi ai vari elementi) usando un puntatore invece che l’indice.
//Calcolo l’indirizzo di memoria del primo elemento dell’array usando l’operatore &
pElemento = & nCosti[0]; //Calcola l'indirizzo della variabile relativa al primo elemento dell'array
E cosi via. Attenzione a non superare il numero di elementi precedentemente dichiarati nella’array.
Esempio:
Saluti.
Il programmatore che c’e’ in noi – Lezione 8– Array di caratteri, stringhe e costanti carattere
Nel corso delle precedenti puntate abbiamo imparato (spero) a conoscere gli array sia di numeri che
di caratteri. Nella creazione di programmi si ha spesso a che fare con del testo, il linguaggio C non
ha un proprio tipo di dati per gestire il testo, ma usa gli array di caratteri terminati da uno speciale
carattere chiamato NUL. (che vale 0 decimale 0x0 esadecimale ‘\0’ costante carattere)
nome[0]=’F’;
nome[1]=’r’;
nome[2]=’a’;
nome[3]=’n’;
nome[4]=’c’;
nome[5]=’o’;
nome[6]=’\0’;
L’istruzione
nome[6]=’\0’;
puo’ essere tranquillamente sostituita con
nome[6]=0x0; //NUL valore esadecimale
oppure
nome[6]=0; //NUL valore decimale
Tutte le lettere e le cifre che utilizziamo, possono essere rappresentate da una costante di tipo
carattere oppure dall’equivalente codice ASCII.
Esempio:
Il carattere A si indica con ‘A’ come costante carattere oppure come valore numerico 65 decimale
in codice ASCII.
Tra le costanti carattere segnalo quelle particolari, ossia codici che non hanno un equivalente nella
nostra lingua ma hanno un significato speciale se usate nei programmi e che sono rappresentate dal
fatto di avere il simbolo BACKSLASH \ seguito da un carattere il tutto racchiuso tra singoli apici.
Tabelle delle costanti carattere con backslah
Codice Significato
‘\b’ Backspace
‘\f’ Form feed
‘\n’ New line
‘\r’ Carriage return
‘\t’ Horizontal Tab
‘\”’ Double quote
‘\’’ Single Quote
‘\\’ Backslash
‘\xN’ Hexadecimal constant N
‘\N’ Octal constant N
La costante NUL di cui abbiamo parlato in precedenza, quindi altro non e’ che ‘\0’ ossia zero il
ottale (che vale comunque zero in decimale…)
Un ultima considerazione, la costante ‘\n’ in realta’ e’ formata da due caratteri il carattere Carriage
return (0xA) o 10 ed il carattere line feed (0xD) o 13, e generalmente indica la fine di una riga in
un file di testo (a volte dipende dal sistema operativo...).
Per gestire le stringhe in C language, si utilizzano delle funzioni di libreria, quali strcpy() per
assegnare un valore, strcmp() per confrontare due stringhe e printf() per visualizzare il valore di una
variabile di tipo stringa. Per informazioni sull’uso delle funzioni di manipolazione delle stringhe, e’
sufficiente cercare in internet, troverete innumerevoli esempi e spiegazioni.
Dato che l’obiettivo e’ quello di programmare in Cocoa, vi anticipo che tramite Cocoa e quindi
Objective C e’ comunque possibile gestire le stringhe come array di caratteri ed usare le funzioni
standard di libreria, ma Cocoa ha una apposita CLASSE, di nome NSString e sue derivate,
NSMutableString ecc. per gestire le stringhe, di conseguenza ne parleremo quando inizieremo a
parlare di Cocoa.
Saluti.
Il programmatore che c’e’ in noi – Lezione 9– Strutture, Unioni, Enumerazioni
La nostra carta di identita’ contiene un insieme di dati di diverso tipo e di diverse dimensioni.
Sono presenti dati quali Cognome (stringa o array di caratteri) Nome (stringa) Comune di Nascita
(stringa) Data di nascita (immaginiamola divisa in tre interi, anno, mese, giorno) Altezza (double)
ecc.
La cosa veramente importante e’ che in un solo tipo di dato (la carta di identita’) sono state
raggruppate diverse informazioni (dati) ma che sono in qualche modo “logicamente” collegate tra di
loro.
Pensate se invece di avere un unico documento con tutti i dati identificativi, avessimo un
documento per ciascun dato (una carta che specifica il Nome, una il Cognome, una dove siamo Nati
…) mentre e’ estremamente comodo gestire un solo documento che contenga tutte le informazioni
necessarie per l’identificazione di una persona.
Una struttura e’ una collezione di variabili, anche di tipo e dimensioni differenti, che possono essere
gestite tramite un solo nome.
In modo da garantire un modo semplice ed immediato il legame logico che i dati, presenti nelle
singole variabili, hanno tra di loro e per semplificarne la gestione.
Per definire una struttura dati, si utilizza la keyword struct propria del linguaggio C.
Le sintassi sono:
In alternativa e’ possibile dichiarare sia la struttura sia la/le variabili che saranno di tale tipo in una
sola sintassi
struct NOMESTRUTTURA {
TIPO NOMEVARIABILE;
TIPO NOMEVARIABILE;
…
}ELENCONOMIVARIABILI; //GLI ELENCHI SONO SEPARATI DA VIRGOLA
Se, abbiamo bisogno di gestire 100 carte di identita’, conviene usare un ARRAY DI STRUTTURA
struct NOMESTRUTTURA {
TIPO NOMEVARIABILE;
TIPO NOMEVARIABILE;
…
}NOMEARRAY[DIMENSIONE]; //DICHIARO un certo numero (DIMENSIONE) di strutture
accessibili tramite indice dell’array NOMEARRAY,
ESEMPIO (1)
Ogni volta che nel programma avro’ bisogno di una nuova carta di identita’ da gestire, sara’
sufficiente scrivere
In alternativa, se so gia’ prima che gestiro’ solo un certo numero di carte di identita’, posso
dichiarare tutto in una sola volta,quindi
ESEMPIO (2)
//Dichiaro come e’ costituita la struttura e due variabili di tale tipo di dato (cartaIdentita)
struct cartaIdentita{
char nome[101]; //stringa per contenere al massimo 100 caratteri (+1 = NUL)
char cognome[101];
char comuneResidenza[51];
int nAnnoNascita; //Anno aaaa
int nMeseNascita; // Mese mm
int nGiornoNascita; //Giorno gg
double nAltezza; //Altezza in metri,centimetri
} CartaIdentitaFranco, CartaIdentitaMarco;
Nel caso in cui, abbiamo bisogno di gestire 1000 carte di identita’, dichiaro un array
ESEMPIO (2)
//Dichiaro un array di strutture, oltre alla definizione della struttura stessa
struct cartaIdentita{
char nome[101]; //stringa per contenere al massimo 100 caratteri (+1 = NUL)
char cognome[101];
char comuneResidenza[51];
int nAnnoNascita; //Anno aaaa
int nMeseNascita; // Mese mm
int nGiornoNascita; //Giorno gg
double nAltezza; //Altezza in metri,centimetri
} carteIdentitaClienti[1000]; //Dichiaro un array di 1000 strutture di tipo cartaIdentita
Cosa avviene quando definisco in dato di tipo struct e creo un istanza di tale variabile:
Dove e’ posizionata in memoria ? Quanta memoria occupa ?
Quindi per conoscere la locazione di memoria in cui si trova la nostra struttura in questo modo
NSLog(“Indirizzo della variabile : %p”,& CartaIdentitaFranco);
Per accedere ai membri della struttura (le variabili che la compongono) si utilizza l’operatore .
(punto)
NOMEVARIABILESTRUTTURA.NOMEVARIABILEMEMBRO
Esempio
Per compilare i dati della struttura CartaIdentitaFranco
Dato che alcuni membri della struttura sono delle stringhe C (array di caratteri), uso la funzione
standard C per assegnare un testo ad una variabile stringa ossia
strcpy(char *strDestinazione,char *strOrigine)
strcpy(CartaIdentitaFranco.cognome,”Germinara”);
strcpy(CartaIdentitaFranco.comuneResidenza,”Pinerolo”);
CartaIdentitaFranco.nAnnoNascita=1966; //Assegno il valore 1966 alla variabile nAnnoNascita di
tipo INT
CartaIdentitaFranco.nMeseNascita=2; //Assegno il mese
CartaIdentitaFranco. nGiornoNascita =20; //Assegno il giorno
CartaIdentitaFranco.nAltezza=1.72; //Assegno altezza
NSLog(“Carta di identita’: Cognome: ‘%s’ Nome: ‘%s’ Residente: ‘%s’ Nato il: %02d/%02d%04d
Alto: %6.2f”, CartaIdentitaFranco.nome, CartaIdentitaFranco.cognome, CartaIdentitaFranco.
comuneResidenza, CartaIdentitaFranco. nGiornoNascita, CartaIdentitaFranco. nMeseNascita,
CartaIdentitaFranco. nAnnoNascita, CartaIdentitaFranco. nAltezza);
Per accedere alle variabili di un array di struttura, e’ sufficiente indicare l’indice dell’array tra
parentesi quadre, prima di usare l’operatore .
Esempio
#define MAX_PERSONE 1200 //definisco una costante numerica
struct cartaIdentita elencoVotanti[MAX_PERSONE]; //Istanzio un array di strutture cartaIdentita
//Per accedere ai dati della prima carta di identita’ uso indice 0, quindi
strcpy(elencoVotanti[0].nome,”Marco”);
strcpy(elencoVotanti[0].nome,”Coisson”); //spero non me ne voglia Marco per aver usato i dati
//in queso esempio
elencoVotanti[0]. nAltezza = 1.74; //Non ne ho idea… invento…
…
Una union e’ una zona di memoria condivisa tra due o piu’ variabili anche di tipo diverso tra di
loro, ed e’ molto simile ad una struct (di cui abbiamo parlato la volta scorsa) sia nella dichiarazione
sia nell’utilizzo.
Per essere chiari, ecco un esempio che compara una struct con una union.
struct datiProdotto{
char nome[21]; //Nome del prodotto 20 char
char tipo[11]; //Tipologia
double prezzo; //Prezzo
}unaStrutturaProdotto;
//Totale Memoria Occupata = 21 BYTE (la variabile che occupa piu' spazio e'
nome di 21 Byte)
//Bene, 24 e' comunque vicino a 21, che e' il valore che avevo previsto, ma
perche' e 24 ?
//Tale comportamento relativo alle struct ed alle union e' chiamato "BYTE
ALLIGNED" e puo' essere
//modificato tramite opportune opzioni del compilatore.
//Consiglio a chi fosse interessato di consultare il manuale del compilatore
//DATO CHE LA ZONA DI MEMORIA E' LA STESSA, QUANDO ASSEGNO IL VALORE A TIPO,
VADO
//AUTOMATICAMENTE A MODIFICARE IL VALORE DI NOME
//In realta' in memoria dovrei avere questa situazione
//0-------------------------------24
//Lamponi e Fragole'\0' Dopo la prima assegnazione
//Frutta'\0' e Fragole'\0' Dopo la seconda assegnazione
//Dato che sono delle stringhe C, ossia null terminated, chiuse quindi dal
carattere 0x00 (NUL)
//quando chiedo la stampa del contenuto della variabile con NSLog() ottengo
Frutta
NSLog(@"Indirizzo unaStrutturaProdotto.nome =
0x%x\n",&unaStrutturaProdotto.nome);
NSLog(@"Indirizzo unaStrutturaProdotto.tipo =
0x%x\n",&unaStrutturaProdotto.tipo);
NSLog(@"Indirizzo unaStrutturaProdotto.prezzo =
0x%x\n",&unaStrutturaProdotto.prezzo);
Ora che abbiamo appreso come funziona una union, la domanda e’ “a cosa serve ?”
Bene, diciamo che e’ molto utile per fare delle conversioni dati, quando si lavora con i singoli byte
o bit.
Esempio:
Ho un numero intero, tale numero e’ rappresentato in memoria (sul nostro Mac) da 4 BYTE (lo
abbiamo visto ad inizio corso), voglio conoscere il valore dei singoli byte.
Una possibile soluzione consiste nell’utilizzare una union.
In pratica “sovrappongo” una variabile int e un array di 4 unsigned char.
Bene, ovviamente se modifico il valore di un byte della union, cambiera’ il valore della variabile
intero in essa presente.
Saluti.
Il programmatore che c’e’ in noi – Lezione 11– Enumerazioni e Tipi dati utente
Una enumerazione e’ un elenco di costanti intere a cui, per ciascun valore numerico, e’ stato
assegnato un nome .
Nella realizzazione di programmi, ci troviamo spesso, per comodita’, a dover utilizzare dei valori
numerici interi, consecutivi (0,1,2,3...) per rappresentare determinati valori che numerici non sono.
Ad esempio un elenco di colori, un elenco di categorie di libri o l’elenco delle monete relative
all’Euro (5,10,20,50 centesimi, 1,2 euro).
Per motivi di LEGGIBILITA’ conviene utilizzare una COSTANTE numerica (che ha quindi un
NOME) piuttosto che utilizzare il solo valore numerico che rappresenta ciascun elemento
dell’elenco.
Nota:
Con il termine Leggibilita’ si intende sostanzialmente questo:
- chi legge il sorgente di un programma non e’ detto che sia la stessa persona che lo ha scritto
- a distanza di anni potreste riprendere in mano un vecchio programma per modificarlo
Conosciamo gia’ un modo per poter fare questo (assegnare un NOME ad un valore numerico),
utilizzando la parola chiave #define NOMECOSTANTE valore
Esempio:
#define ROSSO 2
#define VERDE 3
ma esiste un sistema ancora piu’ efficace: l’uso della parola chiave enum.
enum tag{
nomeelemento,
nomeelemento,
nomeelemento…
}listavariabili;
Esempio:
Una enumerazione di categorie di libri
enum genere{
fantascienza,
giallo,
avventura,
fantasia,
informatica
} genereDiLibro;
Tramite questa dichiarazione, ogni elemento della enum ora e’ rappresentato da un valore numerico
intero, a partire da zero e che si incrementa di 1 per ogni elemento successivo della lista.
Controlliamo se e’ vero …
Da XCODE creaimo un nuovo progetto, cocoa application, quindi nel file main.m andiamo a
dichiarare la nostra enum e con NSLog vediamo i valori numerici corrispondenti dei vari elementi.
Bene, inizializziamola
genereDiLibro=informatica; //Inizializzo la variabile con un elemento della enum
Verifichiamo…
[Session started at 2006-07-02 10:33:01 +0200.]
2006-07-02 10:33:01.488 Enumerazioni[966] Valore dell'elemento informatica = 4
2006-07-02 10:33:01.488 Enumerazioni[966] Valore della variabile genereDiLibro:
7468
2006-07-02 10:33:01.488 Enumerazioni[966] Valore della variabile genereDiLibro:
4
In sintesi, ora e’ possibile utilizzare il nome degli elementi della enum, in qualsiasi espressione
invece di usare il valore numerico, migliorandone la leggibilità e riducendo la possibilità di
errori nel programma.
Esempio:
Se devo controllare se un dato libro e’ di informatica scrivero’ qualcosa del genere …
if( genereDiLibro == informatica)
NSLog(@"Genere di libro: informatica");
if( genereDiLibro == 4)
NSLog(@"Genere di libro: informatica");
Esempio
enum colors{
rosso=100,
verde,
gialloC, //Ho gia’ giallo definito nella enum genere devo dare un altro nome !
marrone=0,
nero,
bianco
};
2006-07-02 10:40:51.803 Enumerazioni[994] Rosso: 100 Verde: 101 Giallo: 102 Marrone: 0 Nero:
1 Bianco: 2
Nella vita reale, spesso amiamo dare nomi diversi alle persone o alle cose, per renderle piu’ facili da
ricordare o per “evidenziarne” delle caratteristiche.
Bene, in informatica possiamo fare anche questo, con i nostri tipi di dato.
Se, ad esempio, invece di usare il nome double per identificare una variabile di tipo numerica reale
che poi uso per dichiarare delle variabili relative al mio stipendio, voglio creare un tipo di variabile
che si chiami stipendio devo usare la parola chiave typedef per creare un mio tipo di dati
personalizzato.
La sintassi e’ la seguente
typedef nometipodatobase nuovonome;
quindi, per l’esempio citato sopra:
typedef double stipendio; //Creo un nuovo tipo dato che si chiama stipendio
Per utilizzarlo
stipendio meseGennaio=120.0;
stipendio meseFebbraio=180.0;
stipendio meseMarzo=220.0;
stipendio medio=0.0;
Se il programma deve funzionare invece che su un Mac su un sistema che non conosce il tipo dati
double, ma usa solo float, e’ sufficiente cambiare la sola istruzione typedef double stipendio;
con typedef float stipendio; e tutto funzionerà regolarmente.
double meseGennaio=120.0;
double meseFebbraio=180.0;
double meseMarzo=220.0;
dobbiamo cambiare tutte le istruzioni dove compare il tipo dato double con il tipo dato float,
operazione piu’ lunga e piu’ soggetta ad errori.
Normalmente non si procede alla ridefinizione dei tipi di dati base a meno che non si abbia gia’ in
mente di creare un software portabile su piu’ piattaforme.
Un uso molto comune del typedef e’ invece quello di assegnare un proprio tipo di dato ad un
struttura dati.
In una delle precedenti puntate abbiamo esaminato le struct, che sostanzialmente ci consentono di
“raggruppare” delle variabili logicamente legate tra di loro.
struct cartaIdentita{
char nome[101]; //stringa per contenere al massimo 100 caratteri (+1 = NUL)
char cognome[101];
char comuneResidenza[51];
int nAnnoNascita; //Anno aaaa
int nMeseNascita; // Mese mm
int nGiornoNascita; //Giorno gg
double nAltezza; //Altezza in metri,centimetri
};
Questa e’ la definizione della nostra struttura dati da gestire, come abbiamo visto la volta scorsa,
ogni volta che mi serve una variabile per contenere tale struttura dati, devo fare una dichiarazione di
questo tipo
quindi ora per creare la variabile di tipo cartaIdentita, e’ necessario indicare solo
cIdentita CartaIdentitaFranco;
Riepilogo la differenza:
1) senza usare typedef
//Assegno i valori …
strcpy(CartaIdentitaFranco.nome,”Francesco”);
…
2) usando typedef
cIdentita CartaIdentitaFranco;
cIdentita CartaIdentitaRossi;
cIdentita CartaIdentitaMarco;
//Assegno i valori …
strcpy(CartaIdentitaFranco.nome,”Francesco”);
…
Il vantaggio che abbiamo e’ quello di scrivere meno (non devo ripetere struct ogni volta) e di
rendere un codice piu’ leggibile.
Generalmemente nel momento stesso un cui si definisce la struttura, si assegna il nome con
typedef, cosi’ risparmio ulteriori istuzioni.
cIdentita fg;
strcpy(fg.nome,"Francesco");
NSLog(@"nome: %s",fg.nome);
Questo e’ un esempio preso pari-pari dalla classe NSButtonCell, che definisce i possibili tipi di
pulsanti utilizzabili tramite Cocoa.
NSMomentaryPushButton = 0,
NSMomentaryLight = 7
} NSButtonType;
Questi significa che ogni volta che nel programma voglio impostare un tipo di pulsante potro’
utilizzare il tipo dato NSButtonType.
Esempio:
Con questa puntata si conclude il ciclo relativo ai dati ed alle variabili, dalla prossima iniziamo a
parlare delle istruzioni del linguaggio. (STATEMENTS).
Saluti.
Il programmatore che c’e’ in noi – Lezione 12– Statements
Ha un inizio, uno svolgimento ed un termine e ciascuna di queste parti si evolve in modo diverso
in base a come si presentano determinate situazioni o eventi.
Gli statements, non sono altro che istruzioni che interessano lo svolgimento di alcune parti del
programma, potremmo paragonarli a delle azioni che possono essere compiute e che influenzano lo
svolgimento del programma stesso.
Un programma ha sempre un inizio, e nel nostro caso (objectivec & cocoa) e’ rappresentato da
queste poche righe inserire in modo automatico da xcode, nel file main.m
#import <Cocoa/Cocoa.h>
Cosa significa, praticamente viene creato (istanziato) un primo oggetto di tipo (NSApplication).
Tale oggetto rappresenta il punto di risveglio del programma, o meglio, il punto di INGRESSO nel
programma, tale oggetto e di conseguenza il programma restera’ attivo siano a quando l’utente non
decidera’ di CHIUDERE l’applicazione.
La chiusura dell’applicazione corrisponde alla parte finale della storiella iniziale (…poi vado a
dormire).
Per verificare che, quando eseguo un programma, appena esso e’ eseguito il codice e’ proprio
quello indicato sopra, facciamo un esperimento.
Usiamo l’istruzione NSLog(@"Programma partito"); per far scrivere nel file di Log
dell’applicazione un messaggio quando il programma e’ eseguito.
#import <Cocoa/Cocoa.h>
//3: esce dal blocco corrente (un blocco e’ una parte di codice compresa tra
aperta e chiusa parentesi graffa) restituendo il valore della variabile
valoreRestituito
return valoreRestituito;
#import <Cocoa/Cocoa.h>
int main(int argc, char *argv[])
{
NSLog(@"Programma partito");
#import <Cocoa/Cocoa.h>
NSLog(@"Programma terminato");
return 0;
}
Compiliamo ed eseguiamo, ora il risultato e’ che NON compare nessuna finestra, il programma
parte e termina istantaneamente; e’ esattamente cio’ che gli abbiamo chiesto di fare, ossia
SVEGLIATI, scrivi PROGRAMMA PARTITO, scrivi PROGRAMMA TERMINATO,vai a
dormire.
Il run log e’ il seguente
[Session started at 2006-07-20 23:00:06 +0200.]
2006-07-20 23:00:06.903 statements[494] Programma partito
2006-07-20 23:00:06.903 statements[494] Programma terminato
Notare la riga che dice che il programma statements e’ uscito (e’ terminato) con lo stato 0! Zero e’
infatti il valore che noi abbiamo indicato nella nostra istruzione return. (se metto 23…)
#import <Cocoa/Cocoa.h>
NSLog(@"Programma terminato");
return 23;
}
Il codice di uscita del programma puo’ essere intercettato da altri programmi o dal sistema
operativo per valutarne il valore ed effettuare o meno determinate attività, ma questo non ci
interessa (...per il momento…)
vado in bagno,
vaiInBagno(); //Eseguo la funzione
doccia,
faiDoccia(); //Eseguo la funzione
colazione
faiColazione();//Eseguo la funzione
BOOL bHoLeChiaviDellaMacchina=FALSE;
while(bHoLeChiaviDellaMacchina!=TRUE){
bHoLeChiaviDellaMacchina=cercaChiaviDellaMacchina();
}
BOOL bSemaforoVerde=FALSE;
if(bSemaforoVerde == FALSE){
aspetto();
}else{
parcheggio();
}
Conclusione:
In questa puntata di introduzione agli statements ho cercato di spiegare con un esempio cosa sono e
a cosa servono le varie istruzioni e parti del programma.
Nella prossima puntata, tratteremo i vari statements singolarmente, analizzandone la sintassi ed il
modo di utilizzo in un programma.
Quante volte nell’arco della nostra giornata ci troviamo a dover scegliere, cosa fare in funzione del
verificarsi o meno di determinate condizioni.
Probabilmente e’ una delle attivita’ che facciamo piu’ di frequente, a volte inconsciamente.
Alcune scelte sono molto semplici, banali… se piove prendo l’ombrello, se ho fame mangio, altre
sono piu’ complicate e richiedono la valutazione di differenti condizioni … per attraversare la
strada, considero la presenza o meno di veicoli, la loro velocita’ e la mia, la distanza che devo
percorrere per attraversare e se c’e’ un semaforo, se e’ verde.
In informatica e nella programmazione le cose non cambiano; spesso, molto spesso, dobbiamo
decidere cosa fare in funzione di una condizione (caso semplice) o di molte condizioni (caso piu’
complicato).
Col termine condizione (o test) intendo la valutazione di una espressione che puo’ dare come
risultato sono VERO (TRUE) o FALSO (FALSE).
Esempi
Il semaforo e’ rosso ? (condizione o test da valutare) -> FALSO o VERO (NO/SI)
10 e’ maggiore di 5 ?
In linguaggio C e suoi derivati, esistono delle costanti che identificano il valore VERO o FALSO di
una condizione, e sono, ad esempio true,TRUE,YES,valore diverso da zero oppure
false,FALSE,NO,valore 0.
La prima istruzione che incontriamo per valutare una condizione di test e’ la if/else
ISTRUZIONE IF/ELSE
La sintassi dell’istruzione e’
1)
if(espressione di test)
istruzione eseguita se il risultato e’ VERO;
else
istruzione eseguita se il risultato e’ FALSO;
Se ho piu’ di una sola istruzione, si utilizzano le istruzioni di blocco {} per racchiudere l’elenco
delle istruzioni da eseguire, la sintassi in questo caso e’
2)
if(espressione di test){ //Eseguite se il risultato dell’espressione e’ VERO
istruzione1;
istruzione2;
istruzione3;
…
}else{ //Eseguite se il risultato dell’espressione e’ FALSO
istruzione1;
istruzione4;
…
}
Non e’ sempre necessario indicare l’istruzione else, dipende dalle situazioni, per esempio
acquistare il topolino;
IMPORTANTE: prestate sempre molta attenzione quando si usa la sintassi 1) in quanto, non
essendo presente un blocco di istruzioni (delimitato da aperta e chiusa graffa), sara’ eseguita una
sola istruzione.
E’ anche possibile “annidare” delle istruzioni if, complicando la leggibilita’ del codice; la sintassi in
questo caso e’ la seguente
3)
If(espressione)
If(espressione2)
If(espressione3)
Istruzione;
Esempio
Se(semaforoVerde)
Se(arrivaUnaMaccchina)
Se(vaPiano)
attraversoLaStrada;
Per esercitarvi con questo primo approccio alle decisioni, trovate in allegato un progetto da
compilare in XCODE 2.3 o successivo, completo di sorgenti.
A presto.
Ho sete, decido di andare a prendere qualcosa ad una delle solite macchinette del caffe’.
Mi ritrovo davanti ad una sfilza di pulsanti, ciascuno corrispondente ad una bevanda, rifletto su cosa
preferisco bere,infilo ma mia bella monetina ed eccomi servito.
Immaginate ora di essere voi la macchinetta in questione, quindi dovrete fare determinate cose
(preparare la bevanda selezionata) in funzione del bottone che e’ stato premuto dalla persona che e’
davanti alla macchinetta.
Macchinetta: aspetto che qualcuno metta dei soldi … uffa… che noia…
Macchinetta: … uao! Qualcuno a messo dei soldi… sentiamo cosa vuole… aspetto che prema un
bottone
Macchinetta: …uhm…vuole un caffe’ amaro, che costa 0,50 euro…ha messo 1 euro, quindi ok…
spettacolo… posso preparare un bel caffe’… devo anche dare il resto 0.50…
Una buona parte delle operazioni sopra descritte puo’ essere trasformata in un programma
utilizzando le istruzioni if ed else viste in precedenza. Nel momento in cui la macchinetta deve
valutare quale bottone e’ stato premuto conviene utilizzare un'altra istruzione di valutazione della
condizione.
La convenienza risiede unicamente nel fatto di poter scrivere un codice piu’ leggibile ed a volte piu’
breve rispetto ad utilizzare una serie di if.
IMPORTANTE
Per poter utilizzare l’istruzione switch e’ indispensabile che il valore da valutare sia una
valore numerico intero oppure una variabile di tipo carattere.
Sintassi:
switch(valore da valutare){
case costante1:
prima istruzione da eseguire;
seconda istruzione da eseguire;
…
break;
case costante2:
prima istruzione da eseguire;
…
break;
… altri case…
default:
istruzioni eseguite se il valore non corrisponde a nessuno dei valori indicati nelle precedenti
istruzioni case
}
Esempio:
Di conseguenza, quando viene premuto un pulsante (tra 1 e 9), la macchinetta deve preparare la
relativa bevanda.
int nNumeroBottonePremuto=0;
1) MODO: Usiamo IF
if(nNumeroBottonePremuto == 1){
//utente ha scelto il Caffe’ Normale Amaro
}
if(nNumeroBottonePremuto == 2){
//utente ha scelto il Caffe’ Lungo Amaro
}
if(nNumeroBottonePremuto == 3){
//utente ha scelto il Caffe’ Normale Dolce
}
if(nNumeroBottonePremuto == 4){
//utente ha scelto il Caffe’ Lungo Dolce
}
if(nNumeroBottonePremuto == 5){
//utente ha scelto il Caffe’ macchiato
}
if(nNumeroBottonePremuto == 6){
//utente ha scelto il The al limone
}
if(nNumeroBottonePremuto == 7){
//utente ha scelto il The alla pesca
}
if(nNumeroBottonePremuto == 8){
//utente ha scelto la Cioccolata
}
if(nNumeroBottonePremuto == 8){
//utente ha scelto Cioccolata e Latte
}
switch(nNumeroBottonePremuto){
case 1: //utente ha scelto il Caffe’ Normale Amaro
break;
case 2: //utente ha scelto il Caffe’ Normale Amaro
break;
case 3: //utente ha scelto il Caffe’ Normale Amaro
break;
case 4: //utente ha scelto il Caffe’ Normale Amaro
break;
case 5: //utente ha scelto il Caffe’ Normale Amaro
break;
case 6: //utente ha scelto il Caffe’ Normale Amaro
break;
case 7: //utente ha scelto il Caffe’ Normale Amaro
break;
case 8: //utente ha scelto il Caffe’ Normale Amaro
break;
case 9: //utente ha scelto il Caffe’ Normale Amaro
break;
default:
//errore nella selezione. (ha premuto un pulsante non compreso tra 1 e 9)
}
IMPORTANTE
L’istruzione default, all’interno dello switch e’ facoltativa.
L’istruzione break all’interno di uno switch provoca uscita dal blocco dello switch stesso
(compreso tra { e } ) o, in altri termini, provoca il salto del codice da eseguire alla prima istruzione
presente alla fine del blocco dello switch.
Se a piu’ valori corrispondono le stesse istruzioni, si puo’ scrivere lo switch nel modo seguente
swicth(nNumeroBottonePremuto){
case 1:
case 2:
//istruzioni da eseguire, indipendentemente se ho premuto 1 o 2
break;
case 3:
//istruzione da eseguire se ho premuto 3
break;
}
Per concludere, vediamo l’esempio utilizzando una variabile di tipo carattere come elemento da
valutare nello switch.
char chMenuScelto;
//supponiamo che l’utente possa scegliere una voce di menu, premendo la prima lettera iniziale
//Immissione dati
//Variazione dati
//Cancellazione
//Stampa
//Esci
switch(chMenuScelto){
case ‘I’:
//Immissione dati
//istruzioni per gestire l’immissione dati
break;
case ‘V’:
//variazione dati
break;
case ‘S’: //stampa
break;
case ‘E’: //Esci
break;
default:
//Visualizza errore, scelta non valida..
}
Vi lascio con un esempio (la macchinetta del caffe’) implementato in Cocoa, su cui esercitarvi.
Saluti.