Sei sulla pagina 1di 141

Il programmatore che c'è in noi - Lezione 1 - DATI

I DATI rappresentano le INFORMAZIONI che l’esecutore del programma (il computer) ha a


disposizione per poter eseguire il programma stesso (DATI DI INPUT=dati in ingresso) e per
generare dei risultati (DATI OUTPUT=Dati di uscita).

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

DATI DI USCITA (Dati Output)


10 MELE

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:

DATI DI TIPO NUMERICO


DATI DI TIPO CARATTERE
DATI DI TIPO LOGICO

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

Una mela occupa piu’ spazio di una moneta di un euro.

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’ RAM abbiamo, piu’ la scatola è grossa.

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 ?).

Bene, l’unita’ di misura che si utilizza in informatica è il BYTE

1 BYTE (per ragioni storiche…) possiamo paragonarlo ad un CARATTERE

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.

Come tutte le unità di misura esistono i multipli e i sottomultipli

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

Nel precedente articolo ho dato le indicazioni relative a come “misurare” lo


spazio occupato nella memoria del calcolatore utilizzando una nomenclatura
non del tutto corretta come è stato giustamente suggerito da Paolo Portaluri.
Dal link http://www.germinara.it/iMemory.zip potete scaricare un progetto in
Cocoa (contenente sia l’applicazione sia i sorgenti) dove sono meglio chiarite le
nomenclature. Il realtà tale progetto contiene anche le indicazioni relative
all’argomento di oggi.

I TIPI DI DATI BASE

La volta scorsa ho semplicemente fatto una distinzione tra


DATI DI TIPO NUMERICO
DATI DI TIPO CARATTERE
DATI DI TIPO LOGICO

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).

Se devo memorizzare (=mettere nello scatolone) il valore di temperatura invernale in montagna


probabilmente avro’ necessita di indicare un valore tra – 25 e 30, usando in questo caso anche il
numero negativo (quindi il segno).

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

1 char occupa 1 BYTE nella nostra memoria Ram

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).

Tipi di dati base

Nome Dimensione occupata Descrizione


char - 1 BYTE Carattere o numero intero piccolo
int - 4 BYTES Numero intero
float - 4 BYTES Numero reale (decimali) singola precisione
double - 8 BYTES Numero reale (decimali) doppia precisione

La dimensione occupata è indicativa, puo’ variare in funzione del computer utilizzato.


Per conoscere l’esatta dimensione occupata in memoria dal tipo di dato in linguaggio C si utilizza
l’operatore sizeof(nomedeltipodidato)

Quindi

sizeof(char) mi dira’ quanto spazio occupa un char in memoria


sizeof(double) mi dira’ quanto spazio occupa un double in memoria

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

ovviamente se voglio sapere la dimensione occupata in memoria uso


sizeof(unsigned long int)

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.

Spero non vi sia venuto il mal di testa…


Il programmatore che c'è in noi - Lezione 3 – XCODE Tutorial

Oggi, visto che siamo a fine settimana, facciamo qualcosa di


rilassante, vediamo come creare un progetto Cocoa con XCODE e
creiamo insieme un piccolo programma per visualizzare i
LIMITI dei valori che possiamo inserire nei vari tipi di dati
che ho descritto nella lezione 2.

Ovviamente si presume che Xcode sia stato installato sul


proprio Mac.

- Individuiamo dove si trova l’applicazione XCODE


e la mandiamo in esecuzione, quindi scegliamo File.
NewProject

Compare
E selezioniamo COCOA APPLICATION

A questo punto diamo il nome alla nostra applicazione, nel


mio esempio limiti e scegliamo in quale cartella dovrà
risiedere il progetto, quindi premiamo Finish.
Ci appare l’ambiente di lavoro di XCODE

Per verificare se tutto è a posto, creiamo la nostra


applicazione (la prima ?...)

E se tutto e’ a posto, eccola!!! BCS (… è un mio modo di


dire… Bella Come il Sole… )
Ovviamente non fa assolutamente nulla, pero’ ho gia una
finestra e un menu.

Andare a vedere dove è stato creato il progetto, e vedrete


tutti i files che Xcode ha generato per il progetto.
Quelli con estensione .m e .h sono il codice sorgente in
ObjectiveC, mentre quelli .nib sono i files di risorsa (=di
interfaccia) usati da interface builder per progettare
l’interfaccia utente (le finestre, i bottono ecc.)

Comunque rimando alla documentazione della Apple per maggior


dettaglio, o eventualmente, postate un commento specifico su
Tevac.
Visto che abbiamo accennato a Interface Builder ed alla
interfaccia utente, ritorniamo in XCODE e con un doppio clic
sul elemento MainMenu.nib attiviamo Interface Builder (IB)
per andare a modificare la nostra finestra e iniziare a
personalizzare la nostra applicazione.
Una volta lanciato Interface Builder, vi consiglio di
attivare subito la visualizzazione dell’Ispettore degli
oggetti tramite questo menu

e di disporre le varie finestre nel modo piu’ comodo per


lavorarci

Ciccando sulla finestra (Window) vedremo cambiare le


informazioni dell’Ispettore di oggetto, accertandosi che sia
attivo per la nostra finestra (nella finestra dell’ispettore
deve comparire il titolo NSWindow Inspector )
Andiamo a cambiare il nome della finestra dal generico Window
in Limiti
Dopo l’invio il nome della finestra viene aggiornato

Per fare un po’ di pratica, diciamo che ha l’aspetto


“metallico”

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.

L’ispettore ora si presenta con i seguenti dati


Provare a modificare i valori di w (larghezza) e h (altezza)
e vedrete la finestra cambiare dimensione.

Bene, posizioniamo ora degli oggetti di testo (etichette o


label), in quanto
Vogliamo far comparire nella nostra finestra delle
indicazioni che scriviamo noi

Oltre alla nostra finestra “metallizzata”, alla finestra


dell’ispettore, abbiamo altre due finestre, una TOOL BAR e
una che rappresenta il nostro oggetto MainMenu.nib

Nella Tool bar, selezioniamo Cocoa-Text

e successivamente cliccliamo su una Label/Etichetta che


vogliamo inserire nella nostra finestra “metallizzata”
(esempio Small System Font Text)
e mantenendo premuto il tasto sinistro del mouse, trasciniamo
la scritta sulla finestra metallizzata, quindi rilasciamo il
tasto del mouse.
Una volta “lasciato cadere” l’oggetto testo, possiamo
ciccarci sopra e spostarlo nella posizione a noi piu’
congeniale.
Mentre spostare l’oggetto selezionato, vedrete apparire delle
linee guida che vi aiutano a disporre gradevolmente l’oggetto
e rispettando le linee guida Apple relativamente a Acqua.

Per modificare invece il contenuto del testo, doppio clic e


si scrive dentro (oppure si usa l’ispettore…)

Nel nostro esempio, cambiamo la dicitura in char max, in


quanto l’idea per il nostro programma è quella di far
scrivere accanto al tipo di dato il suo range di valori
minimi e massimi (i sui limiti).

Dato che abbiamo diversi elementi da posizionare, conviene


usare il “copia” e “incolla”, Mela-C e Mela-V, quindi
riposiziono gli oggetti.
e cosi via sino a completare la finestra con tutte le
informazioni che intendo gestire.
Ecco come si presenta la finestra al termine.

A questo punto passo a posizionare un altro tipo di oggetto,


un campo di input.

Sempre dalla tool bar, cocoa-text, trascino l’elemento che mi


interessa nella finestra metallizzata

E lo lascio cadere…
Come al solito, provvedo a posizionarlo ed eventualmente a
ridimensionarlo.

Quindi uso il solito Copia/Incolla per velocizzare le


operazioni di creazione di oggetti analoghi.

Sino ad ottenere l’effetto desiderato


A questo punto, voglio che le dimensioni della finestra siano
fisse, quindi non ridimensionabile dall’utente, allora da
Ispettore imposto Min w/h e Max w/h al valore della
dimensione corrente della finestra.

Giunti a questo punto dobbiamo creare un nostro OGGETTO, che


ci consentirà di controllare la nostra finestra (o meglio i
campi della nostra finestra) durante l’esecuzione del
programma.

Infatti, quando il programma sarà mandato in esecuzione,


vorremmo far comparire a fianco della scritta char max il
valore (es. 127) massimo che posso memorizzare in un dato di
tipo char ecc.

Per creare questo nostro oggetto “controllore” che chiameremo


appunto ilControllore, dobbiamo usare la finestra
MainMenu.nib.
Da tale finestra clicco sul tab CLASSES, e mi posiziono
sull’oggetto NSObject.

Una volta evidenziato NSObject clic con il destro (=CTRL+CLIC


se ho il mouse con solo tasto…credo…)

E dal menu che compare scelgo Subclass NSObject


Viene creata una nuova voce, MyObject, che io provvedo a
rinominare con il nome ilControllore come avevo anticipato.

Do invio

e selezionando il mio oggetto “ilControllore”, clic di destro


(per far apparire il menu contestuale)
E questa volta scelgo, Instantiate ilControllore, ossia
chiedo a Interface Builder di creare il mio oggetto in modo
che io possa lavorarci sopra.

Ed eccomi accontentato.
Ora ho nel MainMenu.nib anche un oggetto che si chiama
ilControllore che è il mio oggetto appena creato.

Ovviamente tale oggetto è al momento un contenitore Vuoto.

Lo seleziono, ciccandoci sopra

e guardo cosa succede nella finestra dell’Ispettore.


Vedete che ClassName = ilControllore (il nome del mio
oggetto) e che è un contenitore vuoto (0 Outlets e 0
Actions).

Cosa deve fare il mio oggetto controllore ?

Deve gestire il passaggio delle informazioni tra il mio


programma sorgente e gli oggetti grafici (il testo, il campo
di input, un pulsante ecc.) che io ho collocato sulla mia
finestra.

Nel nostro esempio, io voglio poter controllare da programma


il campo Field Text che ho inserito a fianco dell’etichetta
di testo “char max” e “char min”.

Ovviamente questo andrebbe fatto per tutti i campi Text Field


che abbiamo
Posizionato nella nostra finestra metallizzata, ma io, in
questo esempio lo faccio solo per i primi due… a voi fare poi
gli altri…

Per far conoscere al mio oggetto controllore l’oggetto Text


Field (=rettangolo bianco) a fianco del testo char max, devo
ciccare sul Tab Outlets nella finestra ispettore,
relativamente all’oggetto “ilControllore” come visibile

Quindi premo ADD per aggiungere un Outlet che mi consentirà


di accedere all’oggetto Text Field.
Viene creato un nuovo elemento myOutlet
che ovviamente provvedo a chiamare in un modo piu’ idone

dato che voglio tramite questo outlet gestire questo campo


(char max)
come anticipato prima, il campo che abbiamo trascinato in
precedenza nella nostra finestra metallizzata (quello
rettangolare bianco) è un oggetto Cocoa che si chiama
NSTextField, puo’ essere usato sia come campo di input dati
sia come campo di output.

Ma impareremo a conoscerlo meglio nelle prossime puntate.


Nella colonna a fianco Outlet Name, c’e’ la colonna Type, che
identifica di che tipo è l’oggetti A CUI SI RIFERISCE (o
meglio si referirà… perche’ dovremo poi effettivamente
collegarlo all’oggetto che abbiamo messo sulla finestra
metallizzata).
Nel nostro casco, scegliamo NSTextField dall’elenco che
appare.

In modo da ottenere
Dato che in questo tutorial voglio gestire solo i primi due
(char max e char min) rifaccio nuovamente l’operazione.

(tale operazione deve essere fatta per CIASCUN oggetto che ho


messo sulla finestra del quale voglio avere il controllo
tramite il codice sorgente del programma).
Bene, finita questa fase, ritorno sulla finestra MainMenu.nib
seleziono Instances in modo da portarmi nella seguente
modalità

Ora, sulla sinistra del nostro oggetto ilControllore, è


comparso un punto esclamativo: esso indica che abbiamo degli
outlets o delle actions da collegare.

Quindi, colleghiamoli…

L’operazione che stiamo per fare (tramite drag & drop)


“collega” le outlets che abbiamo appena creato con i
rispettivi oggetti Cocoa che avevamo piazzato sulla finestra
metallizzata.

Per fare un COLLEGAMENTO di una OUTLET

Clic sull’oggetto presente in MainMenu.nib (ilControllore),


premo il tasto CTRL e faccio CLIC, trascino il puntatore del
mouse sopra l’oggetto che mi interessa collegare e lascio
andare il tasto CTRL e il tasto del mouse

L’ispettore si posiziona in questa modalità


A questo punto faccio un clic sul OUTLET che voglio collegare
all’oggetto che ho precedentemente selezionato e premo
CONNECT.

Quando è collegato, compare un pallino sulla sinistra, e


nella colonna Destination compare l’oggetto NSTextField.

Rifaccio l’operazione di collegamento anche per il secondo


campo a fianco del testo char min (che sara’ collegato
all’outlet limiteCharMin)
Se diamo uno sguardo al nostro oggetto ilControllore nella
videata MainMenu.nib ora non ha piu’ il punto esclamativo.

Siamo quasi al dunque,… abbiate fede…

Ora non ci rimane che chiedere a Interface Builder di creare


per noi i files di sorgenti relativi al nostro oggetto
ilControllore che abbiamo creato.

Perche’ questo ?

Semplice, noi vogliamo scrivere dei valori nei due


NSTextField che abbiamo posizionato nella finestra
metallizata e tali valori devono essere scritti dal nostro
programma, quindi abbiamo bisogno dei files di programma (.m
e .h) che rappresentano l’oggetto ilControllore. Tali files
devono essere inclusi nel nostro progetto di xcode.

Per far creare a Interface Builder i files necessari,


selezioniamo CLASSES dalla finestra MainMenu.nib
Quindi selezioniamo il nostro oggetto ilControllore e
facciamo comparire il menu contestuale (clic tasto destro del
mouse)
Questa volta scegliamo CREATE FILES FOR ilControllore e
compare

vedete che ha gia’ assegnato i nomi ai files .m e .h cre


saranno creati e che saranno automaticamente aggiunti al
progetto “limiti”. Non dobbiamo far altro che premere CHOOSE.

Bene, è giunto il momento di abbandonare Interface Builder,


salviamo tutto e chiudiamo.
Ritorno si XCODE e mi ritrovo i files che sono stati aggiunti
da Interface Builder
Bene, vediamo cosa c’e’ scritto dentro i files
ilControllore.h e ilControllore.m, che sono parte dei
sorgenti del nostro programma.

Per vederne il contenuto, ciccate sul pulsante Editor nella


barra strumenti

in modo da avere la seguente modalità di visualizzazione


Quello che vediamo è stato scritto in automatico (seguendo le
nostre indicazioni…) relativamente all’oggetto ilControllore.
Sono facilmente riconoscibili i due outlet che abbiamo in
precedenza creato.

Il file ilControllore.m, invece non ha nessuna indicazione


particolare.

Per concludere il mio tutorial, volevo scrivere il valore max


e minimo consentito per un tipo di dato char.

Per fare questo, devo intercettare un particolare evento di


Cocoa, che si chiama awakeFromNib.
In pratica, quando lancio il programma, il programma in
automatico carichera’ in memoria il file NIB indicato e
creera’ tutti gli oggetti presenti nel file NIB stesso (tra
cui il nostro ilControllore).

Quando questo avviene, l’applicazione avvisa tutti gli


oggetti inviando loro un messaggio di tipo awakeFromNib
appunto. (=risvegliato dal NIB)

In questo modo, nel mio programma, io posso effettuare delle


azioni non appena l’oggetto viene creato, nel caso specifico
l’azione che io voglio fare è quella di scrivere un valore
dentro l’oggetto NSTextField collegato all’outlet
limiteCharMax.

Per fare cio’ devo

aggiunger il metodo awakeFromNib nel codice sorgente del mio


oggetto ilControllore.c

e quindi all’interno di tale metodo, ricavare il valore max e


min consentiti per un char.

Per ricavare tale valore, non faccio altro che usare i valori
costanti definiti dal file limits.h (che potete cercare sul
vostro Mac.)

Questo è un pezzo di tale file

#include <ppc/_limits.h>

#define CHAR_BIT 8 /* number of bits in a char */


#define MB_LEN_MAX 6 /* Allow 31 bit UTF2 */

#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 */

#define UCHAR_MAX 255 /* max value for an unsigned char */


#define CHAR_MAX 127 /* max value for a char */
#define CHAR_MIN (-128) /* min value for a char */

#define USHRT_MAX 65535 /* max value for an unsigned short */


#define SHRT_MAX 32767 /* max value for a short */
#define SHRT_MIN (-32768) /* min value for a short */

….

Quindi il valore massimo di un char è indicato dalla COSTANTE

CHAR_MAX che vale 127 come visibile dal file.

Dato che il valore è numerico, ed il campo che io ho messo


nella finestra metallizzata è un NSTextField (campo di testo)
lo devo convertire in testo.

Per fare tale operazione, uso un oggetto Cocoa che


rappresenta una stringa di caratteri NSString ed uso un suo
metodo per convertire numeri in testo
stringWithFormat
Vedremo comunque meglio ed in dettaglio queste cose, in una
prossima lezione.

In pratica il codice che vado ad inserire dentro


ilControllore.m
Tra @implementation e @end

Quindi effettuo le operazioni di conversione dati numerici in


stringa

e dico alla mia outlet (che rappresenta il campo NSTextField


char max) di che ora contiene una stringa che è il dato che
interessa.
Ecco come si presenta il codice completo di ilControllore.m

#import "ilControllore.h"

@implementation ilControllore

-(void) awakeFromNib{
NSString *unaStringa;

unaStringa=[NSString stringWithFormat:@"%d", CHAR_MAX];

[limiteCharMax setStringValue:unaStringa];

unaStringa=[NSString stringWithFormat:@"%d", CHAR_MIN];

[limiteCharMin setStringValue:unaStringa];
}

@end

Ed ecco il risultato finale dell’applicazione e del tutorial.

Esercitatevi a fare tutti i passagi che ho descritto in modo


da acquisire la necessaria manualità ed a familiarizzare con
l’ambiente XCODE e Interface Builder.

Per i piu’ intraprendenti: provare a completare


l’applicazione facendo calcolare anche gli altri valori e
rendendo i campi solo di OUTPUT.
Se volete potete anche inviarmi il vostro lavoro al termine,
al mio indirizzo email.

Francesco Germinara
Info@germinara.it
www.germinara.it
Il programmatore che c'è in noi - Lezione 3 – XCODE Tutorial

Oggi, visto che siamo a fine settimana, facciamo qualcosa di


rilassante, vediamo come creare un progetto Cocoa con XCODE e
creiamo insieme un piccolo programma per visualizzare i
LIMITI dei valori che possiamo inserire nei vari tipi di dati
che ho descritto nella lezione 2.

Ovviamente si presume che Xcode sia stato installato sul


proprio Mac.

- Individuiamo dove si trova l’applicazione XCODE


e la mandiamo in esecuzione, quindi scegliamo File.
NewProject

Compare
E selezioniamo COCOA APPLICATION

A questo punto diamo il nome alla nostra applicazione, nel


mio esempio limiti e scegliamo in quale cartella dovrà
risiedere il progetto, quindi premiamo Finish.
Ci appare l’ambiente di lavoro di XCODE

Per verificare se tutto è a posto, creiamo la nostra


applicazione (la prima ?...)

E se tutto e’ a posto, eccola!!! BCS (… è un mio modo di


dire… Bella Come il Sole… )
Ovviamente non fa assolutamente nulla, pero’ ho gia una
finestra e un menu.

Andare a vedere dove è stato creato il progetto, e vedrete


tutti i files che Xcode ha generato per il progetto.
Quelli con estensione .m e .h sono il codice sorgente in
ObjectiveC, mentre quelli .nib sono i files di risorsa (=di
interfaccia) usati da interface builder per progettare
l’interfaccia utente (le finestre, i bottono ecc.)

Comunque rimando alla documentazione della Apple per maggior


dettaglio, o eventualmente, postate un commento specifico su
Tevac.
Visto che abbiamo accennato a Interface Builder ed alla
interfaccia utente, ritorniamo in XCODE e con un doppio clic
sul elemento MainMenu.nib attiviamo Interface Builder (IB)
per andare a modificare la nostra finestra e iniziare a
personalizzare la nostra applicazione.
Una volta lanciato Interface Builder, vi consiglio di
attivare subito la visualizzazione dell’Ispettore degli
oggetti tramite questo menu

e di disporre le varie finestre nel modo piu’ comodo per


lavorarci

Ciccando sulla finestra (Window) vedremo cambiare le


informazioni dell’Ispettore di oggetto, accertandosi che sia
attivo per la nostra finestra (nella finestra dell’ispettore
deve comparire il titolo NSWindow Inspector )
Andiamo a cambiare il nome della finestra dal generico Window
in Limiti
Dopo l’invio il nome della finestra viene aggiornato

Per fare un po’ di pratica, diciamo che ha l’aspetto


“metallico”

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.

L’ispettore ora si presenta con i seguenti dati


Provare a modificare i valori di w (larghezza) e h (altezza)
e vedrete la finestra cambiare dimensione.

Bene, posizioniamo ora degli oggetti di testo (etichette o


label), in quanto
Vogliamo far comparire nella nostra finestra delle
indicazioni che scriviamo noi

Oltre alla nostra finestra “metallizzata”, alla finestra


dell’ispettore, abbiamo altre due finestre, una TOOL BAR e
una che rappresenta il nostro oggetto MainMenu.nib

Nella Tool bar, selezioniamo Cocoa-Text

e successivamente cliccliamo su una Label/Etichetta che


vogliamo inserire nella nostra finestra “metallizzata”
(esempio Small System Font Text)
e mantenendo premuto il tasto sinistro del mouse, trasciniamo
la scritta sulla finestra metallizzata, quindi rilasciamo il
tasto del mouse.
Una volta “lasciato cadere” l’oggetto testo, possiamo
ciccarci sopra e spostarlo nella posizione a noi piu’
congeniale.
Mentre spostare l’oggetto selezionato, vedrete apparire delle
linee guida che vi aiutano a disporre gradevolmente l’oggetto
e rispettando le linee guida Apple relativamente a Acqua.

Per modificare invece il contenuto del testo, doppio clic e


si scrive dentro (oppure si usa l’ispettore…)

Nel nostro esempio, cambiamo la dicitura in char max, in


quanto l’idea per il nostro programma è quella di far
scrivere accanto al tipo di dato il suo range di valori
minimi e massimi (i sui limiti).

Dato che abbiamo diversi elementi da posizionare, conviene


usare il “copia” e “incolla”, Mela-C e Mela-V, quindi
riposiziono gli oggetti.
e cosi via sino a completare la finestra con tutte le
informazioni che intendo gestire.
Ecco come si presenta la finestra al termine.

A questo punto passo a posizionare un altro tipo di oggetto,


un campo di input.

Sempre dalla tool bar, cocoa-text, trascino l’elemento che mi


interessa nella finestra metallizzata

E lo lascio cadere…
Come al solito, provvedo a posizionarlo ed eventualmente a
ridimensionarlo.

Quindi uso il solito Copia/Incolla per velocizzare le


operazioni di creazione di oggetti analoghi.

Sino ad ottenere l’effetto desiderato


A questo punto, voglio che le dimensioni della finestra siano
fisse, quindi non ridimensionabile dall’utente, allora da
Ispettore imposto Min w/h e Max w/h al valore della
dimensione corrente della finestra.

Giunti a questo punto dobbiamo creare un nostro OGGETTO, che


ci consentirà di controllare la nostra finestra (o meglio i
campi della nostra finestra) durante l’esecuzione del
programma.

Infatti, quando il programma sarà mandato in esecuzione,


vorremmo far comparire a fianco della scritta char max il
valore (es. 127) massimo che posso memorizzare in un dato di
tipo char ecc.

Per creare questo nostro oggetto “controllore” che chiameremo


appunto ilControllore, dobbiamo usare la finestra
MainMenu.nib.
Da tale finestra clicco sul tab CLASSES, e mi posiziono
sull’oggetto NSObject.

Una volta evidenziato NSObject clic con il destro (=CTRL+CLIC


se ho il mouse con solo tasto…credo…)

E dal menu che compare scelgo Subclass NSObject


Viene creata una nuova voce, MyObject, che io provvedo a
rinominare con il nome ilControllore come avevo anticipato.

Do invio

e selezionando il mio oggetto “ilControllore”, clic di destro


(per far apparire il menu contestuale)
E questa volta scelgo, Instantiate ilControllore, ossia
chiedo a Interface Builder di creare il mio oggetto in modo
che io possa lavorarci sopra.

Ed eccomi accontentato.
Ora ho nel MainMenu.nib anche un oggetto che si chiama
ilControllore che è il mio oggetto appena creato.

Ovviamente tale oggetto è al momento un contenitore Vuoto.

Lo seleziono, ciccandoci sopra

e guardo cosa succede nella finestra dell’Ispettore.


Vedete che ClassName = ilControllore (il nome del mio
oggetto) e che è un contenitore vuoto (0 Outlets e 0
Actions).

Cosa deve fare il mio oggetto controllore ?

Deve gestire il passaggio delle informazioni tra il mio


programma sorgente e gli oggetti grafici (il testo, il campo
di input, un pulsante ecc.) che io ho collocato sulla mia
finestra.

Nel nostro esempio, io voglio poter controllare da programma


il campo Field Text che ho inserito a fianco dell’etichetta
di testo “char max” e “char min”.

Ovviamente questo andrebbe fatto per tutti i campi Text Field


che abbiamo
Posizionato nella nostra finestra metallizzata, ma io, in
questo esempio lo faccio solo per i primi due… a voi fare poi
gli altri…

Per far conoscere al mio oggetto controllore l’oggetto Text


Field (=rettangolo bianco) a fianco del testo char max, devo
ciccare sul Tab Outlets nella finestra ispettore,
relativamente all’oggetto “ilControllore” come visibile

Quindi premo ADD per aggiungere un Outlet che mi consentirà


di accedere all’oggetto Text Field.
Viene creato un nuovo elemento myOutlet
che ovviamente provvedo a chiamare in un modo piu’ idone

dato che voglio tramite questo outlet gestire questo campo


(char max)
come anticipato prima, il campo che abbiamo trascinato in
precedenza nella nostra finestra metallizzata (quello
rettangolare bianco) è un oggetto Cocoa che si chiama
NSTextField, puo’ essere usato sia come campo di input dati
sia come campo di output.

Ma impareremo a conoscerlo meglio nelle prossime puntate.


Nella colonna a fianco Outlet Name, c’e’ la colonna Type, che
identifica di che tipo è l’oggetti A CUI SI RIFERISCE (o
meglio si referirà… perche’ dovremo poi effettivamente
collegarlo all’oggetto che abbiamo messo sulla finestra
metallizzata).
Nel nostro casco, scegliamo NSTextField dall’elenco che
appare.

In modo da ottenere
Dato che in questo tutorial voglio gestire solo i primi due
(char max e char min) rifaccio nuovamente l’operazione.

(tale operazione deve essere fatta per CIASCUN oggetto che ho


messo sulla finestra del quale voglio avere il controllo
tramite il codice sorgente del programma).
Bene, finita questa fase, ritorno sulla finestra MainMenu.nib
seleziono Instances in modo da portarmi nella seguente
modalità

Ora, sulla sinistra del nostro oggetto ilControllore, è


comparso un punto esclamativo: esso indica che abbiamo degli
outlets o delle actions da collegare.

Quindi, colleghiamoli…

L’operazione che stiamo per fare (tramite drag & drop)


“collega” le outlets che abbiamo appena creato con i
rispettivi oggetti Cocoa che avevamo piazzato sulla finestra
metallizzata.

Per fare un COLLEGAMENTO di una OUTLET

Clic sull’oggetto presente in MainMenu.nib (ilControllore),


premo il tasto CTRL e faccio CLIC, trascino il puntatore del
mouse sopra l’oggetto che mi interessa collegare e lascio
andare il tasto CTRL e il tasto del mouse

L’ispettore si posiziona in questa modalità


A questo punto faccio un clic sul OUTLET che voglio collegare
all’oggetto che ho precedentemente selezionato e premo
CONNECT.

Quando è collegato, compare un pallino sulla sinistra, e


nella colonna Destination compare l’oggetto NSTextField.

Rifaccio l’operazione di collegamento anche per il secondo


campo a fianco del testo char min (che sara’ collegato
all’outlet limiteCharMin)
Se diamo uno sguardo al nostro oggetto ilControllore nella
videata MainMenu.nib ora non ha piu’ il punto esclamativo.

Siamo quasi al dunque,… abbiate fede…

Ora non ci rimane che chiedere a Interface Builder di creare


per noi i files di sorgenti relativi al nostro oggetto
ilControllore che abbiamo creato.

Perche’ questo ?

Semplice, noi vogliamo scrivere dei valori nei due


NSTextField che abbiamo posizionato nella finestra
metallizata e tali valori devono essere scritti dal nostro
programma, quindi abbiamo bisogno dei files di programma (.m
e .h) che rappresentano l’oggetto ilControllore. Tali files
devono essere inclusi nel nostro progetto di xcode.

Per far creare a Interface Builder i files necessari,


selezioniamo CLASSES dalla finestra MainMenu.nib
Quindi selezioniamo il nostro oggetto ilControllore e
facciamo comparire il menu contestuale (clic tasto destro del
mouse)
Questa volta scegliamo CREATE FILES FOR ilControllore e
compare

vedete che ha gia’ assegnato i nomi ai files .m e .h cre


saranno creati e che saranno automaticamente aggiunti al
progetto “limiti”. Non dobbiamo far altro che premere CHOOSE.

Bene, è giunto il momento di abbandonare Interface Builder,


salviamo tutto e chiudiamo.
Ritorno si XCODE e mi ritrovo i files che sono stati aggiunti
da Interface Builder
Bene, vediamo cosa c’e’ scritto dentro i files
ilControllore.h e ilControllore.m, che sono parte dei
sorgenti del nostro programma.

Per vederne il contenuto, ciccate sul pulsante Editor nella


barra strumenti

in modo da avere la seguente modalità di visualizzazione


Quello che vediamo è stato scritto in automatico (seguendo le
nostre indicazioni…) relativamente all’oggetto ilControllore.
Sono facilmente riconoscibili i due outlet che abbiamo in
precedenza creato.

Il file ilControllore.m, invece non ha nessuna indicazione


particolare.

Per concludere il mio tutorial, volevo scrivere il valore max


e minimo consentito per un tipo di dato char.

Per fare questo, devo intercettare un particolare evento di


Cocoa, che si chiama awakeFromNib.
In pratica, quando lancio il programma, il programma in
automatico carichera’ in memoria il file NIB indicato e
creera’ tutti gli oggetti presenti nel file NIB stesso (tra
cui il nostro ilControllore).

Quando questo avviene, l’applicazione avvisa tutti gli


oggetti inviando loro un messaggio di tipo awakeFromNib
appunto. (=risvegliato dal NIB)

In questo modo, nel mio programma, io posso effettuare delle


azioni non appena l’oggetto viene creato, nel caso specifico
l’azione che io voglio fare è quella di scrivere un valore
dentro l’oggetto NSTextField collegato all’outlet
limiteCharMax.

Per fare cio’ devo

aggiunger il metodo awakeFromNib nel codice sorgente del mio


oggetto ilControllore.c

e quindi all’interno di tale metodo, ricavare il valore max e


min consentiti per un char.

Per ricavare tale valore, non faccio altro che usare i valori
costanti definiti dal file limits.h (che potete cercare sul
vostro Mac.)

Questo è un pezzo di tale file

#include <ppc/_limits.h>

#define CHAR_BIT 8 /* number of bits in a char */


#define MB_LEN_MAX 6 /* Allow 31 bit UTF2 */

#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 */

#define UCHAR_MAX 255 /* max value for an unsigned char */


#define CHAR_MAX 127 /* max value for a char */
#define CHAR_MIN (-128) /* min value for a char */

#define USHRT_MAX 65535 /* max value for an unsigned short */


#define SHRT_MAX 32767 /* max value for a short */
#define SHRT_MIN (-32768) /* min value for a short */

….

Quindi il valore massimo di un char è indicato dalla COSTANTE

CHAR_MAX che vale 127 come visibile dal file.

Dato che il valore è numerico, ed il campo che io ho messo


nella finestra metallizzata è un NSTextField (campo di testo)
lo devo convertire in testo.

Per fare tale operazione, uso un oggetto Cocoa che


rappresenta una stringa di caratteri NSString ed uso un suo
metodo per convertire numeri in testo
stringWithFormat
Vedremo comunque meglio ed in dettaglio queste cose, in una
prossima lezione.

In pratica il codice che vado ad inserire dentro


ilControllore.m
Tra @implementation e @end

Quindi effettuo le operazioni di conversione dati numerici in


stringa

e dico alla mia outlet (che rappresenta il campo NSTextField


char max) di che ora contiene una stringa che è il dato che
interessa.
Ecco come si presenta il codice completo di ilControllore.m

#import "ilControllore.h"

@implementation ilControllore

-(void) awakeFromNib{
NSString *unaStringa;

unaStringa=[NSString stringWithFormat:@"%d", CHAR_MAX];

[limiteCharMax setStringValue:unaStringa];

unaStringa=[NSString stringWithFormat:@"%d", CHAR_MIN];

[limiteCharMin setStringValue:unaStringa];
}

@end

Ed ecco il risultato finale dell’applicazione e del tutorial.

Esercitatevi a fare tutti i passagi che ho descritto in modo


da acquisire la necessaria manualità ed a familiarizzare con
l’ambiente XCODE e Interface Builder.

Per i piu’ intraprendenti: provare a completare


l’applicazione facendo calcolare anche gli altri valori e
rendendo i campi solo di OUTPUT.
Se volete potete anche inviarmi il vostro lavoro al termine,
al mio indirizzo email.

Francesco Germinara
Info@germinara.it
www.germinara.it
Il programmatore che c'è in noi - Lezione 4 – Variabili

Le variabili, in un programma, sono i “contenitori” dentro


cui memorizzare dei valori, che possono cambiare (per questo
si chiamano variabili) durante l’esecuzione del programma
stesso.

Quindi per semplificare il concetto di variabile, immaginiamo


di avere delle scatole (come avevamo già fatto parlando dei
tipi di dati), ma questa volta diamo un nome alle scatore, in
modo da poterle facilmente individuare.

Le azioni che posso fare, usando queste scatole, sono


sostanzialmente di “vedere” cosa contiene una scatola oppure
“mettere” qualcosa nella nostra scatola.

Ovviamente, riferendomi alle lezioni sui tipi di dato, una


scatola è “personalizzata” in base al tipo di dato che essa
conterrà.

La sintassi (ossia il modo in cui devo scrivere un istruzione


in un linguaggio di programmazione) per poter dichiarare una
variabile in linguaggio C / C++ e ObjectiveC e’ la seguente

Tipo di dato Nome della Valore iniziale


variabile

Vediamo degli esempi:

Se nel mio programma dovro’ gestire dei dati di tipo intero


esempio l’eta’ di due persone (mio padre e mia madre) avro’
la necessità di usare due VARIABILI (ossia due scatole,
dentro cui mettere i valori relativi agli anni dei miei
genitori).

Per dichiarare (ossia, creare, far nascere, all’interno del


programma una variabile) mi pongo alcune semplici domande:

1) che tipo di dato deve contenere la variabile ?


La risposta a tale domanda, determina il TIPO DI DATO da
usare nella sintassi per la dichiarazione della variabile
stessa
2) quale e’ l’informazioni che sara’ gestita dalla variabile
?
La risposta a tale domanda, mi aiuta a scegliere il NOME
DELLA VARIABILE.
3) ha un valore iniziale ?
In questo caso, la mia risposta è sempre SI, il valore
iniziale io lo metto sempre, anche se vale 0.

Nel esempio, il tipo di dato che deve contenere l’eta’,


potrebbe essere un int, un unsigned short, un char o un
double.
Ovviamente, la scelta migliore deve essere fatta tenendo in
considerazione, quando possibile, l’ottimizzazione
dell’utilizzo della memoria RAM del nostro Mac.

Scelgo, unsigned char

Per il nome, decido nAnniPapa e nAnniMamma

NOTA:
Non esiste un criterio universalmente riconosciuto per
scrivere il nome della variabile.

Come regole di base, non usate simboli di punteggiatura, non


scrivete tutto in maiuscolo, non iniziate un nome di
variabile con un numero, non lasciate spazi tra le parti
componenti il nome.

Comunque, se il nome che avete scelto non e’ accettabile dal


“compilatore” (=programma che elabora le nostre istruzioni di
programma scritte in un certo linguaggio di programmazione, e
le trasforma in istruzioni per il microprocessore del nostro
Mac) sara’ segnalato un errore nel momento in cui cercherete
di mandare in esecuzione il programma (per come fare a
compilare e mandare in esecuzione un vostro programma,
scaricate il tutorial su XCODE della puntata precedente).

Cercate sempre di usare un nome di variabile che sia il piu’


descrittivo possibile (evitando di scrivere un poema…) questo
è di FONDAMENTALE importanza, perche’ quando rimettete le
mani su un vostro programma a distanza di tempo, sarete in
grado di risalire alle funzionalita’ del programma ed allo
scopo delle variabili. La stessa cosa vale se dovrete mettere
le mani su programmi fatti da altri e viceversa.

Vediamo quindi come scriveremo nel nostro programma le due


variabili di cui abbiamo parlato

Tipo Dato Nome variabile Valore Iniziale


unsigned char nAnniPapa 0
unsigned char nAnniMamma 0
ASSEGNAZIONE DI UN VALORE AD UNA VARIABILE

Per mettere qualcosa nella scatola si usa l’operatore di


assegnazione indicato dal simbolo =
Quindi per assegnare un valore ad una variabile si usa
l’operatore =

unsigned char nAnniPapa=45; //dichiaro la variabile e gli


assegno un valore
unsigned char nAnniMamma=38; //dichiaro la variabile e gli
assegno un valore

Esaminiamo questa riga di codice

unsigned char nAnniPapa=45; //dichiaro la variabile e gli


assegno un valore

Va letta nel modo seguente

“assegnamo il valore numerico intero pari a 45 alla variabile


di tipo unsigned char che si chiama nAnniPapa”

E scomponiamola nei vari elementi.

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

//Dichiarazione delle variabili


unsigned char nAnniPapa=45; //Anni Papa’
unsigned char nAnniMamma=38; //Anni Mamma

//Ora voglio assegnare nuovi valori


nAnniPapa = 52; //Assegno il valore 52 alla variabile
nAnniPapa
nAnniMamma = 45;

Posso anche assegnare il valore di una variabile ad un'altra


In questo modo
//Supponiamo che papa’ e mamma abbiano la stessa eta’
nAnniPapa= nAnniMamma;

Posso assegnare il risultato di un calcolo

nAnniPapa = 2006 – 1966;

oppure

nAnniPapa = nAnniMamma+5;

Per i piu’ curiosi…


Tecnicamente parlando, la variabile che si trova alla
SINISTRA del simbolo = (assegnazione) si chiama LVALUE,
ricordatevi questo nome, in alcuni casi il compilatore vi
potrebbe segnalare questo errore: invalid lvalue in
assignment , che significa appunto che c’e’ un errore nella
parte sinistra di un operatore di assegnazione, l’operazione
non puo’ essere fatta.

Esempio di tale errore

7=5; //7 Non e’ un lvalue e’ un valore costante e quindi non


puo’ essere modificato

Vi lascio sperimentare la dichiarazione delle variabili


all’interno di XCODE,
ho preparato a tal proposito questo semplice progetto.

Esempi di dichiarazioni e di assegnazione di variabili


double nTotCostoCassetta=0.0;
int nNumeroDiMele=0;
double nCostoMela=1.5;

//Assegnazione dati
nNumeroDiMele=100;

//Calcolo costo totale e assegno il risultato alla variabile


nTotCostoCassetta = nNumeroDiMele * nCostoMela;

Saluti.
Il programmatore che c’e’ in noi – Lezione 5 – Introduzione ai Puntatori

Le variabili sono estremamente importanti nella programmazione, indipendentemente dal


linguaggio di programmazione che si intende utilizzare.

La loro comprensione e’ di conseguenza di fondamentale importanza, quindi oggi approfondiamo il


discorso relativo alle variabili iniziato in precedenza.

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

int nNumeroRivista=0; //Dichiaro la variabile di tipo int e la inizializzo a 0


int nBytesOccupati=0;

//Calcolo lo spazio di memoria occupato dalla variabile nNumeroRivista


nBytesOccupati = sizeof(nNumeroRivista); // 4 bytes

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 i nostri 4 BYTES saranno rispettivamente agli indirizzi 10,11,12,13.

Bene, in C/C++ e ObjectiveC esiste un OPERATORE che ci consente di conoscere la locazione di


partenza di una qualsiasi variabile, ossia di conoscerne il suo INDIRIZZO in MEMORIA RAM.

Tale operatore e’ & (and)

Quindi per sapere realmente la locazione di memoria a cui e stata “posizionata” la nostra variabile
devo scrivere una istruzione di questo tipo

indirizzoVariabile = &nomeVariabile e quindi nel nostro esempio

indirizzoVariabile = &nNumeroRivista; //Ricavo l’indirizzo (di partenza) della variabile

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) .

Le seguenti due istruzioni sono uguali


nNumeroRivista=223; //Assegno il valore DECIMALE
nNumeroRivista=0xDF; //Assegno il valore Hex

Per la conversione dei valori Dec/Hex e viceversa, vi consiglio di usare l’applicazione


Calcolatrice del vostro Mac. Per chi vuole approfondire il discorso, consiglio di fare delle
ricerche in Internet o se qualcuno ha tempo/voglia potrebbe approfondire meglio il discorso qui
su Tevac.
2) Complicazione
Il valore che viene calcolato dall’operatore &, e’ di un TIPO DI DATO particolare, molto
particolare, e’ un PUNTATORE.

Quindi sembra un valore int ma non lo e’.

Di conseguenza NON posso fare una operazione di questo tipo

int nNumeroRivista=0; //Dichiaro la variabile di tipo int e la inizializzo a 0

int indirizzoVariabile=0;

indirizzoVariabile=& nNumeroRivista; //Calcolo l’indirizzo della variabile

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.

Come faccio a dichiarare la variabile puntatore che mi serve ?

1) penso ad un nome coerente con la variabile a cui si riferisce l’indirizzo


esempio, la variabile della quale voglio conoscere l’indirizzo si chiama nNumeroRivista, il
nome della mia variabile di tipo puntatore che dovra’ contenere l’indirizzo della variabile
nNumeroRivista la chiamo pNumeroRivista (la p che ho usato come prefisso mi “ricorda” che
e’ un puntatore

2) di che tipo e’ la variabile della quale voglio calcolare l’indirizzo ?


nNumeroRivista e’ di tipo int, di conseguenza il mio puntatore e’ un puntatore ad un tipo int

3) come indico nel programma che la variabile e’ una variabile puntatore ?


Si usa un ulteriore OPERATORE del linguaggio di programmazione, che e’ * (asterisco) da
anteporre al nome della variabile (*nomevariabile) o da attaccare all’identificatore di tipo
(int*) il risultato e’ lo stesso. La scelta di un modo piuttosto che l’altro e’ una questione
stilistica e (reputo) personale.

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.

Bene, ci siamo… riprendiamo il nostro esempio

int nNumeroRivista=0; //Dichiaro la variabile di tipo int e la inizializzo a 0


int* pNumeroRivista=nil; //Dichiaro la variabile di tipo puntatore ad un int e la inizializzo a nil

//Calcolo l’indirizzo della variabile


pNumeroRivista=& nNumeroRivista; //calcolo il valore dell’indirizzo e lo memorizzo nella
variabile pNumeroRivista. Il valore di indirizzo potrebbe essere, ad esempio, 0xbffff614

Per vedere il valore di indirizzo del puntatore, se usiamo la NSLog() gia’ incontrata in altre
occasioni, dovremo scrivere in questo modo

NSLog(@"Indirizzo di nNumeroRivista: %p",pNumeroRivista);

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.

Questo e’ il Run log prodotto dal programma …

LeVariabiliApprofondimento[1085] Valore contenuto in nNumeroRivista: 223


LeVariabiliApprofondimento[1085] Dimensione Occupata da nNumeroRivista: 4
LeVariabiliApprofondimento[1085] Indirizzo di nNumeroRivista: 0xbffff628
LeVariabiliApprofondimento[1085] All' Indirizzo 0xbffff628 c'e' il valore 223
LeVariabiliApprofondimento[1085] Ora all' Indirizzo 0xbffff628 c'e' il valore 548
LeVariabiliApprofondimento[1085] ed ovviamnete anche nNumeroRivista vale 548

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.

Saluti e buone feste.


Il programmatore che c’e’ in noi – Lezione 6 – Aritmetica e Puntatori

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.

Bene, cosa significa ?

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…)

Si dice quindi che la variabile PUNTA all’oggetto indicato dall’indirizzo.

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 ?

La situazione di via Verdi e’ questa:


via Verdi 8 – Prima Villa bifamiliare (prima villa)
via Verdi 9 – Prima Villa bifamiliare (seconda villa)

via Verdi 10 – Seconda Villa bifamiliare (prima villa)


via Verdi 11 – Seconda Villa bifamiliare (seconda villa)

Andate alla villa al N° 9 o quella al N° 10 ?

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.

& Serve per ricavare l’indirizzo di memoria di un oggetto


* Serve per dichiarare una variabile 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

nNumeroCivico=8; //Assegno il valore alla variabile int


pUnNumeroCivico = & nNumeroCivico; //ricavo l’indirizzo di memoria della variabile ad un int

nUnAltroNumeroCivico = *pUnNumeroCivico; //ricavo il valore della variabile che si trova


//all’indirizzo contenuto in pUnNumeroCivico
// ossia l’indirizzo di nNumeroCivico e di
// conseguenza il suo valore e cioe’ 8.

//a questo punto la variabile nUnAltroNumeroCivico vale 8.

*pUnNumeroCivico = 12; //Cosa succede ?

//Semplice, ho modificato il valore contenuto nella variabile puntata da pUnNumeroCivico ossia


nNumeroCivico, quindi a questo punto nNumeroCivico vale 12.
---------------------------------------------------------------------------------------------------------------------
OPERATORI ARITMETICI

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;

nEta= nAnnoCorrente – nAnnoNascita; //Uso operatore – per effettuare il calcolo e l’operatore =


per assegnare il risultato (40) alla variabile nEta
int nNumero=45;
BOOL bIsDispari=FALSE;

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++; //uso l’operatore di incremento per incrementare di 1 il valore della variabile

// nContatore a questo punto vale 1

nContatore++;

// nContatore a questo punto vale 2

nContatore--; //uso l’operatore di decremento per decrementare di 1 il valore della variabile

// nContatore a questo punto vale 1

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)

//Se invece scrivo


nValore =nContatore++; // nValore = Vale 0, nContatore Vale 1 (prima viene assegnato il valore di
nContatore (0) alla variabile nValore, quindi viene incrementato il valore di nContatore da 0 a 1)

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.

PRECEDENZA DEGLI OPERATORI ARITMETICI

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;

nValore = nPrimoNumero * ++nSecondoNumero – nTerzoNumero; // Quanto vale ?


//prima viene valutato l’operatore ++ che ha precedenza piu’ alta, quindi ++nSecondoNumero = 3
//quindi viene valutato l’operatore * di moltiplicazione nPrimoNumero * 3 = 5*3=15
//Infine viene valutato l’operatore – di sottrazione 15 – nTerzoNumero= 15 – 4 = 11
//Il risultato dell’espressione e’ quindi 11

//La seguente espressione fornisce come risultato -5


nValore = nPrimoNumero * (++nSecondoNumero – nTerzoNumero); // Quanto vale ?

//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

Per i piu’ intraprendenti …

nValore = nPrimoNumero * nSecondoNumero++ – nTerzoNumero; // Quanto vale ?


//In questo caso, la valutazione dell’espressione da come risultato 6 come mai ?
Ovviamente il mio personale consiglio e’ quello di scrivere sempre il codice (=il programma, il
sorgente) nella maniera piu’ chiara possibile (=leggibile)

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;

nValore = (nPrimoNumero * nSecondoNumero) – nTerzoNumero; // Quanto vale ?

Bene, siamo ora in grado di parlare della Aritmetica dei Puntatori, come indicato nel titolo di questa
lezione…

Supponiamo di avere questa situazione

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

int* pIndirizzoDiEta=nil; //dichiaro la variabile puntatore e la inizializza a nil (nulla)

pIndirizzoDiEta = &nEta; //Calcolo l’indirizzo di memoria della variabile nEta e lo memorizzo


//nella variabile puntatore

nOldEta=*pIndirizzoDiEta; //Ricavo il valore della variabile puntata dal puntatore e lo memorizzo


//in nOldEta, questa istruzione equivale a scrivere nOldEta = nEta;

pIndirizzoDiEta++; //Incremento il valore di pIndirizzoDiEta, CHE E’ UN indirizzo di memoria


//Di quanto e’ stato incrementato ?
//A cosa punta il nuovo indirizzo ?
Per rispondere a queste domande, facciamoci dire quanto vale l’indirizzo contenuto in
pIndirizzoDiEta prima e dopo l’incremento usando
NSLog(@"Indirizzo contenuto pIndirizzoDiEta=%p",pIndirizzoDiEta);

Questo e’ il risultato relativo all’esecuzione sul mio mac,


pIndirizzoDiEta=0xbffffa18 (prima)
pIndirizzoDiEta=0xbffffa1c (dopo)

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

pIndirizzoDiEta=&nEta; //Calcolo indirizzo e lo metto in pIndirizzoDiEta

pIndirizzoUnByte = pIndirizzoDiEta; //Assegno il valore di pIndirizzoDiEta anche a


//pIndirizzoUnByte (vedi nota)

pIndirizzoDiEta++; //Indirizzo ora e’ stato incrementato di 4 (un int vale 4 bytes)


pIndirizzoUnByte ++; //Indirizzo ora e’ stato incrementato di 1 (un char vale 1 byte)

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

Se invece di incrementare l’indirizzo di una variabile puntatore, volessi incrementare il contenuto


della variabile a cui punta l’indirizzo si procede nel seguente modo

(*pIndirizzoDiEta)++; //Incremento il valore puntato dall’indirizzo contenuto in pIndirizzoDiEta

(*pIndirizzoDiEta) += 4; //Sommo 4 al valore puntato dall’indirizzo contenuto in pIndirizzoDiEta

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.

int nEta=10; //Variabile di tipo int


int nValore=0; //Variabile di tipo int
int* pIndirizzoDiEta=nil; //dichiaro la variabile puntatore e la inizializza a nil (nulla)

pIndirizzoDiEta = &nEta; //Calcolo l’indirizzo di memoria della variabile nEta e lo memorizzo


//nella variabile puntatore

(*pIndirizzoDiEta)++; // Incremento il valore della variabile puntata da pIndirizzoDiEta quindi


// nEta, che passa da 10 a 11.

(*pIndirizzoDiEta)+=5; // Incremento il valore della variabile puntata da pIndirizzoDiEta quindi


// nEta, che passa da 11 a 16.

(*pIndirizzoDiEta)=25; // Assegno il valore della variabile puntata da pIndirizzoDiEta quindi


// nEta, che passa da 16 a 25.

nValore = (*pIndirizzoDiEta); //Assegno a nValore il valore puntato da pIndirizzoDiEta


//ossia nEta che attualmente vale 25

pIndirizzoDiEta++; //Incremento l’indirizzo di memoria contenuto in pIndirizzoDiEta che


//passa ad esempio da 0xbffffbac a 0xbffffbb0 (+4Bytes)

nValore = (*pIndirizzoDiEta); //Assegno a nValore il valore puntato da pIndirizzoDiEta


// !!!! ATTENZIONE !!! Ora non punta piu’ a nEta, quindi non
//siamo in grado di sapere a cosa punta e di conseguenza a quale e’ il
//valore contenuto alla //locazione di memoria indicata

(*pIndirizzoDiEta)=67; //!!! ATTENZIONE !!! Operazione MOLTO pericolosa, nel senso


//che stiamo scrivendo in una zona di memoria di cui non conosciamo
//il significato di conseguenza si rischia di “sporcare”
//sino a rischiare il crash (=il blocco dell’applicazione, la chiusura
// inaspettata…)
//come l’esempio del log del crash della mia applicazione
//quando ho cercato di eseguire queste istruzioni
// pIndirizzoDiEta-=0xbffffbc4;
// (*pIndirizzoDiEta)=789;

Exception: EXC_BAD_ACCESS (0x0001)


Codes: KERN_INVALID_ADDRESS (0x0001) at 0xc0000d78

Spero, di essere riuscito a essere chiaro ed esauriente in questa spiegazione.


Le prossime lezioni, saranno di sicuro meno “pesanti”, la parte piu’ difficile ed ostica e’ proprio
questa sui puntatori, quindi sforzatevi di comprenderne appieno il funzionamento, dopo di che’ tutto
il resto e’ una passeggiata (o quasi…).

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.

[1° cassetto] [2° cassetto] [3° cassetto] (prima riga)


[1° cassetto] [2° cassetto] [3° cassetto] (seconda riga)

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.

Per dichiarare una array di variabili si usa la seguente sintassi

Singola dimensione
TYPO_VARIABILE NOME_VARIABILE [NUMERO_ELEMENTI];

Due dimensioni
TYPO_VARIABILE NOME_VARIABILE [NUMERO_RIGHE] [NUMERO_COLONNE];
Operazioni sugli arrays

int nEtaPersone[50]; //Array di 50 elementi (accessibili usando indice tra 0 e 40 compresi)


// di tipo INT
char strCodice[12]; //Array di 12 char (caratteri) accessibili con indice 0-11
BOOL bBattagliaNavale[3][4];//Array bidimensionale di tre righe e quattro colonne di tipo BOOL

Per assegnare il valore ad uno specifico elemento

nEtaPersone[5]=37; //Assegna il valore 37 al 6° elemento dell’array … il primo elemento e’ 0…

//Assegno dei caratteri all’array strCodice che e’ di tipo char


strCodice=[0]=’C’;
strCodice=[1]=’i’;
strCodice=[2]=’a’;
strCodice=[3]=’o’;

bBattagliaNavale[0][0]=TRUE; //Assegno valore del primo elemento


bBattagliaNavale[2][3]=TRUE; //Assegno valore dell’ultimo

Per leggere il valore contenuto nell’array

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

//Dichiaro un array di caratteri di 12 elementi, accessibili con indice tra


//0 e 11 e lo inizializzo con la scritta Tevac Corso

char strCodice[12]={'T','e','v','a','c',' ','C','o','r','s','o','.'};

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)

‘T’ e’ una costante di tipo char (=di tipo carattere)

int nCosti[3]={ 1200, 800, 899 }; //Inizializzo un array di dipo int

double nQta={ 123.45, 234.56, 89.00 } //Inizializzo un array di tipo double

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
};

Esempio di inizializzazione di una array con l’operatore di assegnazione


E’ necessario assegnare singolarmente i valori ai vari elementi che compongono l’array.

int nCosti[3]; //dichiaro l’array di interi ma non e’ inizializzato

//inizializzo i valori dell’array


nCosti[0]=1200;
nCosti[1]=800;
nCosti[2]=899;

//Modico il valore di un elemento dell’array


char strCodice[12]={'T','e','v','a','c',' ','C','o','r','s','o','.'};

strCodice[5]=’!’; //ora strCodice contiene Tevac!Corso ho sostituito il valore


//del sesto elemento dell’array (era uno spazio) con il !
Uso dei puntatori con gli array

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.

//Dichiaro una variabile puntatore ad interi e la inizializzo a NULL


int* pElemento=NULL;
int nCosti[3]={ 1200, 800, 899 }; //Dichiaro ed inizializzo un array di dipo int di TRE elementi.

//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

(*pElemento)=45; //ASSEGNO IL VALORE 45 alla variabile puntata dal puntatore


//pElemento (in questo caso e' il primo elemento dell'array)

pElemento++; //Passo all'elemento successivo dell'array (indice 1)

(*pElemento)=78; //Ora pElemento PUNTA al secondo elemento dell’array (indice = 1)

E cosi via. Attenzione a non superare il numero di elementi precedentemente dichiarati nella’array.

Concludo con un esempio pratico dell’uso di un array.

Esempio:

Vogliamo memorizzare le nostre uscite finanziarie nei diversi mesi dell’anno.


Se non uso gli array, devo dichiarare 12 variabili per memorizzare il valore delle uscite per ciascun
mese invece mi basta dichiarare un array di 12 elementi.

int nSpeseNelMese[12]; //Spese per ciascun mese

//Inizializzo l’array con le spese, mese per mese

nSpeseNelMese[0]=120; //speso a Gennaio


nSpeseNelMese[1]=170; //speso a Febbraio
nSpeseNelMese[2]=190; //speso a Marzo
nSpeseNelMese[3]=10; //speso a Aprile
nSpeseNelMese[4]=140; //speso a Maggio
nSpeseNelMese[5]=180; //speso a Giugno
nSpeseNelMese[6]=190; //speso a Luglio
nSpeseNelMese[7]=320; //speso a Agosto
nSpeseNelMese[8]=520; //speso a Settembre
nSpeseNelMese[9]=220; //speso a Ottobre
nSpeseNelMese[10]=1120;//speso a Novembre
nSpeseNelMese[11]=70; //speso a Dicembre

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)

Tanto per essere chiari:

char nome[10]; //Dichiara un array di 10 caratteri (indicizzabile tra 0 e 9)

nome[0]=’F’;
nome[1]=’r’;
nome[2]=’a’;
nome[3]=’n’;
nome[4]=’c’;
nome[5]=’o’;
nome[6]=’\0’;

Ora nome e’ una stringa cioe’ “Franco”.


La prima implicazione e’ che quando io devo dichiarare la dimensione del mio array che dovra’
contenere una stringa DEVO aggiungere 1 alla dimensione che mi serve, in quanto devo
considerare il carattere NUL che devo aggiungere per terminare la stringa.
Nell’esempio indicato il testo massimo che potra’ contenere la stringa nome, e’ di 9 caratteri + 1
NUL (totale 10 singoli caratteri, come dichiarato all’inizio)

L’istruzione
nome[6]=’\0’;
puo’ essere tranquillamente sostituita con
nome[6]=0x0; //NUL valore esadecimale
oppure
nome[6]=0; //NUL valore decimale

Fate attenzione a non scrivere


nome[6]=’0’; //In questo caso assegno la cifra 0 e non NUL !!! Quindi nome non e’ stringa, ma
SOLO un array di caratteri.

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 mettere in pratica quanto esposto ho realizzato il programmino scaricabile qui

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

Tutti abbiamo un carta d’identita’. (la mia e’ scaduta, tra l’altro…)

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.

Bene, la carta di identita’ e’ una STRUTTURA DATI.

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:

//DICHIARAZIONE DELLA STRUTTURA (come e’ composta, ma non crea nessuna variabile)


struct NOMESTRUTTURA {
TIPO NOMEVARIABILE;
TIPO NOMEVARIABILE;

};

//DICHIARAZIONE DI UNA VARIABILE CHE USA LA STRUTTURA


struct NOMESTRUTTURA NOMEVARIABILE;

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,

Facciamo un po’ di esempi .

Creiamo ad esempio la struttura per “simulare” la nostra carta d’identita’.

ESEMPIO (1)

//Dichiaro come e’ costituita la struttura


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
};

//Mi serve una carta di identita’, creo la variabile


struct cartaIdentita CartaIdentitaFranco;

Ogni volta che nel programma avro’ bisogno di una nuova carta di identita’ da gestire, sara’
sufficiente scrivere

struct cartaIdentita CartaIdentitaMarco;

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

Rappresentazione in memoria della nostra variabile di tipo struttura.


Ogni variabile quando viene stanziata (allocata) per poter essere utilizzata all’interno di un
programma,occupa una determinata quantita’ di RAM (in funzione del tipo di dato) e viene
posizionata in una specifica locazione della nostra memoria ram.

Cosa avviene quando definisco in dato di tipo struct e creo un istanza di tale variabile:
Dove e’ posizionata in memoria ? Quanta memoria occupa ?

Analizziamo l’ESEMPIO (1).

La variabile CartaIdentitaFranco sara’ posizionata in memoria in una qualche locazione (possiamo


sapere quale usando l’operatore & per farci restituire l’indirizzo di memoria)

Quindi per conoscere la locazione di memoria in cui si trova la nostra struttura in questo modo
NSLog(“Indirizzo della variabile : %p”,& CartaIdentitaFranco);

Per conoscere la dimensione, uso sizeof()


NSLog(“Dimensione della variabile: %d”, sizeof( CartaIdentitaFranco));

Accesso alle variabili contenute nella struttura (VARIABILI MEMBRO)

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.nome,”Francesco”); //Assegno la stringa Francesco alla variabile


membro nome, per accedere alla variabile nome uso operatore .

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

Per visualizzare il contenuto della variabile CartaIdentitaFranco

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…

//Per accedere ai dati dell’ultima carta di identità la 1199 esima.


strcpy(elencoVotanti[MAX_PERSONE-1].nome,”Franco”);
Il programmatore che c’e’ in noi – Lezione 10 – Unioni

Premessa: non dovete avere mal di testa.

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.

//Dichiarazione di una struct

struct datiProdotto{
char nome[21]; //Nome del prodotto 20 char
char tipo[11]; //Tipologia
double prezzo; //Prezzo
}unaStrutturaProdotto;

//In memoria ho a questo punto costruito qualcosa del genere


//[01234567890123456789][0123456789][12345678]
// nome tipo prezzo
//Totale Memoria Occupata = 21+11+8 = 40 BYTE

//Vediamo se e' vero:


int nDimStrutturaProdotto=sizeof(struct datiProdotto);
NSLog(@"Memoria occupata dalla struttura prodotto:
%d\n",nDimStrutturaProdotto); //=40

//Vediamo invece una union


union datiProdottoU{
char nome[21]; //Nome del prodotto 20 char
char tipo[11]; //Tipologia
double prezzo; //Prezzo
}unaUnionProdotto;

//In memoria questa volta ho costruito qualcosa del genere


//[01234567890123456789] nome
//[0123456789] tipo
//[12345678] prezzo

//Quindi tutte e tre le variabili utilizzano la stessa zona di memoria


//Il totale della memoria occupata e' quindi quella della variabile che
occupa piu' memoria
//e non dalla somma delle quantita

//Totale Memoria Occupata = 21 BYTE (la variabile che occupa piu' spazio e'
nome di 21 Byte)

//Vediamo se e' vero:


int nDimUnionProdotto=sizeof(union datiProdottoU);
NSLog(@"Memoria occupata dalla union prodotto: %d\n",nDimUnionProdotto);
//Gulp ! = 24 !!!

//Bene, 24 e' comunque vicino a 21, che e' il valore che avevo previsto, ma
perche' e 24 ?

//Il computer per ottimizzare la velocita' di esecuzione nelle operazioni che


riguardano
//la copia o lo spostamento dei dati nella memoria, preferisce avere a che
fare con dimensioni
//di dati che siano dei multipli un INT.

//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

//Assegnamo dei valori alla union e vediamo come si comporta


strcpy(unaUnionProdotto.nome,"Lamponi e Fragole");

//Vediamo cosa contiene la nostra union


NSLog(@"unaUnionProdotto.nome = '%s'\n",unaUnionProdotto.nome);

//Ora assegno il valore alla seconda variabile ...


strcpy(unaUnionProdotto.tipo,"Frutta");

//Vediamo cosa contiene la nostra union


NSLog(@"unaUnionProdotto.nome = '%s'\n",unaUnionProdotto.nome);
//Vediamo cosa contiene la nostra union
NSLog(@"unaUnionProdotto.tipo = '%s'\n",unaUnionProdotto.tipo);

//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

//Cosa succede se ora assegno un valore numerico a prezzo ? Vediamo.


unaUnionProdotto.prezzo=1200;

//Vediamo cosa contiene la nostra union


NSLog(@"unaUnionProdotto.nome = '%s'\n",unaUnionProdotto.nome); // '@í¿'
NSLog(@"unaUnionProdotto.tipo = '%s'\n",unaUnionProdotto.tipo); // '@í¿'
NSLog(@"unaUnionProdotto.prezzo = '%.0f'\n",unaUnionProdotto.prezzo); // 1200

//Quindi ogni assegnazione che vado a fare ad una variabile, comporta la


modifica del contenuto
//anche delle altre variabili, questo e' dovuto al fatto che TUTTE le
variabili stanno
//PUNTANDO alla stessa zona di memoria.
//Controlliamo se e' vero ...

NSLog(@"\n\nTutte le variabili della struct puntano ad una locazione propria


della memoria\n");

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);

NSLog(@"\n\nTutte le variabili della union puntano alla stezza locazione di


inizio della memoria\n");

NSLog(@"Indirizzo unaUnionProdotto.nome = 0x%x\n",&unaUnionProdotto.nome);


NSLog(@"Indirizzo unaUnionProdotto.tipo = 0x%x\n",&unaUnionProdotto.tipo);
NSLog(@"Indirizzo unaUnionProdotto.prezzo =
0x%x\n",&unaUnionProdotto.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.

//Dichiaro una union come composta da un int e da 4 unsigned char


union convInt{
int nValore;
unsigned char byte[4];
}dato;

//Assegno il valore all'intero


dato.nValore=168987384;
//Leggo il valore dell'intero
NSLog(@"Valore di nValore = %d\n",dato.nValore);

//Leggo il valore dell'intero come rappresentato dai 4 BYTE in memoria


NSLog(@"Valore di nValore = %d BYTE0: %d BYTE1: %d BYTE2: %d BYTE3: %d
\n",dato.nValore,dato.byte[0],dato.byte[1],dato.byte[2],dato.byte[3]);

A questo punto il problema che ci eravamo posti e’ stato risolto.

E se volessi calcolare il valore di un int conoscendo i valori dei singoli byte


che lo rappresentano ?

Per convertire il valore rappresentato in BYTE in un int e' sufficiente


moltiplicare il valore di ciascun byte per le potenze di 256 descrescenti a
partire da 3

//quindi valoreIntero= byte0 * 256^3 + byte1*256^2+byte2*256^1+byte3*256^0

//semplificando (in quanto 256^1 = 256 e 256^0=1)

//valoreIntero= byte0 * 256^3 + byte1*256^2+byte2*256+byte3

int aValoreRappresentato=(dato.byte[0] * pow(256,3)) + (dato.byte[1] *


pow(256,2)) + (dato.byte[2] * pow(256,1)) + (dato.byte[3] * pow(256,0));

NSLog(@"valore rappresentato: %d",aValoreRappresentato);

Bene, ovviamente se modifico il valore di un byte della union, cambiera’ il valore della variabile
intero in essa presente.

//Se cambio il valore di un byte, cambia anche il valore dell'intero presente


nella union
dato.byte[0]=0;
dato.byte[1]=0;
dato.byte[2]=2;
dato.byte[3]=128;

NSLog(@"Valore di nValore = %d\n",dato.nValore);

ora vale 640 (provate a calcolarlo con il metodo sopra descritto…)

Per i piu’ curiosi e volenterosi


Se dovessimo rappresentare il valore binario di un carattere, come possiamo fare, usando le struct e
le union esiste una semplice soluzione, che trovate nell’esempio legato a questa lezione.
Provare a farlo senza guardare la soluzione!
Chi vuole potrebbe implementare il programma CheCarattere creato in una delle scorse puntate
aggiungendo la possibilita’ di visualizzare anche il valore binario dei caratteri e renderlo disponibile
anche agli altri.

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.

Una enumerazione si definisce nel seguente modo:

//Definizione della enum


enum tag{
nomeelemento,
nomeelemento,
nomelemento,

};

//Dichiarazione di una variabile di tipo enum


enum tag nomevariabile;

oppure, tutto insieme…

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.

Quindi fantascienza vale 0, giallo=1, avventura=2 ecc.

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.

int main(int argc, char *argv[])


{
//Dichiarazione dell'enum
enum genere{
fantascienza, //=0
giallo, //=1
avventura, //=2
fantasia, //=3
informatica //=4
}genereDiLibro; //Dichiarazione della variabile

NSLog(@"Valore dell'elemento informatica = %d",informatica);

return NSApplicationMain(argc, (const char **) argv);


}

Eseguiamo ed ecco il risultato …


2006-07-02 10:28:00.048 Enumerazioni[943] Valore dell'elemento informatica = 4

Quanto vale la nostra variabile genereDiLibro ?


NSLog(@"Valore della variabile genereDiLibro: %d ",genereDiLibro); //Bho ? Non
e' stata assegnata! Quindi dipendera' da quello che trova in memoria
Infatti ecco il mio log
2006-07-02 10:30:35.339 Enumerazioni[947] Valore della variabile genereDiLibro: 7468

Bene, inizializziamola
genereDiLibro=informatica; //Inizializzo la variabile con un elemento della enum

NSLog(@"Valore della variabile genereDiLibro: %d ",genereDiLibro); //Ora vale


informatica cioe' 4.

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");

che e’ molto piu’ chiaro di

if( genereDiLibro == 4)
NSLog(@"Genere di libro: informatica");

Per i piu’ curiosi


E’ possibile far partire l’indice dei valori da assegnare agli elementi della enum con un valore
numerico intero qualsiasi (se non viene specificato nulla, parte da 0).

Esempio

enum colors{
rosso=100,
verde,
gialloC, //Ho gia’ giallo definito nella enum genere devo dare un altro nome !
marrone=0,
nero,
bianco
};

NSLog(@"Rosso: %d Verde: %d Giallo: %d Marrone: %d Nero: %d Bianco: %d",


rosso,verde,gialloC,marrone,nero,bianco);

E’ questo e’ il risultato del log

2006-07-02 10:40:51.803 Enumerazioni[994] Rosso: 100 Verde: 101 Giallo: 102 Marrone: 0 Nero:
1 Bianco: 2

TYPEDEF – Tipi dati utente

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

//Dati definiti da utente


typedef double stipendio;

stipendio meseGennaio=120.0;
stipendio meseFebbraio=180.0;
stipendio meseMarzo=220.0;

stipendio medio=0.0;

medio =( meseGennaio + meseFebbraio + meseMarzo) /3.0;

NSLog(@"Stipendio Medio: %.2f",medio);

L’utilizzo di typedef ha un duplice scopo:


1 – migliora la leggibilita’ e la comprensione del codice
2 - facilita la migrazione del programma tra i sistemi (es. Mac os x, Unix, Linux, Windows)
questa caratteristica dei programmi si chiama PORTABILITA’.

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.

Se, invece abbiamo scritto

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.

Riprendo l’esempio della carta di identita’…

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

struct cartaIdentita CartaIdentitaFranco; //Creo la variabile

A questo punto posso lavorare sulla variabile CartaIdentitaFranco.

L’uso dell’istruzione typedef, ci consente di creare un tipo dati


typedef struct cartaIdentita cIdentita;

quindi ora per creare la variabile di tipo cartaIdentita, e’ necessario indicare solo
cIdentita CartaIdentitaFranco;

Riepilogo la differenza:
1) senza usare typedef

struct cartaIdentita CartaIdentitaFranco; //Creo la variabile


struct cartaIdentita CartaIdentitaRossi; //Creo la variabile
struct cartaIdentita CartaIdentitaMarco; //Creo la variabile

//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.

typedef 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
} cIdentita;

cIdentita fg;
strcpy(fg.nome,"Francesco");
NSLog(@"nome: %s",fg.nome);

Concludo segnalando la possilita’ di utilizzare typedef anche per le enum e le union.

Questo e’ un esempio preso pari-pari dalla classe NSButtonCell, che definisce i possibili tipi di
pulsanti utilizzabili tramite Cocoa.

typedef enum _NSButtonType {


NSMomentaryLightButton = 0, // was NSMomentaryPushButton
NSPushOnPushOffButton = 1,
NSToggleButton = 2,
NSSwitchButton = 3,
NSRadioButton = 4,
NSMomentaryChangeButton = 5,
NSOnOffButton = 6,
NSMomentaryPushInButton = 7, // was NSMomentaryLight

/* These constants were accidentally reversed so that NSMomentaryPushButton


lit and
NSMomentaryLight pushed. These names are now deprecated */

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:

NSButtonType tipoDiMioPulsante; //Dichiaro la variabile tipoDiMioPulsante


//Di tipo enum _NSButtonType

tipoDiMioPulsante = NSMomentaryLight; //Assegno il valore alla mia variabile


//Uso uno dei valori previsti dalla Apple

NSLog(@"tipoDiMioPulsante: %d",tipoDiMioPulsante); //vale 7.

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

Mi sveglio, vado in bagno, doccia, colazione.


Per colazione, preparo il caffe’, accendo il gas, se e’ acceso metto la caffettiera sopra il fornello ed
aspetto. Aspetto sino a quando non sento la caffettiera brontolare, mi alzo, se il caffe’ e’ salito
spengo il fornello del gas.
Bevo il caffe’.
Cerco le chiavi della macchina sino a quando non le trovo. Scendo, guido sino all’ufficio, incontro
un semaforo, se e’ verde, passo, se e’ rosso mi fermo (…ehm… una volta mi sono distratto e sono
passato con il rosso, mi e’ costata una multa salata e 6 punti sulla patente…).
Arrivo nel piazzale dell’ufficio, ma e’ tutto pieno, allora giro, giro, giro sino a quando non si libera
un posto. Parcheggio. Lavoro, poi rientro a casa, se e’ pronta la cena, mangio, altrimenti improvviso
qualche piatto veloce. Un po’ di TV (o di Mac ((vedi stasera…)) ) poi vado a dormire.

Un programma, come nella vita reale, non e’ quasi mai sequenziale.

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.

Gli statements possono essere raggruppati in

- decisionali - if, if else if, ?, switch


- iterazioni - for, while,do while
- salti - return,goto,break,continue
- etichette - nomeetichetta:
- espressioni - una espressione terminata con;
- blocchi - { }

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>

int main(int argc, char *argv[])


{
return NSApplicationMain(argc, (const char **) argv);
}

Per fare il paragone di quanto scritto all’inzio,


“Mi sveglio” equivale a “utente manda in esecuzione il programma”,quindi il programma si sveglia,
ossia esegue il codice sopra specificato o per essere piu’ precisi esegue la funzione
int main(int argc, char *argv[]).

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.

Quindi, individuiamo il file main.m e modifichiamo il codice in questo modo

#import <Cocoa/Cocoa.h>

int main(int argc, char *argv[])


{
NSLog(@"Programma partito");

return NSApplicationMain(argc, (const char **) argv);

Compiliamo il programma (Build e Run) e guardiamo cosa succede:


Compare la classica finestra vuota e nel file di run log
[Session started at 2006-07-20 22:34:47 +0200.]
2006-07-20 22:34:47.983 statements[367] Programma partito

Bene, e quando termina ? Bella domanda.

L’istruzione return NSApplicationMain(argc, (const char **) argv);


dobbiamo immaginarla composta da queste parti

//1: Variabile di tipo int


int valoreRestituito=0;

//2: esegue la funzione NSApplicationMain ed assegna il valore restituito dalla


funzione alla variabile valoreRestituito;
valoreRestituito=NSApplicationMain(argc, (const char **) argv)

//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;

dato che il blocco di programma da cui si esce e’ il main(){ } il programma


termina la propria esecuzione restituendo al sistema operativo, il codice di
ritorno (valoreRestituito).

Se, quindi, desiderassi visualizzare un messaggio “programma terminato” quando il programma e’


prossimo al termine della sua esecuzione, usando NSLog(@"Programma terminato");
probabilmente cercherei di scrive qualcosa del genere

#import <Cocoa/Cocoa.h>
int main(int argc, char *argv[])
{
NSLog(@"Programma partito");

return NSApplicationMain(argc, (const char **) argv);

NSLog(@"Programma terminato"); //NON FUNZIONA! Questa istruzione non sara’


//mai eseguita, in quanto e’ stata incontrata
//una istruzione return che ha causato
//l’uscita da questo blocco,
}
Provare per credere…

Bene, modifichiamo leggermente il codice in modo da poter intercettare il momento della


terminazione dell’applicazione, per fare cio’ e’ necessario commentare la riga in cui viene effettuata
la chiamata a NSApplicationMain() che crea ed esegue l’applicazione cocoa.
Nota: date un occhiata alla documentazione Apple relative al tale funzione.

#import <Cocoa/Cocoa.h>

int main(int argc, char *argv[])


{
NSLog(@"Programma partito");

// return NSApplicationMain(argc, (const char **) argv);

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

statements has exited with status 0.

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>

int main(int argc, char *argv[])


{
NSLog(@"Programma partito");
// return NSApplicationMain(argc, (const char **) argv);

NSLog(@"Programma terminato");

return 23;
}

[Session started at 2006-07-20 23:01:43 +0200.]


2006-07-20 23:01:43.453 statements[513] Programma partito
2006-07-20 23:01:43.454 statements[513] Programma terminato

statements has exited with status 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…)

Ritorniamo alla storiella iniziale; e proviamo a farne il programma…


Mi sveglio
int main(int argc, char *argv[]){

vado in bagno,
vaiInBagno(); //Eseguo la funzione
doccia,
faiDoccia(); //Eseguo la funzione
colazione
faiColazione();//Eseguo la funzione

cerco le chiavi della macchina, sino a quando non le trovo…

BOOL bHoLeChiaviDellaMacchina=FALSE;

while(bHoLeChiaviDellaMacchina!=TRUE){
bHoLeChiaviDellaMacchina=cercaChiaviDellaMacchina();
}

incontro un semaforo, se e’ verde, passo, se e’ rosso mi fermo

BOOL bSemaforoVerde=FALSE;
if(bSemaforoVerde == FALSE){
aspetto();
}else{
parcheggio();
}

Un po’ di TV (o di Mac ((vedi stasera…)) )


NSLog(@"un po' di TV o MAC...");

poi vado a dormire


NSLog(@"Programma terminato (=poi vado a dormire)");
return 23;
}

Questo e’ il run log del programma

[Session started at 2006-07-20 23:42:58 +0200.]


2006-07-20 23:42:58.071 statements[742] Programma partito
2006-07-20 23:42:58.072 statements[742] Sono in bagno!
2006-07-20 23:42:58.072 statements[742] Sono sotto la doccia!
2006-07-20 23:42:58.072 statements[742] Metto il caffe'
2006-07-20 23:42:58.072 statements[742] guido...
2006-07-20 23:42:58.072 statements[742] semaforo rosso...aspetto...5 secondi
2006-07-20 23:43:03.072 statements[742] semaforo Verde!
2006-07-20 23:43:03.072 statements[742] lavoro...
2006-07-20 23:43:03.072 statements[742] cena...
2006-07-20 23:43:03.072 statements[742] un po' di TV o MAC...
2006-07-20 23:43:03.072 statements[742] Programma terminato (=poi vado a dormire)

statements has exited with status 23.

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.

Mi auguro di essere stato sufficientemente chiaro e vi saluto.

In allegato il progetto in Xcode 2.3 contenente l’esempio trattato.


Il programmatore che c’e’ in noi – Lezione 13– Decidere…decidere

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 ?

Ci sono macchine che arrivano ?

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

Esco per andare a comperare un topolino

Se(non ho soldi in tasca)


PassareAlBancomat;

entrare dal giornalaio;

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;

ed anche annidare delle istruzioni if else if


4)
if(espressione)
istruzioneSeVeroEspressione1;
else if (espressione2)
istruzioneSeVeroEspressione2;
else if (espressione3)
istruzioneSeVeroEspressione3;
else
istruzioneSeFalso;

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.

Videata del programma


Codice esempio

- (IBAction)valutaSituazione:(id)sender{ //Questo metodo e' chiamato, a seconda


dei casi da
//
chkBoxSemaforoVerde2,chkBoxArrivaUnAuto2 o chkBoxVaPiano2

BOOL bStatoSemaforo=[chkBoxSemaforoVerde2 state];


BOOL bArrivaUnAuto=[chkBoxArrivaUnAuto2 state];
BOOL bVaPiano=[chkBoxVaPiano2 state];

//La valutazione che effettuo e' la seguente:


/*
se il semaforo e' ROSSO
FERMO
altrimenti
se arriva un auto
se va piano
ATTRAVERSO
altrimenti
FERMO
altrimenti
ATTRAVERSO
*/

if(bStatoSemaforo == NSOffState){ //Semaforo verde ?


[txtAzione2 setStringValue:@"FERMO!!!"]; //SEMAFORO ROSSO !
}else{ //Ok, e' verde
if(bArrivaUnAuto==NSOnState){ //Ci sono auto ?
if(bVaPiano == NSOnState){ //Si, ci sono. Vanno piano ?
[txtAzione2 setStringValue:@"Puoi attarversare!"]; //Si,vanno piano.Posso
Attraversare
}else{ //Ci sono auto e non vanno piano.
[txtAzione2 setStringValue:@"FERMO!!!"]; //Arriva un auto, veloce. FERMO.
}
}else{ //Il semaforo e' VERDE e non ci sono AUTO. Passo Tranquillo.
[txtAzione2 setStringValue:@"Puoi attarversare!"];
}
}
}
Il programma di esempio e’ scaricabile al seguente link
http://www.germinara.it/corsoprogrammazione/decidere.zip
Il programmatore che c’e’ in noi – Lezione 14– Nel caso in cui …

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.

Si tratta quindi di creare il programma per la macchinetta del caffe’.

Analiziamo la situazione (voi siete la macchinetta):

Macchinetta: aspetto che qualcuno metta dei soldi … uffa… che noia…

Persona: Inserisco un bell’euro, e dopo attenta riflessione, scelgo un caffe’ normale,amaro.

Macchinetta: … uao! Qualcuno a messo dei soldi… sentiamo cosa vuole… aspetto che prema un
bottone

Persona: Premo il bottone, caffe’ normale amaro

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…

Persona: Prendo il resto e prelevo la bevanda.

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.

Tale istruzione e’ SWITCH

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:

La nostra macchinetta ci consente di scegliere tra le seguenti bevande


1 Caffe’ Normale Amaro
2 Caffe’ Lungo Amaro
3 Caffe’ Normale Dolce
4 Caffe’ Lungo Dolce
5 Caffe’ macchiato
6 The al limone
7 The alla pesca
8 Cioccolata
9 Cioccolata e Latte

Di conseguenza, quando viene premuto un pulsante (tra 1 e 9), la macchinetta deve preparare la
relativa bevanda.

Il programma addatto ad effettuare tale operazione potrebbe essere del tipo

int nNumeroBottonePremuto=0;

nNumeroBottonePremuto=UtenteSceglieBevanda(); //Restituisce il numero del pulsante premuto

//Valutiamo quale bevanda e’ stata scelta

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
}

2) MODO: Usiamo SWITCH

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

chMenuScelto=getSceltaUtente(); //Restituisce il carattere corrispondente alla scelta del menu

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.