Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza...

301
MASSIMO UBERTINI VISUAL C++ WWW.UBERTINI.IT

Transcript of Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza...

Page 1: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

MASSIMO UBERTINI

VISUAL C++

WWW.UBERTINI.IT

Page 2: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 1 di 299

MODULO 1

LINGUAGGIO C Introduzione

Page 3: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 2 di 299

INTRODUZIONE

GENERALITÀ Caratteristiche. Portabile. Case-sensitive. Modulare, sciolto e flessibile. Non ha funzioni d’I/O, ogni utente se le costruisce e le memorizza nella libreria. Le funzioni disponibili sono di due tipi. 1. System I/O, veloci ma non compatibili. 2. Standard I/O, lente e voluminose. Il linguaggio ha poche primitive e non ha un forte controllo di tipo, come ad esempio il linguaggio Pascal. Strumento per professionisti È possibile produrre applicazioni la cui efficienza è paragonabile a quella delle applicazioni scritte in assembly, potendo però usufruire dei vantaggi di un linguaggio ad alto livello. Linguaggio di medio livello Perché unisce elementi dei linguaggi ad alto livello con le funzionalità dell’assembly, quindi consente la gestione di bit, byte e indirizzi, queste possibilità sono adatte alla programmazione di SO (Sistema Operativo). Il programmatore deve considerare il codice macchina che la sua applicazione genera.

<<Se il vostro S/W ignora l’H/W, il vostro H/W ignorerà il S/W>>.

MAPPATURA DELLA MEMORIA

Un’applicazione crea e usa quattro aree di memoria distinte. 1. Programma: è memorizzato il codice eseguibile. 2. Varibili globali. 3. Heap: è una regione di memoria libera che può essere richiesta dall’applicazione tramite

le funzioni di allocazione dinamica. 4. Stack: memorizza: l’indirizzo di ritorno delle funzioni, i parametri passati alle funzioni, le

variabili locali e lo stato della CPU.

Page 4: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 3 di 299

Struttura di un’applicazione Tutte le parole chiave sono minuscole. Tutte le applicazioni sono costituite da una o più funzioni. L’unica che dev’essere sempre presente è il main perché è la prima cui è ceduto il controllo quando inizia l’esecuzione di un’applicazione. È buona regola di programmazione usare main per chiamare altre funzioni e non per eseguire elaborazioni. /* direttive del preprocessore*/ #include <stdio.h> #define N 10 /* dichiarazione dei prototipi di funzione: dichiarazione in anticipo della funzione che si utilizzerà con il tipo dei parametri e il tipo della variabile da restituire: questo permette un forte controllo di tipo. */ int stampa1 (int n); /* dichiarazione di tipi */ typedef int vettore[N]; /* dichiarazioni di variabili globali*/ int i; /* main program: definire sempre che il main non possiede parametri */ int main(void) { dichiarazioni; istruzioni; return(0); } /* definizioni di funzioni */ int stampa1 (int n) { printf ("%d ",n); return (0); } Direttiva #include #include <stdio.h> Chiede al compilatore d’includere un determinato file all’interno del file che contiene questa direttiva. Per esempio, le funzioni di libreria del C lavorano con i loro specifici tipi di dati e con le variabili alle quali l’applicazione deve poter accedere. Questi tipi di dati e variabili sono definiti nei file d’intestazione o file header (*.h), forniti insieme al compilatore che vanno inseriti all’inizio di ogni applicazione che li richiede; in questo modo il programmatore leggendo il file header può individuare le dichiarazioni di variabili e funzioni che permettono l’utilizzo di quella libreria pur non conoscendone l’implementazione. Racchiudendo la direttiva (< >) il file sarà cercato nella cartella predefinita. Racchiudendo la direttiva (“ ”) il file sarà cercato prima nella cartella corrente. I file d’intestazione sono di tipo testo e si trovano nella cartella \INCLUDE. È permesso che file inclusi contengano a loro volta le direttive #include, in questo caso si parla d’include annidate.

Page 5: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 4 di 299

Direttiva #undef Elimina la definizione di un nome di macro che è stato precedentemente definito con la direttiva #define. L’utilizzo è quello di limitare le definizioni dei nomi di macro all’interno di porzioni di codice.

SISTEMA D’I/O Il C possiede due tipi di funzioni d’I/O. 1. Presente sia nello standard ANSI sia nello Unix: sistema a file bufferizzato, noto anche

come ad alto livello oppure formattato <stdio.h>. 2. Presente solo nello standard Unix: sistema a file non bufferizzati, oppure non formattato

o ancora Unix-like <io.h>. Il sistema d’I/O del C fornisce un’interfaccia astratta che è indipendente dal particolare dispositivo fisico impiegato. Il livello di astrazione è chiamato stream, canale, mentre il dispositivo fisico è detto file. All’inizio dell’applicazione sono aperti automaticamente cinque stream standard d’I/O. Nome Stream I/O Modo Dispositivo stdin standard input input testo tastiera (CON) stdout standard output output testo monitor (CON) stderr standard error output testo monitor (CON) stdprn standard printer I/O binario stampante (LPT1) stdaux standard auxiliary output binario seriale (COM1) Il marcatore di end of file varia a seconda del SO. Le funzioni di libreria del C fanno sembrare che ogni riga termini con un singolo carattere newline, indipendentemente dal fatto che il file d’input contenga un carattere CR (Carriage Return), un carattere LF (Line Feed) o entrambi. Le funzioni di libreria d’input restituiscono un unico carattere newline. Le funzioni di libreria di output eseguono la traduzione inversa. Se un’applicazione chiama una funzione di libreria per scrivere il carattere newline, la funzione tradurrà il carattere nel marcatore end of file appropriato. Questo approccio rende le applicazioni C portabili. I/O formattato Il termine formattato si riferisce al fatto che le operazioni svolte dalle funzioni avvengono sempre con un formato che rimane sotto il controllo del programmatore. Le funzioni di libreria utilizzano delle stringhe di formato per controllare l’I/O, possiedono l’abilità di convertire i dati dalla forma testuale a quella numerica durante l’input e dalla forma numerica a quella testuale durante l’output. Nessuna delle altre funzioni d’I/O è in grado di effettuare questo tipo di conversioni. printf La funzione printf è progettata per visualizzare il contenuto di una stringa, assieme a valori inseriti in specifici punti della stringa stessa. Scrive un numero variabile di dati nello stream di output utilizzando una stringa di formato che controlla il modo di presentarsi dell’output e ritorna il numero di caratteri scritti, un valore negativo indica che si è verificato un errore. La funzione possiede un numero variabile di parametri. int printf (const char *format [, argument]... ); La stringa di formato contiene normali caratteri e/o delle specifiche di conversione.

Page 6: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 5 di 299

I normali caratteri sono stampati così come sono. Le specifiche di conversione descrivono come i restanti parametri debbano essere convertiti nella forma testuale. Una specifica di conversione è costituita dal simbolo (%) seguito fino da cinque oggetti distinti.

(%) è un segnaposto che indica dove dev’essere inserito durante la stampa il valore da stampare. Flag Opzionale, ne è permesso più di uno. (-) allinea a sinistra all’interno del campo, per default l’allineamento è a destra. (+) i numeri prodotti dalle conversioni con segno iniziano sempre con (+) o (-). Blank: i numeri non negativi prodotti dalle conversioni con segno sono preceduti da un blank, (+) annulla il blank. (#) i numeri ottali iniziano con (o), i numeri esadecimali diversi da zero iniziano con (0x) o (0X), i numeri reali hanno sempre il punto decimale, gli zeri trascinati non sono sempre rimossi dai numeri stampati con le conversioni (g) e (G). Zero: i numeri sono espansi con degli zero fino a riempire tutta la larghezza del campo, lo zero è ignorato se lo specificatore di conversione è uno tra (d), (i), (o), (u), (x) o (X) e se è stata specificata la precisione, (-) annulla l’effetto di zero. Lunghezza minima del campo Opzionale, un oggetto che fosse troppo piccolo per occupare questo numero di caratteri sarebbe espanso, per default sono aggiunti dei blank alla sinistra dell’oggetto, allineandolo a destra all’interno del campo. Un oggetto che fosse troppo grande per la larghezza del campo sarebbe visualizzato comunque nella sua interezza. La larghezza del campo può essere sia un intero sia il carattere (*), in questo caso la larghezza del campo è ottenuta dall’argomento successivo. Precisione Opzionale, dipende dalla conversione usata. (d), (i), (o), (u), (x), (X): numero minimo di cifre, degli zeri sono aggiunti nel caso in cui il numero avesse meno cifre. (a), (A), (e), (E), (s), (F): numero di cifre dopo il punto decimale. (g), (G): numero di cifre significative. s: numero massimo di byte. La precisione è costituita da un (.) seguito da un intero o dal carattere (*), in questo caso la precisione è ottenuta dall’argomento successivo. Se è presente solo il (.) la precisione è pari a zero. Modificatore della lunghezza Opzionale, indica che l’oggetto che dev’essere visualizzato è di un tipo che è più lungo o più corto del normale rispetto alla particolare specifica di conversione in uso. Per esempio, %d si riferisce ad un valore int, %hd è utilizzato per visualizzare uno short int e &ld è usato per i long int.

Page 7: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 6 di 299

Specificatore di conversione (d), (i) converte un valore int nel formato decimale. (o), (u), (x), (X) converte un valore unsigned int in base 8 (o), base 10 (u) o in base 16 (x) visualizza le cifre esadecimali (a – f) come lettere minuscole, (X) le visualizza come lettere maiuscole. (f) converte un valore double nella forma decimale mettendo il punto decimale nella posizione corretta, se non è specificata alcuna precisione, visualizza 6 cifre dopo il punto decimale. (e), (E) converte un valore double nella notazione scientifica, se non è specificata alcuna precisione, visualizza 6 cifre dopo il punto decimale; se è scelto (e) l’esponente è preceduto dalla lettera (e); se è scelto (E) l’esponente è preceduto dalla lettera (E). (g) converte un valore double o nel formato (f) o (e) se l’esponente è minore di (- 4) oppure è maggiore o uguale alla precisione, gli zeri seguenti non sono visualizzati a meno che non sia usato (#); il punto decimale è presente solo quando è seguito da una cifra; (G) sceglie tra i formati (F) e (E). (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura quando è raggiunto il numero di byte specificati dalla precisione, se presente o quando è incontrato il carattere blank o TAB, una newline o NULL. Un blank dopo il (%) lascia uno spazio davanti ai numeri positivi, mentre non altera il meno davanti ai negativi. Il compilatore non controlla se il numero di specifiche di conversione presenti in una stringa di formato corrisponda al numero di oggetti da stampare. (/n) newline avanza automaticamente alla linea successiva dell’output quando termina la stampa. Esempio, stampare i primi tre caratteri della stringa indicata, allineati a sinistra in un campo di 5 spazi, i due asterischi sono aggiunti per vedere i limiti del campo. printf("*%-5.3s*","ciclista"); /* stampa *cic * */ Esempio, i due asterischi non fanno parte della specifica di stampa ma sono normali caratteri aggiunti per evidenziare inizio e fine della stampa del numero. #include <stdio.h> #include <stdlib.h> int main(void) { int x = 777; system("cls"); printf("\tAllineamenti di numeri con printf:\n\n"); printf("Stampa normalmente:\t\t\t*%d*\n\n",x); printf("Allinea a destra in campo largo 8:\t*%8d*\n",x); printf("Allinea a sinistra in campo largo 8:\t*%-8d*\n\n",x); printf("Stampa in campo troppo stretto:\t\t*%2d*\n",x); printf("Stampa in campo largo 6, con zeri:\t*%06d*\n",x); printf("Stampa un minimo di 6 cifre:\t\t*%.6d*\n\n",x); printf("Stampa un numero negativo:\t\t*%d*\n\n",-x); printf("Segno su numero positivo:\t\t*%+d*\n",x); printf("Segno su numero negativo:\t\t*%+d*\n\n",-x); printf("Spazio su numero positivo:\t\t*% d*\n",x); printf("Spazio su numero negativo:\t\t*% d*\n",-x);

Page 8: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 7 di 299

system("pause");return (0); }

scanf La funzione scanf è progettata per leggere dei dati dallo stream d’input utilizzando una stringa di formato per indicare la disposizione dell’input stesso. Dopo la stringa di formato segue un numero qualsiasi di puntatori, ognuno punta ad un oggetto. Gli oggetti d’input sono convertiti in accordo con le specifiche di conversione presenti nella stringa di formato e salvati in questi oggetti. Termina prematuramente se non possono essere letti altri caratteri d’input oppure i caratteri non si adattano alla stringa di formato. int scanf (const char *format [, argument]... ); La stringa di formato rappresenta un pattern che la funzione cerca di far combaciare con l’input che legge, contiene tre cose. 1. Specifiche di conversione: salta i caratteri blank presenti all’inizio di un oggetto d’input

ma mai quelli che seguono. 2. Caratteri blank: uno o più caratteri blank consecutivi presenti all’interno di una stringa di

formato si accoppiano con zero o più caratteri blank presenti nello stream d’input; un carattere di spaziatura fa in modo che dalla stringa proveniente da stdin siano letti, senza essere memorizzati, un certo numero di caratteri di spaziatura, anche nessuno, fino al successivo carattere che non sia di spaziatura, sono caratteri di spaziatura il blank, l’INVIO e la tabulazione; quando nella stringa è presente un carattere diverso da quelli di spaziatura è letto e scartato, %d,%d si scarta la virgola.

3. Caratteri non blank: un carattere che non sia blank, eccetto (%), si accoppia nello stream d’input con lo stesso carattere.

Una specifica di conversione consiste nel carattere (%) seguito dagli elementi elencati di seguito. * Opzionale, un oggetto d’input è letto ma non assegnato, non è incluso nel conto dei caratteri letti. Larghezza massima del campo Opzionale, mette un limite al numero di caratteri presenti in un oggetto d’input, la conversione termina se questo numero è raggiunto, i blank saltati all’inizio della conversione non contano.

Page 9: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 8 di 299

Modificatore della lunghezza Opzionale, indica che l’oggetto nel quale sarà salvato l’input è di un tipo che è più grande o più piccolo del normale per una particolare specifica di conversione. Specificatori di conversione (d) un intero in base 10, tipo int. (i) un intero, tipo int, se inizia con zero è ottale, con (x, X) esadecimale. (0) un intero ottale, tipo unsigned int. (u) un intero in base 10, tipo unsigned int. (x, X) un intero esadecimale, tipo unsigned int. (e, E), (f), (g), (G) un float. (c) si combina con n caratteri, dove n è la larghezza massima del campo oppure un carattere se la larghezza del campo non è stata specificata, non aggiunge il carattere NULL al termine. (s) si combina con una sequenza di caratteri non rappresentanti blank e aggiunge il carattere NULL al termine. Le variabili usate per ricevere i parametri letti da scanf devono essere passate per indirizzo (&), in pratica sono puntatori alle vere variabili. L’asterisco posto tra (%) e il carattere di conversione fa in modo che sia letto il dato corrispondente ma senza essere memorizzato, quando è chiamata la scanf inizia a elaborare le informazioni presenti nella stringa partendo dalla sinistra. Esempio, salta i blank che precedono l’inizio della stringa e si ferma a qualsiasi carattere che rappresenti un blank. #include <stdio.h> #include <stdlib.h> #define N 80 int main(void) { char str[N+1]=""; system("cls"); printf ("Inserisci la frase: "); scanf ("%s",str); printf("\n%s\n",str); system("pause");return (0); }

La scanf non salta i blank, se il successivo carattere non letto è un blank, allora la variabile di lettura conterrà un blank dopo il ritorno della funzione scanf. #include <stdio.h> #include <stdlib.h> int main(void) { char a,b,c; system("cls"); printf ("Inserisci tre caratteri: "); scanf ("%c%c%c",&a,&b,&c); printf("Sono stati inseriti: a=%c b=%c c=%c\n",a,b,c);

Page 10: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 9 di 299

system("pause");return (0); }

Per forzare la scanf a saltare i blank prima della lettura di un carattere si deve mettere un blank all’interno della stringa di formato. #include <stdio.h> #include <stdlib.h> int main(void) { char a,b,c; system("cls"); printf ("Inserisci tre caratteri: "); scanf ("%c %c %c",&a,&b,&c); printf("Sono stati inseriti: a=%c b=%c c=%c\n",a,b,c); system("pause");return (0); }

Dato che la scanf di norma non salta i blank, è facile trovare la fine di una riga d’input: è sufficiente controllare se il carattere appena letto è newline. do { scanf ("%c",&c); } while (c != '\n'); Esempio, lettura di una frazione immessa dall’utente, somma di frazioni. #include <stdio.h> #include <stdlib.h> int main(void) { int n1=0,d1=0,n2=0,d2=0,rn=0,rd=0; system("cls"); printf ("Inserisci la prima frazione:\t"); scanf ("%d%/%d",&n1,&d1); printf ("Inserisci la seconda frazione:\t"); scanf ("%d%/%d",&n2,&d2); rn=n1*d2+n2*d1; rd=d1*d2; printf("La somma vale: %d/%d\n",rn,rd); system("pause");return (0); }

Page 11: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 10 di 299

Esempio, saltare tutti i caratteri fino al primo carattere newline. scanf ("%*[^\n]"); I/O di caratteri Leggono e scrivono singoli caratteri come valori di tipo int e non char. Funzioni di output La funzione putchar scrive un carattere sullo stream stdout. Le funzioni putc e fputc scrivono un carattere in uno stream qualsiasi. Se si verifica un errore in scrittura, tutte e tre le funzioni, impostano l’indicatore di errore dello stream e restituiscono EOF altrimenti restituiscono il carattere che è stato letto. int putchar (int c); int putc(int c, FILE *stream); int fputc (int c, FILE *stream ); Funzioni d’input La funzione getchar legge un carattere dallo stream stdin. Le funzioni fgetc e getc leggono un carattere da uno stream qualsiasi. Tutte e tre le funzioni trattano il carattere come un unsigned char che è poi convertito in int prima di essere restituito. Indicano una condizione di fine file o di errore per mezzo del valore restituito EOF che è una costante intera negativa. int getchar(void); int getc (FILE *stream ); int fgetc (FILE *stream ); int ungetc (int c, FILE *stream ); La funzione ungetc rimette a posto un carattere letto dallo stream e azzera EOF, utile perché legge il carattere successivo. Esempio, leggere una serie di cifre e fermarsi al primo carattere che non sia una cifra. while (isdigit (ch=getc(fp))) { ..... } ungetc (ch,fp); /* rimette a posto l'ultimo carattere letto */ Utilizzare getchar e putchar, invece di scanf e printf, permette di risparmiare tempo durante l’esecuzione dell’applicazione. Le due funzioni sono veloci per due motivi. 1. Sono più semplici rispetto a scanf e printf. 2. Sono implementate come macro. I/O di righe Leggono e scrivono righe, usate con gli stream di testo. Funzioni di output La funzione puts scrive una stringa sullo stream stdout, dopo aver scritto aggiunge sempre il carattere newline. La funzione fputs scrive una stringa in uno stream qualsiasi, dopo aver scritto non aggiunge il carattere newline.

Page 12: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 11 di 299

Entrambe restituiscono EOF nel caso si verificasse un errore di scrittura, negli altri casi restituiscono un valore non negativo. int puts (const char *str ); int fputs ( const char *str, FILE *stream ); Funzioni d’input La funzione gets legge una riga d’input dallo stream stdin, legge i caratteri uno alla volta e li salva nell’array, fino a quando non legge il carattere newline che è scartato, inserisce il carattere NULL che funge da terminatore per la stringa. La funzione fgets legge una stringa da uno stream qualsiasi, pone un limite al numero di caratteri da leggere. Restituiscono un puntatore nullo nel caso si verificasse un errore in lettura o nel caso incontrassero la fine dello stream d’input prima di aver salvato qualsiasi carattere, in pratica se l’utente batte CTRL-Z o il tasto funzione F6. Non è possibile sapere quanti caratteri l’utente scriverà prima di premere INVIO, per evitare di uscire dalla stringa, si deve dimensionare l’array per il massimo numero possibile di caratteri, per esempio nei PC la gets non può leggere oltre 127 caratteri, più (\0) finale. char *gets ( char *buffer ); char *fgets ( char *str, int n, FILE *stream ); Esempio. #include <stdio.h> #include <stdlib.h> #define N 80 int main(void) { char str[N+1]=""; system("cls"); printf ("Inserisci la frase: "); gets (str); printf("%s\n",str); system("pause");return (0); }

Modificare il ciclo scanf per saltare la parte rimanente di una riga. while (getchar() != '\n'); Esempio, leggere la stringa dalla tastiera e stampare la sua lunghezza. #include <stdio.h> #include <stdlib.h> #include <string.h> #define N 10 int main(void) { char s[N]; system("cls"); printf ("Inserisci la frase: ");

Page 13: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 12 di 299

gets (s); printf ("La lunghezza della stringa e' %d\n",strlen(s)); system("pause");return (0); }

I/O di stringhe Leggono e scrivono dati utilizzando una stringa come se fosse uno stream. Funzioni di output La funzione sprintf scrive il suo output in un array di caratteri invece che in uno stream. Quando ha terminato la scrittura, aggiunge il carattere NULL e restituisce il numero di caratteri salvati, senza contare il carattere NULL. Se si verifica un errore ritorna un valore negativo. La funzione _snprintf è uguale a sprintf ma possiede il parametro in più n, nella stringa non saranno scritti più di (n – 1) caratteri, senza contare il carattere NULL che è sempre scritto. int sprintf (char *buffer, const char *format [, argument] ... ); int _snprintf (char *buffer, size_t count, const char *format [, argument] ... ); int sprintf_s (char *buffer, size_t sizeOfBuffer, const char *format [, argument] ... ); Esempio. #include <stdio.h> #include <stdlib.h> int main( void ) { char buffer[200], s[] = "PC", c = 'M'; int i = 35, j=0; float fp = 1.7320534f; system("cls"); j = sprintf_s( buffer, 200, " Stringa:\t%s\n", s ); j += sprintf_s( buffer + j, 200 - j, " Carattere:\t%c\n", c ); j += sprintf_s( buffer + j, 200 - j, " Integer:\t%d\n", i ); j += sprintf_s( buffer + j, 200 - j, " Real:\t\t%f\n", fp ); printf_s( "Output:\n%s\nNumero caratteri = %d\n", buffer, j ); system("pause");return(0); }

Le funzioni seguenti. int vfprintf(FILE *stream, const char *format, va_list argptr ); int vprintf(const char *format,va_list argptr ); int vsnprintf(char *buffer, size_t count, const char *format, va_list argptr ); int vsprintf(char *buffer,const char *format,va_list argptr );

Page 14: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 13 di 299

Ognuna di queste funzioni ha come ultimo argomento un valore va_list, quindi hanno un numero variabile di parametri. In pratica, queste funzioni sono usate per scrivere funzioni wrapper, involucro che accettino un numero variabile di parametri che sono passati alla funzione. Funzioni d’input La funzione sscanf legge da una stringa invece che da uno stream, consente quindi di semplificare le operazioni d’input, evitando tutti i problemi della scanf. Ritorna il numero di dati letti, EOF se raggiunge la fine della stringa terminata con il carattere NULL prima di trovare il primo dato. int sscanf (const char *buffer, const char *format [, argument ] ... ); È utile per estrarre dati da una stringa che è stata letta con un’altra funzione d’input. fgets (str,sizeof(str),stdin); /* legge una riga d'input */ sscanf (str,"%d%d",&a,&b) /* estrae due interi */ È possibile esaminare una riga d’input tutte le volte che è necessario e non solamente una. #include <stdio.h> #include <stdlib.h> int main(void) { int giorno=0, mese=0,anno=0; char str[]="31/12/2011" ; system("cls"); if (sscanf (str,"%d /%d /%d",&giorno, &mese, &anno)==3) printf ("Giorno: %d, mese: %d, anno: %d\n",giorno, mese,anno); else if (sscanf (str,"%d -%d -%d",&giorno, &mese, &anno)==3) printf ("Giorno: %d, mese: %d, anno: %d\n",giorno, mese,anno); else printf ("Data non corretta!"); system("pause");return(0); }

Esempio, la scanf ignora l’INVIO e lascia caratteri nel buffer di linea se non trova esattamente ciò che cerca, basta, infatti, eseguire un semplice input di un’intera linea con la gets e poi analizzare questa linea con una sscanf. #include <stdio.h> #include <stdlib.h> #define N 80 /* massimo numero di caratteri letti*/ int main(void) { char str[N+1]; /* array di caratteri dove si salva l'input*/ int c=0, i=0; system("cls"); printf ("Inserisci gli incassi, le spese: "); gets (str);

Page 15: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 14 di 299

sscanf(str,"%d,%d",&c,&i); printf("\n%d %d\n",c,i); system("pause");return (0); }

La sscanf legge dall’array str[] e deve trovare due numeri interi separati da una virgola, i rispettivi valori sono scritti nelle variabili c e i. La correttezza della lettura si verifica esaminando il valore ritornato da sscanf, anche in caso di errore, l’utente ha introdotto esattamente una linea e non ci sono problemi di caratteri avanzati nel buffer di linea. La sscanf è una completa e versatile funzione d’input ma è anche alquanto ingombrante e relativamente lenta. Filtrare i dati in input È indispensabile impedire che l’utente alla richiesta, per esempio, di un numero possa digitare una lettera. int: caratteri validi 0 .. 9. float, double: caratteri validi 0 .. 9 e il “.”. char: caratteri validi A .. Z, a .. z e ( , ; . :). Sequenze di escape Alcuni caratteri speciali, per esempio newline, non possono essere scritti come caratteri tra singoli apici perché non sono stampabili. Per poter utilizzare questi caratteri, il C mette a disposizione una notazione speciale: le sequenze di escape che sono di due tipi. 1. I caratteri di escape. 2. Gli escape numerici: valori decimali, ottali ed esadecimali. Quando il carattere backslash (\) precede un altro carattere, quest’ultimo è interpretato come carattere speciale. \a alert (bell), produce un beep 0x07 \b backspace, una battuta indietro 0x08 \f form feed, pagina nuova 0x0C \n newline, ritorno a capo su una nuova linea 0x0A \r carriage return, ritorno carrello, porta il cursore all’inizio della riga 0x0D \t tab, tabulazione orizzontale 0x09 \v tabulazione verticale 0x0B \\ backslash, barra rovesciata 0x5C \” apici doppi \’ apice singolo, 0x27 \0 NULL, fine stringa \? punto interrogativo Quando l’utente immette l’input e preme INVIO, l’applicazione dovrebbe leggere due caratteri: CR e LF. Per eredità da Unix, il C considera sempre la fine di una riga come delimitata da un singolo carattere di LF. La libreria del C traduce l’INVIO in un carattere LF.

Page 16: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 15 di 299

Conversione di tipo Il C usa solo variabili che siano state precedentemente dichiarate per due motivi. 1. Ottimizzare l’uso della memoria. 2. Aumentare l’affidabilità. Il tipo di una variabile è statico, in altre parole non può essere modificato in esecuzione. Il tipo è assegnato automaticamente in base al valore assegnato alle variabili. Si riferisce alla situazione in cui sono impiegate variabili di tipi differenti all’interno di un’unica espressione. Quando in un’espressione sono utilizzati tipi di dati differenti, il compilatore li converte tutti nello stesso tipo e in particolare nel tipo che occupa più memoria, operazione per operazione come descritto dalle due regole seguenti. 1. È promosso l’operando con il tipo più piccolo, per esempio tutti i float sono convertiti in

double, i char e gli short int in int. 2. Per tutte le coppie di operandi si applica la seguente sequenza: per esempio se un

operando è double, l’altro operando è convertito in double. Dopo che il compilatore ha applicato questa regola, ogni coppia di operandi risulta dello stesso tipo che sarà anche il tipo del risultato. La possibilità di convertire il tipo di una variabile in un altro tipo: si chiama casting. In C la conversione di tipo può avvenire in due modi. 1. Conversione implicita Sono quelle che avvengono senza il rischio di perdita d’informazioni. In un’espressione che usa diversi tipi interi il risultato sarà convertito implicitamente, in altre parole trasparente, dal compilatore. char c; short int s;int i;unsigned int u; long int l; unsigned long int ul; float f;double d; long double ld; i +=c; /* c è convertito in int */ i += s; /* s è convertito in int */ u +=i; /* i è convertito in unsigned int */ l +=u; /* u è convertito in long int */ ul +=l; /* l è convertito in unsigned long int */ f +=ul; /* ul è convertito in float */ d +=f; /* f è convertito in double */ ld +=d; /* d è convertito in long double */ Le conversioni aritmetiche non si applicano alle assegnazioni, il C segue la regola di convertire l’espressione presente nel lato destro dell’assegnazione, nel tipo di variabile presente nel lato sinistro. char c; int i; float f; double d; i =c; /* c è convertito in int */ f =i; /* i è convertito in float */ d =f; /* f è convertito in double */ Gli altri casi sono problematici. #include <stdio.h> #include <stdlib.h>

Page 17: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 16 di 299

int main(void) { int y=0; float x=7.7; system("cls"); y=x; printf ("%d\n",y); system("pause");return(0); }

2. Conversione esplicita Sono quelle che avvengono con perdita d’informazioni perché il tipo di destinazione non è in grado di rappresentare l’intero dominio dei valori del tipo di origine. Il programmatore deve specificare tramite l’operatore di casting l’intenzione di voler compiere una conversione di tipo. Si esegue facendo precedere l’espressione di cui si vuole convertire il valore, dal tipo di destinazione racchiuso tra parentesi tonde. Un’espressione di cast ha la forma seguente. (nome_del_tipo) espressione Dove (nome_del_tipo) specifica il tipo nel quale sarà convertita l’espressione. #include <stdio.h> #include <stdlib.h> int main(void) { int y=0; float x=7.7; double z=0.0; system("cls"); y= (int) x; z=(double)x; printf ("%d\t%f\n",y,z); system("pause");return(0); }

#include <stdio.h> #include <stdlib.h> int main(void) { int a=20, b=7; float c=0.0, d=0.0; system("cls"); c = a/b; d = (float) a/b; printf("a\t\t= %d\n",a); printf("b\t\t= %d\n",b); printf("a/b\t\t= %f\n",c); printf("a/b\t\t= %f\n",d);

Page 18: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 17 di 299

printf("(float) a/b\t= %f\n",d); system("pause");return (0); }

SPECIFICATORI DELLA CLASSE DI MEMORIZZAZIONE Ci sono quattro classi di memorizzazione, in una dichiarazione può comparire al massimo una classe di memorizzazione e, se presente, deve comparire come primo specificatore. Lo scopo è quello di comunicare al compilatore come si desidera che siano mantenute in memoria le variabili. auto Sono memorizzate nello stack, tutte le variabili locali senza alcun specificatore per default sono auto(matic); appartengono a questa classe le variabili locali, se non sono inizializzate in dichiarazione o all’interno del blocco dove sono definite, sono indefinite. extern Nella programmazione modulare esiste il problema di far conoscere a tutti i moduli che compongono il progetto la presenza di variabili globali, questo compito è delegato al linker. Un inconveniente sorge nel caso in cui si dichiara in ogni modulo tutte le variabili globali dell’intera applicazione, in quanto ci sarebbero uno o più casi di omonimia. Il compilatore non è in grado di segnalare la presenza di questo tipo di errore perché ogni modulo è compilato separatamente dagli altri. Questo problema lo incontra il linker perché non sa quale delle variabili omonime dev’essere utilizzata. La soluzione consiste nel dichiarare tutte le varibili in uno solo dei moduli del progetto e nel farle precedere dalla parola chiave extern nei moduli rimanenti, all’inizio sono inizializzate a zero. extern int x, y; static Può essere utilizzata con tutte le variabili, indipendentemente da dove queste siano state dichiarate, all’inizio sono inizializzate a zero. Aiuta ad implementare la tecnica information hiding, in altre parole nascondere deliberatamente alcune informazioni. Una variabile statica dichiarata dentro un blocco risiede nella stessa locazione di memoria durante tutta l’esecuzione dell’applicazione. Le variabili statiche possiedono le seguenti proprietà. Una variabile statica presente in un blocco è inizializzata solamente una volta, prima

dell’esecuzione dell’applicazione. Una variabile statica mantiene il suo valore indefinitamente. Ogni volta che una funzione è chiamata ricorsivamente ottiene un nuovo insieme di

variabili auto ma se possiede una variabile static, questa è condivisa da tutte le chiamate alla funzione.

register Applicata solo ai tipi int e char, dichiarati come variabili locali o PF, le variabili sono

Page 19: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 18 di 299

mantenute all’interno di uno dei registri della CPU, anziché in una cella di memoria, usata nelle variabili di controllo dei cicli. Il compilatore C lo fa automaticamente ma memorizza solo due variabili register perché il numero di registri della CPU è limitato. Funzioni Le dichiarazioni e le definizioni delle funzioni, come le dichiarazioni delle variabili, possono includere una classe di memorizzazione ma le uniche opzioni disponibili sono extern e static.

Preprocessore Il preprocesore obbedisce ai comandi, direttive che iniziano con (#), questo processo è automatizzato perché il preprocessore è integrato nel compilatore. Le direttive terminano sempre con newline, non con il carattere punto e virgola; possono trovarsi in qualsiasi punto dell’applicazione ma è buona regola di programmazione metterle all’inizio. Non sono tradotte in codice macchina ma espandono l’ambiente di programmazione. # /* direttiva nulla */ Per esempio, #define assegna dei nomi alle costanti in questo modo si rendono le applicazioni più leggibili e modificabili facilmente. Per esempio, la direttiva #include dice al preprocessore di aprire un file e d’includere il suo contenuto come parte del file che dev’essere compilato. Quando un progetto include molti header, l’ordine con cui si scrivono le #include non ha alcuna importanza.

L’input del preprocessore è un’applicazione C che contiene direttive. Durante il processo il preprocessore esegue le direttive, l’output del preprocessore è un’altra applicazione C senza le direttive. L’output del preprocessore va in input al compilatore che lo traduce in codice oggetto. Macro predefinite Ogni macro rappresenta una costante intera o stringa. #include <stdio.h> #include <stdlib.h> int main (void) { system("cls"); printf ("FILE: %s\nLINEA: %d\nDATA: %s\nORA: \%s\n",

Page 20: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 19 di 299

__FILE__,__LINE__,__DATE__,__TIME__); system("pause");return(0); }

Compilazione condizionale È una tecnica che permette di fornire al compilatore solo le parti di un’applicazione che devono essere compilate. #if <expression1> righe che devono essere incluse se expression1 è vera(1) #else righe che devono essere incluse se expression1 è falsa (0) #endif #if <expression1> #elif <expression2> righe che devono essere incluse se expression1 è falsa(0) ma expression2 è diversa da 0 #endif Utile nella fase di debugging di un’applicazione. #include <stdio.h> #include <stdlib.h> #define DEBUG 1 int main (void) { system("cls"); #if DEBUG /* l'alternativa è Release */ /* codice da eseguire solo in debug */ printf ("Valore di c: \n"); # else /* altro codice */ #endif system("pause");return(0); }

La #define definisce l’identificatore DEBUG. La #if controlla il valore di DEBUG, il suo valore è vero (1), allora il preprocessore lascia al suo posto la printf; se DEBUG è posto a falso (0), allora il preprocessore rimuove le tre righe di codice dall’applicazione. La #endif termina la direttiva condizionale. La #if tratta gli identificatori non definiti come delle macro con valore falso (0), quindi se non si definisce l’identificatore DEBUG il test fallisce. #include <stdio.h> #include <stdlib.h>

Page 21: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 20 di 299

#define MAX 100 int main (void) { system("cls"); #if MAX>99 printf ("Maggiore.\n"); #else printf ("Minore.\n"); #endif system("pause");return (0); }

Utile per scrivere applicazioni portabili su macchine o SO diversi. All’inizio dell’applicazione dev’essere definita una e una solo macro al fine di selezionare il tipo di SO. #if defined (WIN32) printf("Applicazioni IA32."); #elif defined (MAC_OS) printf("Applicazioni Apple."); #elif defined (LINUX) printf("Applicazioni Ubuntu."); #endif Direttiva #error #if MAX<100 #error Valore troppo piccolo #endif Operatore (#) converte i parametri di una macro in una stringa letterale. Operatore (##) copia insieme due identificatori in modo da formarne uno solo. #include <stdio.h> #include <stdlib.h> #define paster(n) printf_s( "token" #n " = %d\n", token##n ) int main(void) { int token9 = 9; system("cls"); paster(9); system("pause");return(0); }

Operatore (#@) precede il parametro formale nella definizione di macro. #define makechar(x) #@x

Page 22: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 21 di 299

Operatore defined Quando è applicato ad un identifiactore produce il valore vero (1) se l’identificatore è definito, falso (0) altrimenti. #if defined (DEBUG) printf ("Definito."); #endif

Librerie Permettono di radunare sottoprogrammi da utilizzare nella gestione di applicazioni complesse. Quando un’applicazione, composta da più moduli oggetto, è linkata, tutto il codice di ogni modulo è compattato per far parte di un unico eseguibile, anche quelle parti di codice che non saranno mai utilizzate. La libreria è costituita da un insieme di funzioni, per cui memorizza il nome, il codice oggetto, le informazioni di rilocazione di ogni funzione. Quando un’applicazione fa riferimento a una funzione contenuta in una libreria, il linker cerca questa funzione e ne aggiunge il codice all’eseguibile. Non è consentito scrivere una funzione che abbia lo stesso nome di una funzione della libreria standard anche quando non s’include l’header al quale appartiene la funzione. Libreria standard Gli header standard consistono di prototipi di funzioni, definizione di tipi e macro. Non si possono utilizzare i nomi di macro definite negli header standard. Gli identificatori che iniziano con il carattere (_) seguito da una lettera maiuscola o da un secondo carrattere (_) sono riservati alla libreria standard. Gestione errori <assert.h> La macro assert, usata come una funzione, permette ad un’applicazione di monitorare il suo comportamento e rilevare i possibili problemi. Ogni volta che è eseguita, controlla expression, se è vera (1), non fa nulla; se è falsa (0) scrive un messaggio su stderr e chiama la funzione abort per terminare l’esecuzione dell’applicazione. void assert(expression); Esempio, controllare l’indice dell’array. #include <stdio.h> #include <stdlib.h> #include <assert.h> int main(void) { int vet[10]; int i=15; system("cls"); assert (0<=i && i<10); vet[i]=0; system("pause");return(0); }

Page 23: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 22 di 299

Alcune funzioni della libreria standard salvano un codice di errore, un intero positivo, all’interno di errno, una variabile di tipo int che è dichiarata nell’header <errno.h>. Alcune volte il valore contenuto in errno è di fue tipi. 1. Errori di dominio EDOM (Domain error). 2. Errori d’intervallo ERANGE (Result too large). Esempio, controllare la chiamata alla funzione sqrt. #include <stdio.h> #include <math.h> #include <stdlib.h> #include <errno.h> int main(void) { double x = -16, y=0.0; system("cls"); y=sqrt(x); if (errno !=0) { printf ("sqrt Errore; applicazione terminata.\n"); system("pause"); exit (EXIT_FAILURE); } printf ("%f\n",y); system("pause");return(0); }

Per visualizzare un messaggio che indichi la natura dell’errore si usa la funzione seguente. void perror (const char *s); Esempio. #include <stdio.h> #include <math.h> #include <stdlib.h> #include <errno.h>

Page 24: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 23 di 299

int main(void) { double x = -16, y=0.0; y=sqrt(x); system("cls"); if (errno !=0) { perror ("sqrt Errore; applicazione terminata"); system("pause");exit (EXIT_FAILURE); } printf ("%f\n",y); system("pause");return(0); }

La funzione stampa nell’ordine i seguenti oggetti. 1. L’argomento. 2. Il carattere due punti. 3. Il carattere spazio. 4. Il tipo di errore determinato dal valore di errno. 5. Il carattere newline. Gestione segnali <signal.h> Gestione delle condizioni eccezionali chiamate segnali, si dividono in due categorie. 1. Errori di run-time: per esempio una divisione per zero. 2. Eventi causati al di fuori dell’applicazione: per esempio l’interruzione dell’applicazione

genera un segnale. La funzione signal installa una funzione per la gestione di un segnale in modo che possa essere utilizzata se in un secondo momento dovesse verificarsi il segnale stesso. Il primo parametro è il codice di un generico segnale, il secondo parametro è un puntatore ad una funzione per la gestione del segnale. void (*signal (int sig, void (*func)(int)))(int); Oltre a progettare un gestore, handler, per i segnali, si possono usare quelli predefiniti. #define SIG_DFL (void (__cdecl *)(int))0 /* default signal action */ #define SIG_IGN (void (__cdecl *)(int))1 /* ignore signal */ #define SIG_GET (void (__cdecl *)(int))2 /* return current value */ #define SIG_SGE (void (__cdecl *)(int))3 /* signal gets error */ #define SIG_ACK (void (__cdecl *)(int))4 /* acknowledge */ #define SIG_ERR (void (__cdecl *)(int))-1 /* signal error value */ Esempio. #include <stdio.h> #include <stdlib.h> #include <signal.h> void SignalHandler(int signal); int main(void) { system("cls"); if (signal(SIGINT,SignalHandler) ==SIG_ERR) perror ("signal(SIGINT),handler) fallito");

Page 25: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 24 di 299

system("pause");return(0); } void SignalHandler(int signal) { printf("Gestore segnali...\n");} Alcune volte per un’applicazione è utile provocare il verificarsi di un dato segnale. int raise (int sig); Esempio, testare i segnali. #include <stdio.h> #include <stdlib.h> #include <signal.h> void SignalHandler(int signal); void RaiseSig (void); int main(void) { void (*OrigHandler)(int); system("cls"); printf ("Installazione del gestore segnali per il segnale %d: \n",SIGINT); OrigHandler=signal(SIGINT,SignalHandler); RaiseSig(); printf ("Store gestore segnali per SIG_IGN\n"); signal(SIGINT,SIG_IGN); RaiseSig(); printf ("Restore gestore segnali originale\n"); signal(SIGINT,SignalHandler); RaiseSig(); system("pause");return(0); } void SignalHandler(int signal) { printf("Gestore segnali chiamato per il segnale: %d\n",signal);} void RaiseSig (void) {raise (SIGINT);}

Localizzazione <locale.h> Localizzazione è una nazione o area geografica nella quale è parlata una particolare lingua e include le seguenti caratteristiche. Formattazione dei numeri: LC_NUMERIC. Formattazione della moneta: LC_MONETARY. Il set di caratteri: ASCII (American Standard Code for Information Interchange), ASCII

esteso, Unicode. Aspetto della data e dell’ora: LC_TIME. La funzione seguente cambia la localizzazione corrente. char *setlocale(int category, const char *locale );

Page 26: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 25 di 299

Esempio. #include <stdio.h> #include <stdlib.h> #include <locale.h> int main(void) { system("cls"); setlocale( LC_ALL, "English" ); setlocale( LC_MONETARY, "French" ); setlocale( LC_ALL, NULL ); system("pause");return(0); } Ritorna informazioni sulla localizzazione corrente. struct lconv *localeconv( void ); Data e ora <time.h> Permette di determinare l’ora, includendo la data, eseguire aritmetica sulle ore e formattarle per la stampa. Le ore sono memorizzate nei tre modi seguenti. 1. clock_t: un valore orario misurato in tick di clock. 2. time_t: un formato compatto, calendar time, per la codifica dell’ora e della data. 3. struct tm: un valore orario che è stata diviso in secondi, minuti e ore, chiamato broken

down time. clock_t clock( void ); double difftime(time_t timer1, time_t timer0); time_t mktime(struct tm *timeptr); time_t time(time_t *timer); La funzione mktime converte un’ora dal formato broken down al formato calendar. La funzione clock restituisce un valore clock_t rappresentante il tempo di CPU usato dall’applicazione a partire dall’inizio dell’esecuzione. #include <stdio.h> #include <stdlib.h> #include <time.h> void sleep(clock_t wait); int main( void ) { system("cls"); clock_t inizio =clock(); sleep( (clock_t)3 * CLOCKS_PER_SEC ); printf( "Tempo usato dalla CPU: %g sec.\n", (clock () - inizio) / (double)CLOCKS_PER_SEC ); system("pause");return(0); } /* pausa per uno specifico numero di millisecondi */ void sleep( clock_t wait ) { clock_t goal; goal = wait + clock(); while( goal > clock() ); }

Page 27: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 26 di 299

Per convertire questo valore in secondi si deve dividerlo per CLOCKS_PER_SEC. Per determinare il tempo di esecuzione occorre chiamarla due volte: una all’inizio del main e una alla fine. La ragione della chiamata iniziale è che l’applicazione utilizzerà un po’ di tempo di CPU prima di raggiungere il main a causa del codice nascosto di start-up. La funzione difftime restituisce la differenza espressa in secondi tra due time, è usata per calcolare il tempo di esecuzione dell’applicazione, non il tempo di CPU. #include <stdio.h> #include <stdlib.h> #include <time.h> int main( void ) { time_t app, fine; double elapsed_time=0.0; system("cls"); /* inizio calcolo tempo di esecuzione dell'applicazione */ printf( "Moltiplico 2 numeri reali ...\n" ); time( &app); /* inizio calcolo tempo di CPU */ clock_t cpu =clock(); for(long loop = 0; loop < 500000000; loop++ ) double result = 3.63456789 * 5.274567123; printf( "Tempo usato dalla CPU:\t\t%6.0f secondi.\n", (clock () - cpu) / (double)CLOCKS_PER_SEC ); /* fine calcolo tempo di CPU */ time( &fine ); elapsed_time = difftime( fine, app ); printf( "Tempo usato dall'applicazione:\t%6.0f secondi.\n", elapsed_time ); /* fine calcolo tempo di esecuzione dell'applicazione */ system("pause");return(0); }

Esempio di funzione time. #include <stdio.h> #include <stdlib.h> #include <time.h> int main(void) { char tmpbuf[128], timebuf[26], ampm[] = "AM"; system("cls"); time_t ltime; _strtime_s( tmpbuf, 128 ); printf( "Ora del sistema operativo:\t%s\n", tmpbuf ); _strdate_s( tmpbuf, 128 ); printf( "Data del sistema operativo:\t%s\n", tmpbuf ); time( &ltime );

Page 28: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 27 di 299

/* Universal Time Coordinated */ printf( "Tempo in secondi UTC:\t\t%ld\n", ltime ); system("pause");return(0); }

Le funzioni seguenti convertono ore di tipo calendar in ore di tipo broken down. char *asctime(const struct tm *timeptr); char *ctime(const time_t *timer); struct tm *gmtime(const time_t *timer); struct tm *localtime(const time_t *timer); size_t strftime(char *strDest,size_t maxsize,const char *format,const struct tm *timeptr); ISO (International Standard Organization) 8061 È lo standard che descrive i modi per rappresentare la data, YYYY-MM-DD e l’ora, HH:MM:SS, sono numeriche e le ore espresse con le 24 ore. Esempio, visualizzare la data e l’ora. #include <stdio.h> #include <stdlib.h> #include <time.h> int main( void ) { time_t corrente = time(NULL); struct tm *ptr; char data_ora[21]; int ore=0; char a; system("cls"); /* stampa la data e l'ora nel formato di default */ puts (ctime(&corrente)); /* stampa la data e l'ora con la strftime */ strftime(data_ora,sizeof(data_ora),"%m-%d-%Y %I:%M %p\n",localtime(&corrente)); puts (data_ora); /* stampa la data e l'ora con la printf */ ptr=localtime(&corrente); ore=ptr->tm_hour; if (ore<=12) a ='a'; else { ore -=12; a = 'p'; } if (ore==0) ore=12; printf ("%.2d-%.2d-%d %2d:%.2d %c\n",ptr->tm_mon+1,ptr->tm_mday,ptr->tm_year+1900,ore,ptr->tm_min,a); system("pause");return(0); }

Page 29: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 28 di 299

FUNZIONI PER LA GENERAZIONE DI SEQUENZE PSEUDO CASUALI Utili nelle applicazioni di simulazione e nei giochi. La gestione si trova nell’header <stdlib.h> e fornisce due tipi di funzioni. int rand(void); void srand(unsigned int seed ); Ogni volta che è chiamata rand restituisce un numero compreso tra 0 e RAND_MAX, i numeri non sono veramente casuali ma sono generati a partire da un seme, seed. La funzione srand fornisce il seme per la rand, se la rand è chiamata prima della srand, il valore del seme è assunto uguale a 1, ogni seme determina una particolare sequenza di numeri pseudo casuali, la srand permette al programmatore di selezionare qualsiasi sequenza. È buona regola di programmazione rendere casuale il valore usato per il seme, per fare questo basta chiamare la funzione time che restituisce un numero che codifica la data e l’ora corrente. #include <stdio.h> #include <stdlib.h> #include <time.h> int main(void) { system("cls"); /* inizia sessione random */ srand(time(NULL)); /* numero compreso tra 1 e 10 */ unsigned int r = rand() % 10 + 1; printf("Il numero generato casualmente tra 1 e 10 vale: %d\n", r); system("pause");return (0); }

#include <stdio.h> #include <stdlib.h> #include <time.h> #define N 5 int main(void) { system("cls"); /* inizia sessione random */ srand(time(NULL)); for (int i = 0; i<N; i++) { /* numero compreso tra 1 e 10 */ unsigned int r = rand() % 10 + 1; printf("Il numero generato casualmente tra 1 e 10 vale: %d\n", r); } system("pause");return (0);

Page 30: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 29 di 299

}

Esempio, generazione di un numero casuale compreso tra 0 e 99. #include <stdio.h> #include <conio.h> #include <stdlib.h> int main(void) { unsigned int n=0; system("cls"); printf("Generazione di un numero casuale.\n\n"); printf ("Premere un tasto qualsiasi per generare.\n\n"); while (!kbhit()) n += 7; /* n è aumentata di 7 a ogni iterazione, quando si esce si prende il resto della divisione */ n %= 100; printf("Numero generato = %d\n\n", n); system("pause");return(0); }

Esempio. #include <stdio.h> #include <stdlib.h> #include <time.h> #define N 10 void SimpleRandDemo(void); void RangedRandDemo(int range_min, int range_max); int main(void) { system("cls"); srand((unsigned)time(NULL)); SimpleRandDemo(); printf("\n"); RangedRandDemo(1, 100); system("pause"); return(0); } /* stampa 10 numeri pseudo casuali */ void SimpleRandDemo(void) { for (int i = 0; i < N; ++i) printf("%6d\n", rand()); }

Page 31: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 30 di 299

/* genera 10 numeri nell'intervallo (range-min<= numeri pseudo casuali < range_max) */ void RangedRandDemo(int range_min, int range_max) { for (int i = 0; i < N; ++i) { double a = rand(); printf("a=rand %6f\n", a); double b = (RAND_MAX + 1); printf("b=RAND_MAX+1 %6f\n", b); double c = (range_max - range_min) + range_min; printf("c=(range_max - range_min) + range_min %6f\n", c); int d = (double)rand() / (RAND_MAX + 1) * ((range_max - range_min) + range_min); printf("%6d\n", d); int u = (double)rand() / (RAND_MAX + 1) * ((range_max - range_min) + range_min); printf("%6d\n", u); int e = (double)rand() / (RAND_MAX + 1) * ((range_max - range_min) + range_min); printf("%6d\n", e); } }

C OFFUSCATO Il linguaggio C può essere difficile da leggere. #include <stdio.h> #include <stdlib.h> int main(void) { int v=0,i=0,j=0,k=0,l=0,s=0,a[99]; system("cls"); for (int c=0;c<99;c++) a[c]=1; for (scanf ("%d",&s); *a-s; v=a[j*=v]-a[i],k=i<s,j+=(v=j<s&& (!k&&!!printf(2+"\n\n%c"-(!l<<!j),"Q"[l^v?(l^j)&1:2])&&

Page 32: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 31 di 299

++l||a[i]<<s&&v&&v-i+j&&v+i-j))&&!(l%=s),v||(i==j?a[i+=k]=0: ++a[i])>=s*k&&++a[--i]) ; printf ("\nTermina\n"); system("pause");return(0); }

Page 33: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 32 di 299

MODULO 2

MICROSOFT VISUAL C++ Microsoft Visual C Tools Visual Studio C++ Microsoft Visual C++ Microsoft Visual C++ CLR

Page 34: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 33 di 299

MICROSOFT VISUAL C

COMPILAZIONE CLI

C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC>cl /help Microsoft (R) C/C++ Optimizing Compiler versione 19.00.23026 per x64 Copyright (C) Microsoft Corporation. Tutti i diritti riservati. OPZIONI DEL COMPILATORE C/C++ -OTTIMIZZAZIONE- /O1 Riduce al minimo lo spazio /O2 Ottimizza la velocità /Ob<n> Espansione inline (n predefinito=0) /Od Disabilita le operazioni di ottimizzazione (predefinita) /Og Abilita l'ottimizzazione globale /Oi[-] Abilita le funzioni intrinseche /Os Ottimizza lo spazio del codice /Ot Ottimizza la velocità del codice /Ox Combina le opzioni di ottimizzazione /favor:<blend|AMD64|INTEL64|ATOM> seleziona il processore per cui effettuare l'o ttimizzazione. Scegliere una delle seguenti opzioni: blend Combinazione di ottimizzazioni per più processori x64 diversi AMD64 Processori AMD a 64 bit INTEL64 Processori con architettura Intel(R)64 ATOM - processori Intel(R) Atom(TM) -GENERAZIONE DEL CODICE- /GF Abilita la condivisione delle stringhe in sola lettura /Gm[-] Abilita la ricompilazione minima /Gy[-] Separa le funzioni per il linker /GS[-] Abilita i controlli di sicurezza /GR[-] Abilita RTTI di C++ /GX[-] Abilita EH (equivalente a /EHsc) di C++ /EHs Abilita EH (senza eccezioni SEH) di C++ /EHa Abilita EH con eccezioni SEH) di C++ /EHc Usa nothrow come impostazione predefinita di extern "C" /fp:<except[-]|fast|precise|strict> Scegliere il modello a virgola mobile: except[-] Considera le eccezioni di virgola mobile durante la generazione del codice fast Modello a virgola mobile "fast"; i risultati sono meno prevedibili precise Modello a virgola mobile "precise"; i risultati sono prevedibili strict Modello a virgola mobile "strict" (implica /fp:except) /Qfast_transcendentals Genera intrinseci FP inline anche con /fp:except /Qpar[-] enable parallel code generation /Qpar-report:1 auto-parallelizer diagnostic; indicate parallelized loops /Qpar-report:2 auto-parallelizer diagnostic; indicate loops not parallelized /Qvec-report:1 auto-vectorizer diagnostic; indicate vectorized loops /Qvec-report:2 auto-vectorizer diagnostic; indicate loops not vectorized /GL[-] enable link-time code generation /volatile:<iso|ms> choose volatile model: iso - Acquire/release semantics not guaranteed on volatile accesses ms - Acquire/release semantics guaranteed on volatile accesses /GA Ottimizza per l'applicazione Windows /Ge Impone il controllo dello stack per tutte le funzioni

Page 35: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 34 di 299

/Gs[num] Controlla le chiamate di verifica dello stack /Gh Abilita la chiamata di funzione _penter /GH Abilita la chiamata di funzione _pexit /GT Genera accessi TLS indipendenti da fiber /RTC1 Abilita i controlli rapidi (/RTCsu) /RTCc Converte in controlli di tipo più limitato /RTCs Verifica runtime dello stack frame /RTCu Controlli di utilizzo locale non inizializzati /clr[:opzione] Compila per Common Language Runtime, dove opzione è: pure Produce un file di output solo IL (senza codice eseguibile nativo) safe Produce un file di output verificabile solo IL oldSyntax Accetta la sintassi delle estensioni gestite di Visual C++ 2002/2003 initialAppDomain Abilita il comportamento AppDomain iniziale di Visual C++ 2002 noAssembly Non produce un assembly nostdlib - ignora la directory \clr predefinita /homeparams Impone la scrittura nello stack dei parametri passati nei registri /GZ Abilita i controlli dello stack (/RTCs) /arch:AVX Consente di utilizzare le istruzioni delle estensioni Intel(R) Advance d Vector Extension -FILE DI OUTPUT- /Fa[file] Specifica un file di listato dell'assembly /FA[scu] Configura il listato dell'assembly /Fd[file] Specifica un file PDB /Fe<file> Specifica un file eseguibile /Fm[file] Specifica un file map /Fo<file> Specifica un file oggetto /Fp<file> Specifica un file di intestazione precompilata /Fr[file] Specifica il file di origine del browser /FR[file] Specifica un file SBR esteso /Fi[file] Specifica un file pre-elaborato /doc[file] Elabora i commenti relativi alla documentazione XML ed eventualmente specifica il file XDC -PREPROCESSORE- /AI<dir> Specifica il percorso di ricerca dell'assembly /FU<file> Impone l'utilizzo di assembly/modulo /C Non rimuove i commenti /D<nome>{=|#}<testo> Definisce una macro /E Pre-elabora in stdout /EP Pre-elabora in, senza istruzione #line /P Pre-elabora nel file /Fx Esegue il merge del codice inserito al file /FI<file> Specifica il file di inclusione da utilizzare /U<nome> Rimuove la macro definita in precedenza /u Rimuove tutte le macro definite in precedenza /I<dir> Specifica il percorso di ricerca/X Ignora "posizioni standard" -LINGUAGGIO- /Zi Abilita le informazioni di debug /Z7 Abilita informazioni di debug obsolete /Zp[n] Comprime le strutture allineandole su un limite di n byte /Za Disabilita le estensioni /Ze Abilita le estensioni (predefinita) /Zl Omette il nome della libreria predefinita in OBJ /Zg Genera i prototipi delle funzioni /Zs Solo controllo della sintassi /vd{0|1|2} Abilita/disabilita vtordisp /vm<x> Tipo di puntatori ai membri /Zc:arg1[,arg2] Conformità al linguaggio C++, i cui argomenti possono essere: forScope[-] Impone C++ standard per le regole di ambito wchar_t[-] wchar_t è il tipo nativo, non un typedef auto[-] Impone il nuovo significato di C++ standard per auto

Page 36: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 35 di 299

trigraphs[-] Abilita i trigrammi (disattivato per impostazione predefinita) /ZW abilita le estensioni del linguaggio WinRT /openmp Abilita le estensioni del linguaggio OpenMP 2.0 -VARIE- @<file> Opzioni dei file di risposta /?, /help Visualizza questo argomento della Guida /bigobj Genera il formato di oggetto esteso /c Solo compilazione, nessun collegamento /errorReport:opzione Segnala a Microsoft gli errori interni del compilatore none Non invia la segnalazione prompt Richiede l'invio immediato della segnalazione queue Al successivo accesso di amministratore, richiede l'invio della segnalazione (predefinita) send Invia la segnalazione automaticamente /FC Utilizza i percorsi completi nella diagnostica /H<num> Lunghezza massima dei nomi esterni Il tipo char predefinito /J è unsigned /MP[n] Utilizza fino a 'n' processi per la compilazione /nologo Non visualizza le informazioni sul copyright /sdl abilita ulteriori avvisi e funzionalità di sicurezza /showIncludes Visualizza i nomi dei file di inclusione /Tc<file di origine> Compila il file come file .c /Tp<file di origine> Compila il file come file .cpp /TC Compila tutti i file come file .c /TP Compila tutti i file come file .cpp /V<stringa> Imposta la stringa di versione /w Disabilita tutti gli avvisi /wd<n> Disabilita l'avviso n /we<n> Considera l'avviso n come un errore /wo<n> Riporta l'avviso n una sola volta /w<l><n> Imposta il livello di avviso 1-4 per n /W<n> Imposta il livello di avviso (valore predefinito n=1) /Wall Abilita tutti gli avvisi /WL Abilita le informazioni di diagnostica su una sola riga /WX Considera gli avvisi come errori /Yc[file] Crea un file PCH /Yd Inserisce informazioni di debug in ogni OBJ /Yl[sym] Inserisce riferimenti PCH per la libreria di debug /Yu[file] Utilizza il file PCH /Y- Disabilita tutte le opzioni PCH /Zm<n> Massima allocazione di memoria (% del valore predefinito) /Wp64 Abilita gli avvisi relativi alla portabilità a 64 bit - COLLEGAMENTO - /LD Crea un file .DLL /LDd Crea un libreria di debug .DLL /LN Crea un .netmodule /F<num> Imposta la dimensione dello stack /link [librerie e opzioni del linker] /MD Effettua il collegamento con MSVCRT.LIB /MT Effettua il collegamento con LIBCMT.LIB /MDd Effettua il collegamento con la libreria di debug MSVCRTD.LIB /MTd Effettua il collegamento con la libreria di debug LIBCMTD.LIB

Page 37: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 36 di 299

APPLICAZIONE CONSOLE Sono semplici da costruire, non hanno grafica e hanno interfaccia CUI (Character User Interface) che permette all’utente d’interagire con la tastiera e una finestra. /* Nome dell’applicazione: hello.c Programmatore: Descrizione: */ #include <stdio.h> #include <stdlib.h> int main (void) { printf ("Ciao, mondo in .NET\n"); system("pause");return(0); }

PREPROCESSORE Usare l’opzione seguente dall’ambiente MDE (Microsoft Development Environment), per ottenere un file con estensione .I. Selezionare il menu Progetto/Proprietà….

Page 38: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 37 di 299

TOOLS

LINK.EXE C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC>link /? Microsoft (R) Incremental Linker Version 14.00.23026.0 Copyright (C) Microsoft Corporation. All rights reserved. uso: LINK [opzioni] [file] [@commandfile] opzioni: /ALIGN:# /ALLOWBIND[:NO] /ALLOWISOLATION[:NO] /APPCONTAINER[:NO] /ASSEMBLYDEBUG[:DISABLE] /ASSEMBLYLINKRESOURCE:nomefile /ASSEMBLYMODULE:nomefile /ASSEMBLYRESOURCE:nomefile[,[nome][,PRIVATE]] /BASE:{indirizzo[,dimensione]|@nomefile,chiave} /CLRIMAGETYPE:{IJW|PURE|SAFE|SAFE32BITPREFERRED} /CLRLOADEROPTIMIZATION:{MD|MDH|NONE|SD} /CLRSUPPORTLASTERROR[:{NO|SYSTEMDLL}] /CLRTHREADATTRIBUTE:{MTA|NONE|STA} /CLRUNMANAGEDCODECHECK[:NO] /DEBUG /DEF:nomefile /DEFAULTLIB:libreria /DELAY:{NOBIND|UNLOAD} /DELAYLOAD:dll /DELAYSIGN[:NO] /DLL /DRIVER[:{UPONLY|WDM}] /DYNAMICBASE[:NO] /ENTRY:simbolo /ERRORREPORT:{NONE|PROMPT|QUEUE|SEND} /EXPORT:simbolo /FIXED[:NO] /FORCE[:{MULTIPLE|UNRESOLVED}] /FUNCTIONPADMIN[:dimensione] /HEAP:reserve[,commit] /HIGHENTROPYVA[:NO] /IDLOUT:nomefile /IGNOREIDL /IMPLIB:nomefile /INCLUDE:simbolo /INCREMENTAL[:NO] /KERNEL /KEYCONTAINER:nome /KEYFILE:nomefile /LARGEADDRESSAWARE[:NO] /LIBPATH:dir /LTCG[:{NOSTATUS|PGINSTRUMENT|PGOPTIMIZE|PGUPDATE|STATUS}] /MACHINE:{ARM|EBC|X64|X86}

Page 39: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 38 di 299

/MANIFEST[:{EMBED|NO}] /MANIFESTDEPENDENCY:dipendenza manifesto /MANIFESTFILE:nomefile /MANIFESTINPUT:nomefile /MANIFESTUAC[:{NO|UAC fragment}] /MAP[:nomefile] /MAPINFO:{EXPORTS} /MERGE:da=a /MIDL:@filedicomandi /NOASSEMBLY /NODEFAULTLIB[:libreria] /NOENTRY /NOLOGO /NXCOMPAT[:NO] /OPT:{ICF[=iterations]|NOICF|NOREF|REF} /ORDER:@nomefile /OUT:nomefile /PDB:nomefile /PDBSTRIPPED:filename /PGD:filename /POGOSAFEMODE /PROFILE /RELEASE /SAFESEH[:NO] /SECTION:nome,[[!]{DEKPRSW}][,ALIGN=#] /STACK:reserve[,commit] /STUB:filename /SUBSYSTEM:{BOOT_APPLICATION|CONSOLE|EFI_APPLICATION| EFI_BOOT_SERVICE_DRIVER|EFI_ROM|EFI_RUNTIME_DRIVER| NATIVE|POSIX|WINDOWS}[,#[.##]] /SWAPRUN:{CD|NET} /TLBID:# /TLBOUT:nomefile /TSAWARE[:NO] /VERBOSE[:{CLR|ICF|INCR|LIB|REF|SAFESEH|UNUSEDLIBS}] /VERSION:#[.#] /WINMD[:{NO|ONLY}] /WINMDDELAYSIGN[:NO] /WINMDFILE:nomefile /WINMDKEYCONTAINER:name /WINMDKEYFILE:filename /WX[:NO]

Page 40: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 39 di 299

Usare l’opzione seguente dall’ambiente MDE, per ottenere un file con estensione .MAP. Selezionare il menu Progetto/Proprietà….

File HELLO.MAP hello Timestamp is 55d96d67 (Sun Aug 23 08:51:19 2015) Preferred load address is 0000000140000000 Start Length Name Class 0001:00000000 00010000H .textbss DATA 0002:00000000 00005520H .text$mn CODE 0002:00005520 00001016H .text$mn$00 CODE 0002:00006540 000010f1H .text$x CODE 0003:00000000 00000109H .CRT$XCA DATA ……………………………………………………………… 0008:00000170 000002ccH .rsrc$02 DATA Address Publics by Value Rva+Base Lib:Object 0000:00000000 ___safe_se_handler_table 0000000000000000 <absolute> 0000:00000000 __ImageBase 0000000140000000 <linker-defined> ……………………………………………………………………………….. 0001:00000000 __enc$textbss$begin 0000000140001000 <linker-defined> 0001:00010000 __enc$textbss$end 0000000140011000 <linker-defined> 0002:00000bb0 _RTC_Shutdown 0000000140011bb0 f MSVCRTD:_init_.obj

CVTRES.EXE C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC>cvtres /? Microsoft (R) Windows Resource To Object Converter versione 14.00.23026.0 Copyright (C) Microsoft Corporation. Tutti i diritti riservati. sintassi: CVTRES [opzioni] [file] opzioni: /DEFINE:simbolo /FOLDDUPS /MACHINE:{ARM|EBC|IA64|X64|X86} /NOLOGO /OUT:nomefile /READONLY /VERBOSE

Page 41: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 40 di 299

DUMPBIN.EXE C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC>dumpbin /? Microsoft (R) COFF/PE Dumper Version 14.00.23026.0 Copyright (C) Microsoft Corporation. All rights reserved. uso: DUMPBIN [opzioni] file opzioni: /ALL /ARCHIVEMEMBERS /CLRHEADER /DEPENDENTS /DIRECTIVES /DISASM[:{BYTES|NOBYTES}] /ERRORREPORT:{NONE|PROMPT|QUEUE|SEND} /EXPORTS /FPO /HEADERS /IMPORTS[:nomefile] /LINENUMBERS /LINKERMEMBER[:{1|2}] /LOADCONFIG /OUT:nomefile /PDATA /PDBPATH[:VERBOSE] /RANGE:vaMin[,vaMax] /RAWDATA[:{NONE|1|2|4|8}[,#]] /RELOCATIONS /SECTION:nome /SUMMARY /SYMBOLS /TLS /UNWINDINFO

EDITBIN.EXE C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC>editbin /? Microsoft (R) COFF/PE Editor Version 14.00.23026.0 Copyright (C) Microsoft Corporation. All rights reserved. uso: EDITBIN [opzioni] [file] opzioni: /ALLOWBIND[:NO] /ALLOWISOLATION[:NO] /APPCONTAINER[:NO] /BIND[:PATH=path] /DYNAMICBASE[:NO] /ERRORREPORT:{NONE|PROMPT|QUEUE|SEND} /HEAP:reserve[,commit] /HIGHENTROPYVA[:NO] /LARGEADDRESSAWARE[:NO] /NOLOGO /NXCOMPAT[:NO] /REBASE[:[BASE=address][,BASEFILE][,DOWN]] /RELEASE /SECTION:name[=newname][,[[!]{CDEIKOMPRSUW}][A{1248PTSX}]] /STACK:reserve[,commit]

Page 42: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 41 di 299

/SUBSYSTEM:{BOOT_APPLICATION|CONSOLE|EFI_APPLICATION| EFI_BOOT_SERVICE_DRIVER|EFI_ROM|EFI_RUNTIME_DRIVER| NATIVE|POSIX|WINDOWS}[,#[.##]] /SWAPRUN:{[!]CD|[!]NET} /TSAWARE[:NO] /VERSION:#[.#]

PGOCVT.EXE C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC>pgocvt /? Microsoft (R) Profile Guided Optimization Database to Object Converter 14.00.23026.0 Copyright (C) Microsoft Corporation. Tutti i diritti riservati. uso: PGOCVT [opzioni] [file] opzioni: /NOLOGO /OUT:nomefile /MACHINE:computerdidestinazione /VERBOSE

PGOMGR.EXE C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC>pgomgr /? Microsoft (R) Profile Guided Optimization Manager 14.00.23026.0 Copyright (C) Microsoft Corporation. Tutti i diritti riservati. uso: PGOMGR [opzioni] [Percorsi-conteggio-profili...] Database-profilo opzioni: /? Visualizza questo argomento della Guida /help Visualizza questo argomento della Guida /clear Rimuove tutti i dati di unione da questo database /detail Visualizza statistiche dettagliate sul programma /merge[:n] Unisce i file PGC specificati, con il peso integer opzionale /summary Visualizza le statistiche sul programma /unique Visualizza i nomi decorati delle funzioni

PGOSWEEP.EXE C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC>pgosweep /? Microsoft (R) Profile Guided Optimization Sweeping Utility 14.00.23026.0 Copyright (C) Microsoft Corporation. Tutti i diritti riservati. uso: PGOSWEEP [opzioni] Nome-PGC-output-eseguibile-instrumentato opzioni: /? Visualizza questo argomento della Guida /help Visualizza questo argomento della Guida /noreset Non reimposta a zero i conteggi dopo lo sweep

UNDNAME.EXE C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC>undname /? Microsoft (R) C++ Name Undecorator Copyright (C) Microsoft Corporation. All rights reserved. Usage: undname [flags] fname [fname...] or: undname [flags] file

Page 43: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 42 di 299

VISUAL STUDIO

SVILUPPO DI APPLICAZIONI Console. Windows Forms. WPF (Windows Presentation Foundation). WCF (Windows Communication Foundation). EF (Entity Framework). LINQ (Language INtegrated Query). Programmazione parallela. Office. SharePoint. Web: ASP.NET (Active Server Pages). Silverlight. Cloud computing: Windows Azure.

Estensioni Visual Studio è estendibile con funzionalità che possono essere aggiunte mediante l’installazione di add-in che possono essere realizzati in proprio, creando apposite applicazioni attraverso lo stesso ambiente di sviluppo di Visual Studio. Oppure possono essere scaricati e installati, visitando l’apposito portale su Internet, da un repository creato appositamente e denominato Visual Studio Gallery. Alcuni add-in sono commerciali e sono forniti in versione in prova. Nell’angolo superiore destro della pagina si trova una casella a discesa che permette di scegliere la lingua preferita, compreso l’italiano. Cliccando sul nome di un add-in, si apre una pagina che permette di esaminare le caratteristiche dell’add-in stesso, la versione di Visual Studio su cui può essere installato, la votazione degli utenti, il numero di volte che è stato scaricato, la versione attuale, la data e ora dell’ultimo aggiornamento, l’indicazione se è un prodotto gratuito o in prova.

Page 44: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 43 di 299

C’è il pulsante che permette di scaricare e installare l’add-in. Sviluppo di applicazioni mobili e videoludiche: XNA (XNA is Not Acronymed).

Page 45: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 44 di 299

DynamicDataDisplay È gratis, libreria di controlli WPF e Silverlight per la visualizzazione dinamica e grafica di dati. Le simulazioni che sono messe a disposizione arrivando a tracciare i punti in continuazione, cambiando la scala del grafico man mano che sono aggiunti i punti. L’esempio MapSample permette di visualizzare una mappa mondiale, scendendo fino al livello delle strade cittadine e mostra in una lente d’ingrandimento i dettagli.

L’esempio CoastlineSampleApp permette di visualizzare la linea delle coste di tutto il mondo. L’esempio IsolineSampleApp permette di visualizzare le isolinee, linee appartenenti allo stesso piano, muovendo il mouse sul grafico.

Page 46: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 45 di 299

Fare clic su Strumenti/Estensioni e aggiornamenti….

NuGet È uno strumento di Visual Studio che permette d’integrare nelle applicazioni delle librerie sviluppate da terzi. Fare clic su Strumenti/Gestione pacchetti NuGet/Impostazioni di Gestione pacchetti.

Page 47: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 46 di 299

MDE Ambiente integrato: è possibile gestire l’intero ciclo di sviluppo di applicazioni di qualsiasi tipo esse siano, racchiude l’editor per tutti i linguaggi di programmazione supportati dalla piattaforma .NET. Visual Studio è un’applicazione MDI (Multiple Document Interface), in grado di consentire l’apertura contemporanea di più documenti, all’interno di schede. È possibile realizzare applicazioni utilizzando più linguaggi all’interno della stessa soluzione perché condividono anche la stessa organizzazione dei file sorgenti. Start/Tutte le app/Visual Studio 2015

È possibile personalizzare l’ambiente di sviluppo tramite macro scritte con VBA, utilizzando un editor analogo a quello disponibile in Office. L’area di lavoro è suddivisa in zone, dove nella parte centrale si trova l’editor. Progetto È l’insieme dei file (sorgenti e risorse) che concorrono alla creazione dell’applicazione. Visual studio contiene un numero elevato di template: insieme già pronto di file configurati per sviluppare uno specifico tipo di progetto, raggruppati in aree così che sia più facile individuarli. Soluzione È l’insieme di più progetti, nelle applicazioni semplici la soluzione contiene un solo progetto, in scenari più complessi ci sono più progetti all’interno della soluzione. Per aggiungere un nuovo progetto alla soluzione, fare clic su File/Aggiungi/Nuovo progetto…. Per aggiungere un progetto esistente alla soluzione, fare clic su File/Aggiungi/Progetto

Page 48: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 47 di 299

esistente…. Quando una Soluzione è composta da più progetti, il progetto di default all’avvio, è quello in grassetto, per modificare il progetto di avvio, fare clic in Esplora soluzioni e dal menu contestuale selezionare la voce Imposta come progetto di avvio.

È possibile cambiare in qualsiasi momento il nome della soluzione e dei progetti. Nel caso di questi ultimi, occorre prestare attenzione al fatto che per cambiare il nome del namespace è necessario accedere al menu Progetto/Proprietà…. Cambiare il nome degli elementi all’interno di Esplora soluzioni è la scelta migliore, perché ha effetto sul nome fisico del file e consente di mantenere intatta e valida la struttura della soluzione. Avviando per la prima volta Visual Studio, sarà visualizzata nella parte centrale della finestra una serie di opzioni utili per personalizzare l’ambiente di sviluppo; esse saranno comunque accessibili anche in futuro all’interno della finestra Strumenti/Opzioni... Per esempio, la possibilità di usare l’accelerazione H/W che è automaticamente abilitata.

Possibilità di modificare la disposizione di finestre e pannelli grazie ai gadget per l’ancoraggio che appaiono quando si trascinano le finestre, lo strumento appare come una croce che indica quale posizione avrà la finestra rispetto al pannello sottostante. Ad esempio, se si rilascia il tasto del mouse sul simbolo al centro della croce, si sovrappone la finestra trascinata a quella sottostante e si crea così un pannello con più schede, per passare da una finestra all’altra si usano le relative linguette.

Page 49: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 48 di 299

Una volta personalizzato l’ambiente di sviluppo, c’è la possibilità di conservare e riapplicare tutte le preferenze, grazie alle funzionalità di Strumenti/Importa/Esporta impostazioni… che permette anche di effettuare il reset di tutte le impostazioni. Questo consente di utilizzare impostazioni diverse a seconda del progetto su cui si sta lavorando. C’è il supporto a chi ha più di un monitor, è possibile estrarre dal layout anche le finestre di codice e spostarle tra i diversi schermi. La possibilità di visualizzare contemporaneamente più finestre di codice, designer, permette una visione d’insieme senza precedenti. Per creare un nuovo progetto, fare clic su File/Nuovo/Progetto… (CTRL+MAIUSC+N). È visualizzata la finestra di dialogo seguente, è possibile scegliere anche un progetto da un elenco di modelli online.

Multi targeting Nella parte superiore della finestra è possibile scegliere il .NET Framework che si vuole utilizzare, versioni 2.0, 3.0, 3.5, 4.0, 4.5; in base alla versione scelta, si troveranno nella finestra di dialogo solamente i progetti compatibili

Page 50: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 49 di 299

Esplora soluzioni Fare clic su Visualizza/Esplora soluzioni (CTRL+ALT+L), presenta l’organizzazione gerarchica dei componenti del progetto con nome, finestre, codice e moduli.

È possibile utilizzare nella soluzione delle cartelle che non siano progetti ma possano servire per includere risorse o documentazione, fare clic sul nome della soluzione con il pulsante destro del mouse e dal menu contestuale selezionare Aggiungi/Nuovacartella. I file contenuti in queste cartelle non saranno compilati. A capo automatico Fare clic su Modifica/Avanzate/A capo automatico. Tutte le righe di codice che superano la lunghezza dell’editor sono automaticamente mandate a capo, per evitare la necessità di scorrere lateralmente la finestra dell’editor. Segnalibri Fare clic su Modifica/Segnalibri/Attiva/Disattiva segnalibro (CTRL+K+T). Numeri di riga Fare clic su Strumenti/Opzioni…/Editor di testo/C#/Editor/Numeri di riga. I numeri di riga sono aggiunti al codice. Editor Le varie parti del codice possono essere compresse ed espanse facendo clic rispettivamente sul simbolo (-) e (+) che è automaticamente visualizzato a destra delle dichiarazioni di un metodo, di un insieme di righe di commento.

È possibile effettuare lo zoom del codice tenendo premuto il tasto CTRL e usando la rotellina del mouse, utile se si è in presenza di un metodo molto lungo, si può ridurre il livello di zoom

Page 51: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 50 di 299

temporaneamente per tenere sotto occhio una parte di codice più ampia.

IntelliSense Permette d’inserire del codice li dove è prevedibile che sia inserito. Semplifica il completamento automatico d’istruzioni di codice evitando di digitare parole intere, si attiva non appena s’inizia a digitare nell’editor, altrimenti digitare CTRL+SPAZIO. Argomenti dalla linea di comando Fare clic su Progetto/Proprietà….

Compilazione Il risultato è contenuto nella cartella BIN, posta sotto il progetto. Può essere eseguita in due modi. Compila (F6).

Page 52: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 51 di 299

Eseguendo l’applicazione Debug/Avvia senza eseguire debug (CTRL+F5), anche con il debugger Debug/Avvia debug (F5).

Cartella superiore Se non si conosce il percorso del progetto, basta fare clic con il tasto destro sul nome del codice e selezionare Apri cartella superiore per vedere la cartella con esplora risorse.

Configurazione Quando si crea una nuova soluzione, ci sono già pronte due configurazioni. Debug: indicata solo per lo sviluppo perché non è ottimizzata per la produzione. Release: indicata per compilare e gestire la soluzione per la distribuzione.

Page 53: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 52 di 299

DEBUG Maggiori sono le dimensioni delle applicazioni e le caratteristiche da implementare, maggiori sono le possibilità d’incorrere in errori, detti, in gergo, bug dal verbo debug (to) che significa mettere a punto, in pratica è un processo per riconoscere e correggere errori. Dal progetto alla realizzazione possono essere variate sia le condizioni generali sia le specifiche di base dell’applicazione; il cambiamento di queste ultime è una fonte inesauribile di errori, in altri casi gli errori sembrano presentarsi in modo casuale. Per scovare questi errori, sarebbe molto comodo possedere uno strumento che permetta di scorrere il sorgente riga per riga, controllando, se possibile in una finestra attigua, il valore assunto dalle variabili da tenere sotto controllo. In via generale la fase di debugging si articola nei seguenti quattro passi. 1. Verifica dell’esistenza di un errore, i casi macroscopici si hanno quando la macchina si

“pianta”, altre volte l’applicazione gira correttamente fino a quando non ha a che fare con un numero, o carattere, critico al quale è associato l’errore.

2. Ricerca della posizione dell’errore, questo è più difficile quanto più lunga è l’applicazione, allora conviene sezionarla e individuare quale parte contiene l’errore, la programmazione modulare si sposa perfettamente con questa metodologia di lavoro perché permette di testare singolarmente i vari moduli.

3. Ricerca della causa dell’errore. 4. Correzione dell’errore. Fatal Immediata interruzione. Error Impediscono la generazione dei file OBJ e EXE ma l’esecuzione è completata. Syntax error/Compile-time errors Sono errori di sintassi che gli strumenti di sviluppo possono rintracciare, per esempio l’onda rossa è l’indicatore di errore di sintassi. Si verificano quando un’istruzione non è scritta nel modo corretto e sono segnalati prima che l’applicazione vada in run-time perché sono rilevati in compile-time. Cursore in edit sulla riga dell’errore, nella finestra Elenco errori riga c’è il messaggio relativo.

Warning Condizioni sospette, effetti non voluti genera in ogni caso i file OBJ e EXE.

Page 54: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 53 di 299

Run-time errors Errori di semantica. Errori logici Fa ciò che gli avete detto di fare, anziché ciò che volevate che facesse. <<Un programma che gira, non è detto che giri bene; un test può essere usato per mostrare la presenza di errori, non per mostrare la loro assenza.>> Dijkstra Non si può sostenere che un’applicazione è buona solo perché ha superato dei test. Un test può essere. Selettivo: non garantisce la validità perché i dati d’input sono di natura soggettiva. Esaustivo: impossibile assegnare tutti i valori ai dati d’input perché il tempo di verifica

tenderebbe ad esplodere. Quando un’applicazione comincia ad essere rattoppata al punto, di perdere la sua struttura è più semplice riscriverla. I debugger sono programmi che caricano a loro volta le applicazioni da sottoporre a verifiche. Visual Studio include un debugger, fare clic su Debug/Finestre.

Page 55: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 54 di 299

Possibilità di eseguire una ricerca nella finestra Punti di interruzione. Una finestra Thread fornisce operazioni di filtro, ricerca ed espansione dello stack delle chiamate e raggruppamento. È possibile utilizzare le finestre degli strumenti del debugger. Stack in parallelo Attività in parallelo Per visualizzare ed eseguire il debug di un codice parallelo scritto in Visual C++, Visual C# o Visual Basic. La finestra Espressioni di controllo e i suggerimenti sui dati forniscono un’icona per avvertire l’utente quando la valutazione di un’espressione richiede l’esecuzione di altri thread. Questa esecuzione può modificare lo stato dell’applicazione e fare sì che eventi di debug quali i punti d’interruzione siano ignorati. Breakpoint Consentono di fissare un punto specifico, al cui raggiungimento il debugger si ferma, in attesa che il programmatore possa verificare lo stato di una particolare variabile. Per impostare un breakpoint, fare clic a fianco della riga o sul menu Debug/Imposta/Rimuovi punto d’interruzione (F9), compare un pallino rosso.

Watch Se si passa il mouse sopra una variabile, è visualizzato in automatico il relativo valore.

Page 56: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 55 di 299

FILE CREATI DALLA COMPILAZIONE Compilato ed eseguito l’esempio, la sotto cartella HELLO\DEBUG del progetto contiene i seguenti file.

HELLO.EXE È il file eseguibile dell’applicazione. HELLO.ILK (Incrementale LinKer) È usato dal linker quando si ricompila il progetto, in pratica collega i file OBJ in modo incrementale per evitare di ricollegare il tutto ogni volta che il codice sorgente è modificato. HELLO.PDB (Program Debug Database) Contiene le informazioni di debugging che sono usate quando si esegue l’applicazione in modalità debug. la sotto cartella HELLO\HELLO\DEBUG del progetto contiene il seguente file. HELLO.OBJ È il file oggetto usato dal linker, insieme alle librerie, per generare l’EXE.

Page 57: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 56 di 299

C++

TIMELINE

C++ 11 Rvalue references, move semantics. Lambda expressions. Automatic type deduction, auto and decltype. Range based for loops. Initializer list, uniform initialization syntax. Variadic templates, template aliases, constexpr. Library: threading, atomics, smart pointers. Le novità semplificano e arricchiscono le basi del linguaggio. var numbers=new List<int>() { 1, 2, 3, 4, 5 }; auto numbers = vector<int>{ 1, 2, 3, 4, 5 };

C++ 14 Return type deduction. Generic lambdas. Initialized lambda captures. Variable templates.

C++ 17 Prossima major release. TS (Technical Specifications) Library fundamentals: optional<>, string_view. Arrays: runtime-sized arrays, dynarray<>. Parallelism: Parallel STL (Standard Template Library), parallel algorithms. Concurrency: Nonblocking futures (.then), executors. File System: Portable file system access: Boost.Filesystem. Networking: IP (Internet Protocol) addresses, URI (Uniform Resource Identifier), byte ordering. Concepts Lite: Extensions for template type checking. Transactional Memory.

Page 58: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 57 di 299

auto x = expression; Il compilatore deduce il tipo di x da expression, in modo naturale. int value= 10; auto a = value; // int auto& b = value; // int& auto it = begin(nums); // vector<int>::iterator initializer lists Un container formale creato automaticamente dal compilatore per inizializzare oggetti inline. auto numbers = vector<int> { 1, 2, 3, 4, 5 }; auto list = {1, 2, 3, 4, 5}; class MyVector {

public: MyVector( ) data(begin(init), end(init)){ } ... private:

MyInternalDetails data; };

Value semantics Quello che differenzia il C++ dagli altri linguaggi è la possibilità di controllare con precisione il lifetime degli oggetti: ciò che succede quando un oggetto è creato, copiato, assegnato e distrutto. Normalmente gli oggetti sono passati per valore. Aggiungendo esplicitamente qualificatori (&) è possibile adottare una semantica differente. vector<int> CreateMatrix() { vector<int> tmp{}; // ... riempi tmp: copia di tmp in matrix return tmp; } auto matrix = CreateMatrix(); Move semantics È un’opportunità di ottimizzazione, swap di puntatori è economico ed exception-safe. Dà vita a nuovi idiomi: return by-value, pass by-value. Permette di modellare una policy di unique ownership tramite “trasferimento di proprietà” implicito o esplicito. Estende il controllo al caso in cui un oggetto sia costruito a partire da oggetti temporanei, in altre parole oggetti che stanno per essere distrutti. vector<int> CreateMatrix() { vector<int> tmp{}; // ... riempi tmp: creazione di matrix per move da tmp return tmp; } auto matrix = CreateMatrix();

Page 59: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 58 di 299

RAII (Resource Acquisition Is Initialization) La distruzione degli oggetti avviene in modo deterministico. Il linguaggio garantisce che una variabile auto (locale) sia distrutta appena esce dal suo scope di definizione. vector<string> GetLines(const string& path) { ifstream file{ path }; // file.open(); // ... } // file.close() automatico Rule of zero Se una classe ha a che fare esclusivamente con l’ownership, per esempio un vector allora può personalizzare distruttore, costruttori di copia/move, operatori di assegnazione (copia/move). Altrimenti, per esempio una classe di dominio non deve personalizzarne alcuno, il compilatore crea per i programmatori quelli di default: member-wise. Iterazione Per visitare tutti gli elementi di un container usa un range-based for loop. void Graph::Accept(IVisitor& visitor) { for (auto& node : nodes) visitor.Visit(node); } Lambda expressions Shortcuts per creare function objects (oggetti chiamabili) anonimi (senza nome e con tipo non specificato), direttamente inline. Funzioni anonime stateless sono castabili a function pointers. auto it = find_if(begin(nums), end(nums), [](int i) { return i % 2 == 0; }); Closures hanno accesso ad alcune/tutte le variabili nello scope. auto sum = 0; auto weight = 2; for_each(begin(nums), end(nums), [&sum, weight](int i) { sum += i * weight; }); Concurrency Aggiunto pieno supporto alla programmazione multi thread. Introdotto un memory model standardizzato per controllare come thread differenti accedono alla memoria: multi-thread by design. thread runThis{ [] { cout << "Hello Thread!" << endl; } }; runThis.join(); Quando usare C++ Performance. Codice nativo “close to the metal”. Prestazioni predicibili anche su sistemi non real time. Portabilità. Molte librerie multi piattaforma.

Page 60: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 59 di 299

Compatibilità con il passato. Aggiornamento progressivo di basi di codice. Il codice è più compatto, manutenibile e sicuro. Heterogenous programming. C++ AMP (Accelerated Massive Parallelism). Problemi con il “vecchio” C++. Memory leaks e dangling pointers. Value semantics costosa. Functors che richiedono una classe a parte. Messaggi di errore dei template illeggibili. Modern C++ non è C++98: è più semplice, sicuro e meno verboso.

Librerie http://en.cppreference.com/w/cpp/links/libs Generic (STL, Boost, Qt, Folly, .…). Communication (Boost.Asio, POCO, ACE, cpp-netlib, …). GUI (Graphic User Interface) (Qt, GTK, ...). Multimedia (Cinder, SFML, SDL, ...). Audio (flac, soundtouch, audiofile, id3lib, ...). Video (mjpegtools, libmatroska, libVLC, ...). 3D Graphics (OpneGL, Ogre3D, VTK, ...). Game Engine (EntityX, Anax, ...). Math (Boost, MLPACK, ...). Concurrency (INTEL TBB, Boost.Thread/Atomic/Lockfree/Context, OpenMP, Thrust, ...). Containers (Boost.Any/Array/Bitmap/Container/Fusion/...). Serialization (Boost.Serialization, protobuf, yaml-cpp, ...). Testing (cppunit, Google Test, Boost.Test, Catch). XML (eXtensible Markup Language) (libxml+, tinyxml, ...). JSON (JavaScript Object Notation) (picojson, libjson, ...). Concurrency Multithreaded by design. Introduce un memory model standard.

Page 61: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 60 di 299

Standard library introduce classi per threads, mutexes, locks e futures. Lambda e smart pointers semplificano la scrittura di codice concorrente. Atomic Sono operazioni non interrompibili. Eliminano data race e comportamenti indefiniti. Abilitano la possibilità di scrivere codice lock-free. Migliorano le prestazioni per semplici operazioni ad elevata contesa. C++ introduce <atomic>, una libreria portabile. atomic_flag (clear and test_and_set), atomic_int, atomic_long, atomic_llong. load, store, exchange, compare_exchange. operator++,--, +=, -=, &=, |=, ^=. fetch_add, fetch_sub, fetch_and, fetch_or and fetch_sub. C++ performance Ottimizzare le performance è importante per consumare meno risorse, per esempio meno server e meno batteria. Ci sono caratteristiche del linguaggio che rendono più semplice scrivere codice “veloce”. Finalizzazione deterministica. Memoria contigua per default: array di oggetti. Value types per default: oggetti allocati nello stack. Move semantics. Inoltre, chiamare librerie “native” da codice managed con P/Invoke comporta un leggero overhead, specialmente per il marshalling dei parametri. Memory layout Ogni class con copying semantics è un value type per default. Allocazione nello stack, deterministic finalization. Per default in C++ array e vector di oggetti sono allocati in modo contiguo. http://www.italiancpp.org/

Page 62: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 61 di 299

MICROSOFT VISUAL C++

APPLICAZIONE CONSOLE VISUAL C++ /* Nome dell’applicazione: hello.cpp Programmatore: Descrizione: */ #include <iostream> #include <stdlib.h> using namespace std; int main ( void) { cout<<"Ciao, mondo in .NET"<<endl; system("pause");return(0); }

APPLICAZIONE ASSEMBLY

Il compilatore genera il file HELLO.ASM.

Page 63: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 62 di 299

VISUAL C++ WIN32 Si riferisce al linguaggio C++ standard e supporta un modello a oggetti statico ottimizzato per la velocità di esecuzione e per le prestazioni. Consente l’accesso al SO e all’H/W sottostante ma dà solo un piccolo supporto per accedere ai tipi attivi durante il run-time.

INTRODUZIONE È un linguaggio OOP con tipizzazione statica con le seguenti caratteristiche. C++ (C Plus Plus) significa incremento del C, quindi continuità e compatibilità. Classi, il nome di una classe è un nome di tipo. Funzioni virtuali. Overloading di funzioni e operatori. Ereditarietà multipla. template. Nuovi tipi di cast. Operatore di qualifica della visibilità (::) chiamato risolutore di scope o di risoluzione del

campo di azione. Tipo di dato booleano. Il nome delle enumerazioni è un nome di tipo. Gestione delle eccezioni. Riferimenti. Specificatore const. Controllo dell’utente della gestione della memoria: operatori new e delete Il controllo sui tipi è migliorato. Commento (//):tutto ciò che segue (//) fino alla fine della linea, ne consegue che non è

possibile inserire un commento in mezzo al codice oppure dividerlo su più righe.

LIBRERIE STANDARD Si trovano le seguenti cose. La libreria standard del C/C++. Il supporto per stringhe e manipolatori di stream. Il supporto per diversi tipi di “oggetti contenitore”, in altre parole le principali strutture dati

utilizzabili nelle applicazioni, esempio stack, queue e array. Il supporto per il calcolo numerico, esempio numeri complessi e operatori logici. Il supporto per l’ambiente run-time, esempio la gestione delle eccezioni. La libreria standard del C++ incorpora la libreria standard del C con alcune modifiche minori che ne permettono un uso più comodo. Il C++ fornisce 69 header standard, dei quali 19 sono deprecati.

COMPILATORE Se un compilatore accetta un codice non significa che è necessariamente corretto. I compilatori, infatti, applicano spesso qualche deviazione o estensione rispetto al C++ standard e devono necessariamente adottare qualche arbitrio nell’interpretazione dei comportamenti dipendenti dall’implementazione, non specificati o indefiniti. La costante endl (end line) fornisce lo stesso risultato di newline (\n). #include <iostream> int main( void) { signed char n = 127; system("cls"); std::cout << int(++n)<<std::endl;

Page 64: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 63 di 299

system("pause");return(0); }

Questo è un tipico caso di comportamento indefinito. Non si sa cosa succederà in run-time, perché lo standard non stabilisce queste due cose. 1. Il valore massimo di char, occupa al minimo un byte. 2. Che cosa succede in caso di overflow di un valore signed, dato che la maggioranza delle

architetture usa il complemento a 2, stampa -128. Se si scrive S/W portabile, attenzione a non cadere nel comportamento indefinito o a non dare per scontato un comportamento dipendente dall’implementazione. Troppi dati Il limite consiste solo nella massima area di memoria dimensionabile. Naturalmente, il numero degli elementi dipende dal tipo di dato con cui si lavora. #include <iostream> using namespace std; const int N =1000000000; int main(void) { // double vettore[N]; // Errore: l'espressione dev’essere un lvalue modificabile double *vettore; system("cls"); if ((vettore=new double[N]) !=NULL) cout<<"Ciao"<<endl; else cout<<"Memoria insufficiente!"<<endl; if (vettore !=NULL) delete vettore; system("pause");return(0); }

COSTANTI Usare #define per definire delle costanti è tipico in C ma non permette al compilatore d’inserire la costante all’interno della tabella dei simboli, perché all’unità di traduzione tale costante non arriva nemmeno: è intercettata e sostituita dal preprocessore. In questo modo ottimizzazioni, controlli, messaggi di errore significativi, diventano tutti impossibili. In C++, invece, const è un vincolo che è impossibile aggirare, se non per mezzo di casting, è possibile definire delle costanti con identificatore, è dichiarata solo nel blocco in cui dev’essere utilizzata e il suo valore può anche essere il risultato di un’espressione o il valore restituito da una funzione. Sintassi. const <variable name> [ = <value> ] ;

Page 65: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 64 di 299

<function name> ( const <type>*<variable name> ;) #include <iostream> using namespace std; int main (void) { int s=0; system("cls"); { const int s=5000; cout<<"s ha come visibilita' il blocco interno: "<<s; } s +=1000; cout<<"\ns ha come visibilita' il main:\t\t "<<s<<endl; system("pause");return(0); }

RISOLUTORE DI SCOPE Per riferirsi ad una variabile globale può essere usata la sintassi seguente che permette di riferirsi alla dichiarazione globale di un identificatore. :: variabile_nome; #include <iostream> using namespace std; int a=10; int main(void) { int a=80,b=0; system("cls"); b=::a; cout<<b<<endl; b=a; cout<<a<<endl; system("pause");return(0); }

#include <iostream> using namespace std; void prova (void); int i; int main (void) { system("cls"); prova(); cout<<"Main:\t "<<i<<endl; system("pause");return(0); } void prova (void) { int i=3;

Page 66: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 65 di 299

::i=4; cout<<"Funzione: "<<i<<endl; }

NAMESPACE Lo scope rappresenta l’ambito di visibilità di una variabile. Il C ha una singola area dove tutti i nomi degli identificatori e delle funzioni esistono, ciò significa che tutti i programmatori devono fare attenzione a non usare gli stessi nomi in situazioni dove ci possono essere conflitti. Il C++ possiede un meccanismo per prevenire questa collisione con la parola riservata namespace. Si può pensare ai namespace come a degli “spazi dei nomi”: definire un namespace significa raggruppare sotto un nome comune un insieme di elementi correlati, mantenendo quindi separati gli elementi che appartengono a namespace diversi, sono molto utili anche nella creazione di librerie oppure per separare versioni diverse di codici o classi. Usare un namespace significa rendere visibili dei nuovi elementi all’interno delle applicazioni. namespace Acquario { class Pesce { // codice della classe }; } La definizione di un namespace non termina con il (;) a differenza di ciò che si fa per le classi. Nell’esempio si è inteso dire che solamente usando il namespace Acquario, la classe Pesce sarà visibile all’applicazione. L’utilità dei namespace diventa evidente nel momento in cui si ha a che fare con classi omonime. Se si avesse la seguente definizione di namespace. namespace LaghettoArtificiale { class Pesce { // codice della classe }; } Le classi definite nel nuovo namespace e analogamente per tutti gli altri elementi nel namespace sarebbero altre classi, omonime a quelle definite nel namespace Acquario ma appartenenti ad un namespace diverso. Le classi così definite sono utilizzabili anche in contemporanea, il (::) indica al compilatore dove cercare l’identificatore che lo segue. LaghettoArtificiale::Pesce p1("Trota"); Acquario::Pesce p2("Scalare "); Sono due oggetti di due classi diverse anche se omonime e quindi potrebbero coesistere all’interno della stessa applicazione: infatti, la classe Pesce di cui fanno parte è nel primo

Page 67: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 66 di 299

caso quella del namespace LaghettoArtificiale, nel secondo quella del namespace Acquario. La definizione di un namespace può essere anche divisa in più file, utilizzando le direttive al compilatore seguenti. #ifndef #define #endif Questo è molto utile per estendere un namespace già esistente, aggiungendovi, ad esempio, altre classi, la struttura del codice per tale estensione è la seguente. #ifndef ACQ1 #define ACQ1 namespace Acquario { namespace Acquario { class Pesce { //codice della classe }; } #endif } Ogni insieme di definizioni C++ in una libreria o applicazione è inglobato in un namespace e se qualche altra definizione ha un nome identico ma si trova in un namespace differente, allora non c’è collisione. In pratica, permette di suddividere lo spazio globale dei nomi in parti più piccole, i namespace e rendere unicamente distinguibili tali parti. A questo punto, un identificatore può apparire più volte ma in namespace distinti. Per riferirsi, infatti, ad uno specifico identificatore, occorre indicare anche il nome del namespace. I namespace servono anche ad evitare collisioni fra i nomi creati dal programmatore e quelli che usa la libreria standard. I namespace sono dei contenitori di nomi su cui sono definite le seguenti regole. Per esempio, l’utilizzo del namespace std permette l’integrazione con il resto della libreria standard. 1. Un namespace può essere creato solo nello scope globale. 2. Se nello scope globale di un file esistono due namespace con lo stesso nome, essi sono

fusi in uno solo. 3. È possibile creare un alias di un namespace. 4. È possibile avere namespace senza nome, detti namespace anonimi.

using Consente di utilizzare un namespace, in pratica uno spazio dei nomi al cui interno sono definiti i nomi dei tipi a esso correlati e che, fondamentalmente, garantisce dall’evenienza di poter avere conflitti tra tipi che possono avere lo stesso nome. Il dover specificare il nome del namespace seguito dal (::) per ogni riferimento è un’operazione noiosa, per esempio qualificare i nomi della libreria standard, usando il prefisso (std::). Si può risolvere questo problema usando la parola riservata using per indicare che il nome del namespace corrente, in pratica quello che si sta usando è std, senza bisogno d’indicarlo quando ci si riferisce ad ogni identificatore in esso contenuto. #include <iostream>

Page 68: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 67 di 299

using namespace std; int main( void) { signed char n = 127; system("cls"); cout << int(++n)<<endl; system("pause");return(0); } Per utilizzare la classe Form del namespace System::Windows::Forms, contenuta nell’assembly SYSTEM.WINDOWS.FORMS.DLL, si dovrà scrivere all’inizio del file la direttiva seguente. #using < System.Windows.Forms.dll> Poi, si deve aggiungere l’istruzione seguente. using namespace System::Windows::Forms; bool È utilizzato per rappresentare i valori di verità: vero e falso. bool trovato = false;

GESTIONE I/O Il C++ fornisce il supporto all’I/O a oggetti tramite il file header <iostream> che contiene le classi necessarie per la gestione degli stream. Le classi template che definiscono i flussi in input, output e I/O sono le seguenti. 1. istream, (input stream). 2. ostream, (output stream). 3. iostream, (input output stream). Poiché il C++ vede le periferiche standard d’I/O, tastiera e video, come particolari file, nel file d’inclusione <iostream> sono predefiniti alcuni oggetti. 1. cin (console input) è un oggetto predefinito della classe istream, usato per i flussi d’input

da tastiera. 2. cout (console output) è un oggetto predefinito della classe ostream, usato per i flussi di

output su monitor. 3. cerr (console error) è un oggetto predefinito della classe ostream, legato al dispositivo di

errore standard e che fornisce un output non bufferizzato. 4. clog (console log) è un oggetto predefinito della classe ostream, legato al dispositivo di

errore standard e che fornisce un output bufferizzato, è la versione completa di cerr. Agli oggetti cin e cout sono associati gli operatori di uso comune quali (<<) e (>>), rispettivamente inseritore ed estrattore. Gli stream possono essere concatenati, in altre parole posti in sequenza uno dopo l’altro, per ottenere risultati complessi, una concatenazione di flussi è a sua volta un flusso. /* Nome del programma: sequenza.cpp Programmatore: Descrizione: */ #include <iostream> using namespace std; const int N =10000000; int main(void) { system("cls");

Page 69: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 68 di 299

cout<<"Ciao, mondo!"<<endl; system("pause");return(0); } La libreria <iostream> si basa su due gerarchie parallele di classi derivanti rispettivamente dalla classe ios (input output stream) e dalla classe streambuf. La classe istream sovrintende alle operazioni d’input da uno stream. int gcount(); Restituisce il numero di caratteri letti nell’ultima operazione d’input. int get(); Estrae dallo stream un carattere o il simbolo EOF.

stream& get (char *s, int n, char d='\n'); Estrae dallo stream al massimo (n-1) caratteri e li assegna a s che è chiusa '\0', il delimitatore è conservato nel file. stream& getline (char *s, int n, char d='\n'); Estrae dallo stream al massimo (n-1) caratteri e li assegna a s che è chiusa '\0', il delimitatore è estratto dal file. stream& ignore (int n=1, char d=EOF); Salta n caratteri nello stream. int peek(); Restituisce il codice del primo elemento presente nello stream senza estrarlo. stream& putback (char c); Riscrive un carattere in testa allo stream. La classe ostream sovrintende alle operazioni di output di uno stream. ostream& put (char c); Inserisce in coda allo stream il carattere specificato. Se all’interno della stringa da acquisire è presente un blank, quest’ultimo è interpretato dall’operatore (>>) come carattere delimitatore e chiude l’acquisizione stessa, inoltre i caratteri rimanenti sono mantenuti nel buffer d’input ed interferiscono con le fasi d’input successive.

Page 70: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 69 di 299

#include <iostream> using namespace std; int main(void) { char nome[10]; system("cls"); cin>>nome; cout <<nome<<endl; system("pause");return(0); }

Per ovviare a questo inconveniente si deve usare la funzione seguente. #include <iostream> using namespace std; const int N = 10; int main(void) { char nome[N]; system("cls"); cin.get(nome,N); cout <<nome<<endl; system("pause");return(0); }

Questa funzione estrae dal buffer d’input tutti i caratteri fino ad incontrare il delimitatore di fine stringa ('\n') oppure fino al raggiungimento della dimensione massima specificata diminuita di uno ma il delimitatore non è estratto dal buffer. Questo comportamento può provocare delle disfunzioni nel caso in cui siano previste più chiamate consecutive alla funzione cin.get. Infatti, non estraendo dal buffer d’input il carattere ('\n'), nel momento in cui sarà richiesta una nuova acquisizione con la cin.get, il buffer non risulterà vuoto e di conseguenza non sarà richiesto un nuovo valore da tastiera ma forzata nella variabile la sequenza di caratteri, eventualmente vuota, presenti nel buffer. Quindi c’è la necessità di procedere, dopo l’acquisizione, alla rimozione dei caratteri rimasti nel buffer, compreso ('\n'), con la funzione seguente. #include <iostream> using namespace std; int main(void) { char nome[10]; system("cls"); cin.get(nome,10); cout <<nome; cin.get(); cout<< endl; cin.get(nome,10);

Page 71: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 70 di 299

cout <<nome<<endl; system("pause");return(0); }

All’interno della libreria standard per l’I/O c’è una classe particolare, la classe streambuf che si occupa della bufferizzazione dei flussi di dati: è un meccanismo mediante il quale s’inviano o ricevono blocchi di dati anziché elementi singoli. Ad esempio, si ricevono cento caratteri alla volta invece di uno alla volta, oppure s’inviano centoventotto byte alla volta invece di sedici. La bufferizzazione è usata di default dal C++ in cout. Per effettuare la stampa di un carattere a schermo un’applicazione non fa diretto accesso alla risorsa H/W video ma richiama delle routine del SO per ottenere tale servizio di stampa. La chiamata di queste routine è completamente trasparente al programmatore, in quanto implementata dal compilatore, nel pieno rispetto dell’information hiding. Nel caso di cout la bufferizzazione non fa altro che porre nel buffer i vari caratteri che si mandano in stampa, allo scopo di effettuare molte meno chiamate al SO e aumentare l’efficienza. Una volta raggiunto un certo numero di caratteri accumulati nel buffer, è svuotato mandando in stampa, in un’unica volta, i vari caratteri accumulati. #include <iostream> using namespace std; const int N =100; int main (void) { system("cls"); for (int i=0;i<N;++i) cout << "*"; cout <<endl; system("pause");return(0); }

A volte può essere utile però effettuare la stampa proprio nel momento in cui si esegue la relativa istruzione. In pratica, può essere necessario dover bypassare questo meccanismo di bufferizzazione; questo accade quando non si è interessati all’efficienza dell’applicazione, quanto piuttosto alla sua esecuzione pulita, tipicamente quando ci si trova a fare il debug. Per venire incontro a questa necessità il C++ mette a disposizione un altro stream predefinito di output: cerr, da utilizzare tutte quelle volte in cui la bufferizzazione potrebbe essere un problema piuttosto che un vantaggio. Le situazioni tipiche d’uso di cerr sono le seguenti. 1. La stampa di messaggi di errore. 2. L’individuazione di errori in fase di debug, cerr è usato al posto di cout e sarà eliminato

o sostituito da cout nel codice finale dell’applicazione. #include <iostream>

Page 72: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 71 di 299

using namespace std; const int N = 100; int main (void) { system("cls"); for (int i=0;i<N;++i) cerr << "*"; cout<<endl; system("pause");return(0); } Sempre a proposito di bufferizzazione è da segnalare la presenza in C++ di un manipolatore standard che si chiama flush e ha il compito di pulire il buffer utilizzato per le operazioni d’I/O. In altre parole, il buffer è svuotato esattamente quando decide il programmatore e non in un istante deciso dal compilatore. cout << "Ciao, " << flush << "mondo!" << endl; In questo codice lo svuotamento del buffer è forzatamente effettuato dopo la prima concatenazione e la successiva stringa sarà scritta in un buffer vuoto. Il flushing del buffer può essere utile in alcune situazioni particolari, come ad esempio la programmazione di dispositivi con memoria molto limitata, in cui è importante la gestione delle risorse di calcolo, oppure quando si accede a risorse che possono produrre dei risultati spuri, connessioni di rete instabili. Formattazione dell’output I dati con cui si ha a che fare, stringhe, interi e reali sono convertiti automaticamente in flussi di dati, su cui è possibile operare la concatenazione. Non sempre tuttavia è necessario che la concatenazione sia operata su dei dati. A volte è possibile utilizzare i manipolatori di flusso, in altre parole degli oggetti che, nonostante non aggiungano nulla all’informazione veicolata dal flusso, sono inseriti nella concatenazione per modificare l’andamento del flusso stesso, per esempio formattare l’output per modificarne l’aspetto di visualizzazione. Essi non rappresentano dei dati d’interesse veri e propri, piuttosto rappresentano delle istruzioni sul trattamento del flusso che però sono concatenate esattamente come se fossero dati, ad esempio il manipolatore (endl). Non parametrizzati endl Nuova linea. ends Carattere speciale NULL, terminatore di stringa. ws flush Parametrizzati Sono variabili o metodi, definiti nel file header <iomanip> che possono essere inseriti in un flusso di output. cout.width(3);cout<<num; cout<<setw(3) Definisce la dimensione minima del campo di stampa per un valore numerico che sarà allineato a destra all’interno del campo stesso, l’effetto rimane attivo solo per il primo valore,

Page 73: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 72 di 299

dopo di che è automaticamente ripristinato il formato standard. left, right Allinea a sinistra e a destra. cout.setf(ios::fixed) cout.precision(n) Definisce la precisione dei numeri reali espressi in formato decimale, n è il numero massimo di cifre da porre dopo la virgola cout.setf(ios::scientific) cout.precision(n) – setprecision (n) Definisce la precisione dei numeri reali espressi in formato esponenziale. hex, oct, dec, setbase() Sistema di numerazione dei numeri interi. Setfill(c) Riempie con c gli eventuali blank presenti in un campo. put (get); Stampa (legge) dati binari non formattati. int c = 'x';cout.put(c); cout<<(char)c; #include <iostream> #include <iomanip> using namespace std; int main (void) { double n = 987654.000321; system("cls"); cout<<n<<endl; cout<<setprecision (1)<<fixed <<n<<endl; cout<<setprecision (2)<<fixed <<n<<endl; cout<<setprecision (3)<<fixed <<n<<endl; cout<<setprecision (4)<<fixed <<n<<endl; cout<<setprecision (1)<<scientific<<n<<endl; cout<<setprecision (2)<<scientific<<n<<endl; cout<<setprecision (3)<<scientific<<n<<endl; cout<<setprecision (10)<<scientific<<n<<endl; system("pause");return(0); }

#include <iostream> #include <iomanip>

Page 74: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 73 di 299

using namespace std; int main (void) { system("cls"); cout<<"Radice quadrata di 2: "<<sqrt(2.0)<<endl; cout<<"Radice quadrata di 2: "; cout<<setprecision(2)<<fixed<<sqrt(2.0)<<endl; cout<<"Radice quadrata di 2: "; cout<<setprecision(2)<<scientific<<sqrt(2.0)<<endl; system("pause");return(0); }

È comunque possibile sapere in modo semplice se l’input ha avuto successo usando la funzione good. #include <iostream> using namespace std; int main (void) { int a=0; system("cls"); cout<<"Inserisci un numero: "; cin>>a; if (cin.good()) cout<<"Corretto."<<endl; else cout<<"Errato!"<<endl; system("pause");return(0); }

Stampante #include <iostream> #include <fstream> using namespace std; int main (void) { system("cls"); ofstream cstamp("prn"); cout<<"Questo messaggio compare sullo schermo."<<endl; cstamp<<"Questo messaggio appare sulla stampante!"; system("pause");return(0); }

Gli stream garantiscono maggiormente il rispetto della tipizzazione. Ad esempio, l’istruzione seguente.

Page 75: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 74 di 299

printf("%s", a); Può causare un bel danno, nel momento in cui a è un intero. Problemi che non si hanno utilizzando l’istruzione seguente. cout << a; Inoltre, gli stream aderiscono meglio alle metodologie di progettazione OO. Ad esempio, se si ha un’applicazione che scrive in output e, a un certo punto, si decide di voler mandare la scrittura su file e non più su console, in C si è costretti a cambiare tutte le chiamate printf in fprintf e fare molta attenzione a maneggiare il puntatore al file, mentre con gli stream si risolve tutto in un attimo cambiando la classe su cui opera lo stream. Operatore sizeof() #include <iostream> using namespace std; int main (void) { system("cls"); cout<<"Queste sono le dimensioni in Visual Studio."<<endl<< " char, unsigned char, signed char:\t"<<sizeof(char)<<" bytes"<<endl<< " short, unsigned short:\t\t\t"<<sizeof(short)<<" bytes"<<endl<< " int, unsigned int:\t\t\t"<<sizeof(int)<<" bytes"<<endl<< " long, unsigned long:\t\t\t"<<sizeof(long)<<" bytes"<<endl<< " float:\t\t\t\t\t"<<sizeof(float)<<" bytes"<<endl<< " double:\t\t\t\t"<<sizeof(double)<<" bytes"<<endl<< " long double:\t\t\t\t"<<sizeof(long double)<<" bytes"<<endl<< " puntatore:\t\t\t\t"<<sizeof(int *)<<" bytes"<<endl; system("pause");return(0); }

#include <iostream> #include <limits.h> #include <float.h> using namespace std; int main (void) { system("cls"); cout<<"Questi sono i limiti in Visual Studio."<<endl<< " minimum char value:\t\t"<<CHAR_MIN<<endl<< " maximum char value:\t\t"<<CHAR_MAX<<endl<< " minimum signed char value:\t"<<SCHAR_MIN<<endl<< " maximum signed char value:\t"<<SCHAR_MAX<<endl<< " maximum unsigned char value:\t"<<UCHAR_MAX<<endl<< " minimum (signed) short value:\t"<<SHRT_MIN<<endl<< " maximum (signed) short value:\t"<<SHRT_MAX<<endl<<

Page 76: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 75 di 299

" maximum unsigned short value:\t"<<USHRT_MAX<<endl<< " maximum unsigned long value:\t"<<ULONG_MAX<<endl<< " minimum (signed) int value:\t"<<INT_MIN<<endl<< " maximum (signed) int value:\t"<<INT_MAX<<endl<< " maximum unsigned int value:\t"<<UINT_MAX<<endl<< " minimum (signed) long value:\t"<<LONG_MIN<<endl<< " maximum (signed) long value:\t "<<LONG_MAX<<endl<< " minimum (signed) int64 value:\t"<<_I64_MIN<<endl<< " maximum (signed) int64 value:\t"<<_I64_MAX<<endl<< " maximum unsigned int64 value:\t"<<_UI64_MAX<<endl<< " minimum float number:\t\t"<<FLT_MIN<<endl<< " maximum float number:\t\t"<<FLT_MAX<<endl<< " minimum double number:\t"<<DBL_MIN<<endl<< " maximum double number:\t"<<DBL_MAX<<endl<< " minimum long double number:\t"<<LDBL_MIN<<endl<< " maximum long double number:\t"<<LDBL_MAX<<endl; system("pause");return(0); }

ENUM Progettare l’enumerazione punti cardinali. #include <iostream> #include <string> using namespace std; int main (void) { // nuovo tipo: Nord = 1 in modo che i successivi siano 2, 3 e 4 enum PC { Nord = 1, Sud, Est, Ovest }; string nome[] = {"Nord", "Sud", "Est", "Ovest" }; // variabile di tipo PC PC direzione = Sud; system("cls"); cout<<"Direzione di partenza:\t"<<nome[direzione-1]<<endl; direzione = Nord; cout<<"Direzione cambiata:\t"<<nome[direzione-1]<<endl; system("pause");return(0);

Page 77: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 76 di 299

}

Funzioni Il C++ è un linguaggio fortemente e staticamente tipizzato, in C è comune, ad esempio, usare funzioni che operano per ellissi, ovverosia con parametri di numero e tipo variabile. Il risultato dell’esecuzione dipende dal valore e soprattutto dal tipo delle variabili. In questo caso si hanno errori: parametri giusti ma tipo sbagliato. #include <stdio.h> #include <stdlib.h> int main(void) { system("cls"); printf("%s %s!", Ciao, mondo); system("pause"); return(0); }

In questo caso si ha un’eccezione non gestita: tipo sbagliato ma parametri corretti. L’errore si rivela non a compile-time ma a run-time. #include <stdio.h> #include <stdlib.h> int main(void) { int Ciao = 1; char mondo = 2; system("cls"); printf("%s %s!", Ciao, mondo); system("pause"); return(0); }

Page 78: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 77 di 299

In questo caso si ha un’eccezione non gestita: tipo corretto ma parametri sbagliati, ne manca uno. #include <stdio.h> #include <stdlib.h> int main(void) { system("cls"); printf("%s %s\n", "Ciao, mondo"); system("pause"); return(0); }

In questo caso va tutto bene. #include <stdio.h> #include <stdlib.h> int main(void) { const char* ciao = "Ciao"; const char* mondo = "mondo"; system("cls"); printf("%s %s\n", ciao, mondo); system("pause"); return(0); } È per questo che il C++ ha un sistema di stream, perfettamente sicuro rispetto ai tipi. Per esempio, la stringa di formattazione trae vantaggio dall’overloading degli operatori. boost::format Ma l’ellissi non è che una delle operazioni volte ad aggirare i controlli del compilatore. Anche alcuni esempi di upcasting su tipi non polimorfi sottraggono informazioni al compilatore e alcuni esempi di type punning come questi. union Variant { int intero; char* stringa; }; void* tipoGenerico = reinterpret_cast<void*>(x); Entrambi i casi rendono impotente il compilatore di fronte a errori. Quando si usa la union ci si può solo fidare che sta leggendo dal campo corretto e quando si ripristina il casting da void*, c’è solo da sperare di usare il tipo originale di x, qualunque esso sia. Tipi particolari di funzioni Il C++ oltre alle funzioni “classiche” mette a disposizione del programmatore quattro strumenti più flessibili.

Page 79: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 78 di 299

1. Parametri di una funzione con valori di default È possibile costruire funzioni con parametri formali che possono essere inizializzati al momento della dichiarazione con valori di default predefiniti. L’elenco dei possibili parametri formali con valori di default deve obbligatoriamente seguire quello dei parametri formali tradizionali. È consigliabile mettere i valori di default nella dichiarazione del prototipo. #include <iostream> using namespace std; void calcola (int a, int b=0,int c=0); int main (void) { system("cls"); calcola(1,2,3); calcola(1,2); calcola(1); system("pause");return(0); } void calcola (int a, int b, int c) { cout<<a<<" "<<b<<" "<<c<<endl; return; }

2. Overloading di funzioni È l’uso dello stesso nome per funzioni diverse ma il programmatore è costretto comunque a implementare tante funzioni quante ne servono, è poi compito del compilatore riconoscere, in base al tipo dei parametri, quale tra esse dev’essere richiamata di volta in volta. La scelta del compilatore è fatta in base al risultato del confronto tra i tipi dei parametri attuali e quelli dei corrispondenti parametri formali. Esempio, elementi simmetrici dell’insieme dei caratteri maiuscoli e dei numeri naturali. Simmetrico di A è Z, di B è Y, simmetrico di 0 è 65535, di 1 è 65534. #include <iostream> using namespace std; char simmetrico(char car); unsigned int simmetrico(unsigned int num); int main (void) { char c; unsigned int n=0; system("cls"); cout<<"Inserisci un carattere in maiuscolo:\t"; cin>>c; cout<<"Simmetrico di "<<c<<" e'\t\t\t"<<simmetrico(c); cout<<"\nInserisci un numero tra 0 e 65535:\t"; cin>>n; cout<<"Simmetrico di "<<n<<" e'\t\t\t"<<simmetrico(n)<<endl; system("pause");return(0); } char simmetrico(char car)

Page 80: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 79 di 299

{ return ('Z'-(car - 'A')); } unsigned int simmetrico(unsigned int num) { return (65535-num); }

Esempio, progettare tre metodi aventi lo stesso nome e parametri con tipo di dato diverso, grazie all’overloading ogni volta sarà chiamata la funzione giusta a seconda del tipo. #include <iostream> using namespace std; int somma(int a,int b) { return(a+b);} // versione per i long long somma(long c,long d) { return(c+d);} // versione per i float float somma(float e,float f) { return(e+f);} int main(void) { int mioint1 = 15, mioint2 = 50; long miolong1 = 150000, miolong2 = 1666000; float miofloat1 = 3.02, miofloat2 = 32.24; system ("cls"); // ora sommiamo coppie di variabili di 3 tipi diversi usando sempre somma cout<< mioint1 << "\t+ " << mioint2 << "\t\t= "; cout << somma(mioint1,mioint2) << endl; cout << miolong1 << "\t+ " << miolong2 << "\t= "; cout << somma(miolong1,miolong2) << endl; cout << miofloat1 << "\t+ " << miofloat2 << "\t\t= "; cout << somma(miofloat1,miofloat2) << endl; system("pause");return (0); }

3. Funzioni con la lista dei parametri formali di lunghezza variabile A volte può capitare di dover progettare delle funzioni in cui è impossibile stabilire a priori il tipo e il numero dei parametri che possono essere passati alla funzione stessa all’atto della chiamata, per esempio la printf. Il C++, per risolvere questo problema, mette a disposizione la sintassi dei tre puntini di sospensione (…) che permette di costruire funzioni con un numero variabile di parametri. All’atto della chiamata la funzione deve contenere oltre ai parametri attuali fissi, anche l’elenco esplicito di tutti i parametri attuali che formano la lista definita dalla sintassi dei tre puntini. Le regole per progettare questo tipo di funzioni sono le seguenti. 1. Includere il file d’intestazione #include <stdarg.h>. 2. Definire il prototipo della funzione.

Page 81: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 80 di 299

3. Definire nel corpo della funzione un identificatore di tipo predefinito: va_list usato per scandire la lista di lunghezza variabile.

4. Usare la macro di sistema: void va_start(identificatore_lista,identificatore_ultimo); per definire il corretto punto d’inizio per la scansione.

5. Usare la macro di sistema: void va_arg(identificatore_lista,tip_parametro); per accedere ordinatamente ai singoli elementi della lista.

6. Usare la macro di sistema: void va_end(identificatore_lista); per chiudere la scansione della lista.

Esempio, calcolare la media di un insieme di valori. #include <iostream> #include <stdarg.h> using namespace std; int media( int primo, ... ); int main( void ) { system("cls"); // chiama la funzione con 3 parametri -1 e' usato come terminatore cout<<"La media vale: "<<media( 2, 3, 4, -1 )<<endl; // chiama la funzione con 4 parametri cout<<"La media vale: "<<media( 5, 7, 9, 11, -1 )<<endl; /* chiama la funzione con -1 il terminatore */ cout<<"La media vale: "<<media( -1 )<<endl; system("pause");return(0); } int media( int primo, ... ) { int count = 0, sum = 0, i = primo; va_list marker; va_start( marker, primo ); while( i != -1 ) { sum += i; count++; i = va_arg( marker, int); } va_end( marker ); return( sum ? (sum / count) : 0 ); }

4. Funzioni modello - template Chiamate anche funzioni generiche perché sono definite in modo “generico”, in modo da poterle applicare a dati di tipo diverso. In pratica, una funzione generica definisce una famiglia di funzioni tutte con lo stesso comportamento ma che operano su parametri di tipo diverso. In una funzione generica il tipo di dati sul quale essa deve operare è passato come parametro. Infatti, in molti problemi capita di dover ricorrere ad una serie di funzioni in cui varia solo il tipo dei parametri ma non le operazioni che devono essere compiute su di essi, per esempio la ricerca del valore in insiemi int, float o char. Il C++ permette di progettare un unico modello di funzione in grado di adattarsi a parametri

Page 82: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 81 di 299

di tipo diverso. Sono una semplificazione sintattica e di scrittura, in pratica, i template sono delle strutture base che permettono poi di sfociare in generazioni di codice diverse tra loro. Lo scopo principale per il quale i template sono stati introdotti è la possibilità di gestire, con una singola definizione di funzione, diversi tipi di variabile, creando però una sola funzione. Sintassi. Template-declaration: template < template-argument-list > declaration emplate-argument-list: template-argument template-argument-list, template argument template-argument: type-argument argument-declaration type-argument: class identifier template-class-name: template-name <template-arg-list> template-arg-list: template-arg template-arg-list , template-arg template-arg: expression type-name Definendo il tipo della funzione come un template, in italiano modello, il compilatore si accontenta di una definizione di tipo <class T>, ovvero secondo il tipo di variabile che utilizzerà la funzione, la generazione del codice sarà effettuata dal compilatore in modo trasparente al programmatore e del tutto automatica. Esempio, applicazione con la #define. #include <iostream> using namespace std; #define massimo(a,b) (a>=b ? a:b) int main(void) { system("cls"); cout<<"Il numero maggiore vale: "<<massimo(2,7)<<endl; system("pause");return(0); }

Esempio, applicazione con la <class T>. #include <iostream> using namespace std; template <class T> T massimo (T x, T y) { return (x > y) ? x : y; }; int main(void)

Page 83: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 82 di 299

{ system("cls"); cout<<"Il numero maggiore vale: "<<massimo(2,7)<<endl; system("pause");return(0); } T è il nome simbolico per un tipo generico utilizzato per i parametri formali x e y, in altre parole T è solo un “segnaposto” che sarà sostituito con il tipo di dato reale passato al tempo della chiamata della funzione. È il tipo di parametri attuali che determina il tipo di dato sul quale la funzione generica dovrà operare. Funzioni inline Usare #define per definire delle macro è tipico in C. #include <iostream> using namespace std; #define ABS(n) (n) > 0 ? (n) : -(n) int main( void) { int n=0; system("cls"); cout << ABS(n--); cout<<endl; system("pause");return(0); }

Ci sono due problemi con l’uso delle macro in C++. 1. Una macro assomiglia ad una chiamata di funzione ma non sempre agisce come tale,

quindi può “nascondere” errori difficili da trovare. 2. Il preprocessore del C++ non ha il permesso di accedere a dati delle classi membro,

significa che le macro non possono essere usate come funzioni di classi membro. Per mantenere l’efficienza delle macro ma aggiungere la sicurezza e lo scope di classe di una vera funzione, il C++ progetta le funzioni inline. Le chiamate di funzioni sono molte lente, invece una funzione definita inline non è chiamata ma è invece sostituita nel testo, esattamente come se fosse una macro definita con #define, a differenza di una macro, restano però validi tutti i controlli sul tipo dei parametri. Lo svantaggio delle funzioni inline è uguale ad uno degli svantaggi delle macro, poiché le istruzioni sono sostituite ad ogni chiamata della funzione, c’è una discreta occupazione di spazio, sia durante la compilazione, sia nel codice eseguibile che è prodotto. Ogni funzione definita all’interno del corpo di una classe è automaticamente inline ma è possibile rendere una funzione inline facendo precedere la definizione di funzione dalla parola riservata inline. #include <iostream> using namespace std; const int a=10; inline int q (int i) { return (a+i*i); } inline double q (double f) { return (a+f*f); } int main (void)

Page 84: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 83 di 299

{ char nome[20],cognome[20]; system("cls"); cout<<"Inserisci il cognome e il nome: "; cin>>cognome>>ws>>nome; cout<<cognome<<" "<<nome; cout<<endl<<q(5)<<endl; cout<<q(2.3)<<endl; system("pause");return(0); }

Poiché è inline, sarà espansa nel codice, proprio come la macro. Ma a differenza della macro, sarà una funzione, sotto stretto controllo del compilatore. Il preprocessore dev’essere usato solo per il controllo condizionale della compilazione. C++ ha un’alternativa migliore: le funzioni template inline. #include <iostream> using namespace std; template<typename T> inline T ABS(T n) { return n > 0 ? n : -n; } int main( void) { int n=0; system("cls"); cout << ABS(n--)<<endl; system("pause");return(0); } Poiché la funzione è template, è valida per ogni tipo anche non primitivo, volendo, proprio come la macro.

GESTIONE DEGLI ERRORI Una buona strada da percorrere per rendere il proprio codice robusto, è quella di cercare di prevedere tutti i possibili errori che possono presentarsi in una certa situazione e scrivere del codice adatto a trattarli. Ma scrivere codice per la gestione di errori è una cosa noiosa e ripetitiva, inoltre rende il più delle volte il codice stesso difficile da leggere e capire. Un’eccezione è un oggetto, derivato in modo diretto o indiretto dalla classe System::Exception. Quando si verifica una eccezione, è creato un tale oggetto contenente, tra le altre, informazioni sull’errore che si è verificato. Eccezioni più specifiche possono, inoltre, derivare da classi particolari, ad esempio eccezioni che riguardano errori di I/O sono in genere derivate dalla classe IOException, da cui a loro volta derivano fra le altre FileNotFoundException o FileLoadException che riguardano ancora più specificatamente eccezioni nella gestione di file. Catturare le eccezioni Una volta che un’eccezione si è verificata, in altre parole è stata lanciata in un qualche punto dell’applicazione, è necessario catturarla e svolgere azioni di recupero.

Page 85: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 84 di 299

Per far ciò, le parti di codice in cui è prevedibile che una o più eccezioni si possano verificare, sono racchiuse nel costrutto try...catch. Un’eccezione può verificarsi in qualsiasi parte del codice e, ovviamente, scrivere un blocco d’istruzioni di trattamento dell’errore per ogni singola linea di codice sarebbe una strada impercorribile. All’interno di un blocco di codice possono essere presenti molte fonti di errori, quindi molte fonti di eccezioni. La struttura dei blocchi try…catch può essere estesa in maniera immediata per fare fronte all’esigenza di controllare tutte le possibili eccezioni in un blocco di codice: basta inserire tutte le clausole catch necessarie. Nel gestire più tipi di eccezioni, con più blocchi catch, è necessario prestare attenzione all’ordine dei tipi di eccezione. Infatti, è obbligatorio gestire per primi le eccezioni più specifiche, in altre parole che fanno parte di una catena di derivazione da System::Exception più lunga. Se non fosse così, infatti, l’eccezione potrebbe essere catturata da un catch precedente. Sintassi. try { // codice soggetto a eccezioni, quindi a monitoraggio senza trattamento dell’errore } catch (exception-declaration) { // gestione errore: codice eseguito quando exception-declaration si trova nel blocco try } catch (exception-declaration) { // codice per un altro tipo di eccezione } // lancia l’eccezione throw [expression] finally { // libera le risorse o svolge altre azioni Nella gestione delle eccezioni è possibile omettere sia il blocco catch sia il blocco finally ma non entrambi contemporaneamente. In particolare, la situazione in cui è presente il solo blocco finally è utilizzata per garantire che siano eseguite certe istruzioni prima di uscire da un metodo, ad esempio il blocco try potrebbe contenere più istruzioni return e quindi più punti di uscita. Quando s’incontra il primo dei return, l’applicazione prima di terminare il metodo passa dal blocco finally. Il blocco d’istruzioni preceduto dalla clausola try è un blocco istruzioni ordinario del C++, quindi per esso valgono tutte le regole di visibilità delle variabili. Per quanto riguarda il blocco preceduto dalla parola chiave catch, è da notare il fatto che in esso è possibile definire un particolare identificatore che rappresenterà l’oggetto “eccezione” all’interno del blocco. Questo oggetto è di tipo tipo eccezione e può essere, ad esempio, di uno qualsiasi dei tipi predefiniti del C++ ma, più verosimilmente, sarà un’istanza di una classe creata apposta per contenere informazioni relative al tipo di errore che si è verificato. Un’eccezione può essere sollevata tramite throw direttamente all’interno di un blocco try oppure all’interno di una funzione chiamata dal blocco try. Se il tipo specificato in un costrutto catch combacia con il valore “lanciato” da throw, sarà eseguito il codice relativo a quel costrutto catch e tutti gli altri catch saranno ignorati. Se si lancia un’eccezione non gestita, in altre parole per la quale non si trova alcun tipo corrispondente in un costrutto catch, sarà provocata la chiamata al metodo terminate che a sua volta chiama abort per la terminazione dell’applicazione.

Page 86: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 85 di 299

Lanciare le eccezioni Essendo una classe come le altre, da Exception è possibile derivare le proprie eccezioni personalizzare dalle proprie applicazioni. È bene lanciare un’eccezione solo quando l’applicazione si trova in una situazione inaspettata o imprevista, in quanto l’elaborazione di tali istruzioni è più pesante di un normale flusso di esecuzione. In parole povere, se in un dato punto del codice è eseguita una divisione e se può capitare che il valore per cui dividere sia 0, è meglio controllarlo, piuttosto che eseguire la divisione e lanciare un’eccezione. Il meccanismo per cui un’eccezione è generata, è chiamato throwing. L’istruzione throw ha la funzione di generare e lanciare un’eccezione del tipo specificato e se tale throw è eseguito da una parte di codice eseguita all’interno di un blocco try, entra in funzione la cattura dell’eccezione, sempre che il tipo di eccezione sia stato previsto in un blocco catch. È possibile utilizzare l’istuzione throw anche al di fuori di un blocco try, in pratica in una parte qualunque di codice, ad esempio in un metodo. In questa maniera, al verificarsi di un’eccezione, non trovando il blocco catch per gestirla, il CLR risalirà lo stack delle chiamate fino a trovarne uno o al limite fino a quando sarà restituito un messaggio di eccezione non gestita. Esempio, dividere due numeri inseriti da tastiera. #include <iostream> using namespace std; double dividi (int divid, int divis); int main( void) { int a=0,b=0; system("cls"); cout<<"Inserisci il dividendo:\t"; cin>>a; cout<<"Inserisci il divisore:\t"; cin>>b; try { cout<<"Risultato della divisione = "<<dividi(a,b)<<endl; } catch (char *s) { cout<<"Eccezione: divisione per zero!"<<endl; } catch (int i) { cout<<"Eccezione: numero inserito negativo: "<<i<<endl; } system("pause");return(0); } double dividi (int divid, int divis) { if (divis==0) throw "Divisione per zero!"; if (divid<0) throw divid; return (divid/divis); }

Page 87: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 86 di 299

Nell’esempio sono presenti due costrutti throw. Il primo lancia un’eccezione di tipo char *. Il secondo lancia un’eccezione di tipo int. Le due eccezioni sono catturate dai due rispettivi costrutti catch. Quando è lanciata un’eccezione, il controllo passa al costrutto catch e il blocco try ha termine, nell’esempio l’istruzione return della funzione dividi, se si verifica una divisione per 0, non sarà mai eseguita. Il costrutto throw è stato inserito nella funzione dividi richiamata dal blocco try. Se si fosse usato il seguente costrutto, al posto di catch (int i). catch (double i) { cout<<"Eccezione: numero inserito negativo: "<<i<<endl; } Con il seguente input per il dividendo di (-6) e per il divisore di (2). Il messaggio "Eccezione: numero inserito negativo: " non è visualizzato, in quanto l’eccezione è sollevata da un costrutto throw con parametro int e non double.

A volte è comodo poter catturare le eccezioni di tutti i tipi in un solo blocco catch senza doverne fare uno per ogni tipo di eccezione. Si deve usare la seguente sintassi. catch (…) { // codice eseguito quando exception-declaration si trova nel blocco try } Il simbolo (...) è parte del vocabolario C++ e sta a indicare una generica lista di parametri. Esempio, dividere due numeri inseriti da tastiera. #include <iostream> using namespace std; double dividi (int divid, int divis); int main( void) { int a=0,b=0; system("cls"); cout<<"Inserisci il dividendo:\t"; cin>>a; cout<<"Inserisci il divisore:\t"; cin>>b;

Page 88: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 87 di 299

try { cout<<"Risultato della divisione = "<<dividi(a,b)<<endl; } catch (...) { cout<<"Eccezione!"<<endl; } system("pause");return(0); } double dividi (int divid, int divis) { if (divis==0) throw "Divisione per zero!"; if (divid<0) throw divid; return (divid/divis); }

Quando una funzione fornita dalle librerie standard di C++ lancia un’eccezione, questa sarà un oggetto derivato dalla superclasse Exception. Per catturare eccezioni di questo tipo occorre includere il file header <exception>. Per catturare una qualsiasi eccezione di questo tipo si utilizza il codice seguente. catch (Exception e) { cout<<"Eccezione: dalle funzioni standard!"<<endl; } try annidati Un’eccezione generata all’interno di un blocco try, nel caso in cui il corrispondente catch non la gestisca, sarà propagata al blocco try esterno ed eventualmente gestita dai blocchi catch di questo e così via. Eccezioni personalizzate Ogni programmatore può creare le proprie classi di eccezioni per rappresentare meglio una situazione che può generarsi nella propria applicazione e quindi per rispondere meglio alle esigenze di gestione delle stessa. Per implementare le proprie eccezioni, si deve derivare una classe dalla System::Exception, ad esempio se si tratta di un’eccezione che riguarda l’I/O sarebbe una buona scelta derivarla dalla IOException. #include <iostream> #include <exception> #include <stdlib.h> using namespace std; using namespace System; ref class EccezionePersonalizzata: Exception { }; int main(void)

Page 89: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 88 di 299

{ system("cls"); try { throw gcnew EccezionePersonalizzata(); } catch(EccezionePersonalizzata^ ep) { Console::WriteLine(ep->Message); } system("pause");return (0); }

STRING Per un programmatore C, una stringa altro non è che un array di caratteri che termina con un carattere speciale chiamato “terminatore di stringa”, ('\0'). Questa rappresentazione del concetto di stringa, presenta però notevoli problemi, ad esempio la necessità di ridimensionare a mano la dimensione dell’array qualora si rendesse necessario ingrandire la stringa, oppure l’attenzione da parte del programmatore a non scrivere su zone di memoria ormai deallocate. #include <iostream> #include <string> using namespace std; int main (void) { // dichiarazione e inizializzazione string s1 ("Ciao, mondo!"); string s2 = "E se domani..."; // dichiarazioen e inizializzazione di 10 caratteri U string s3 (10,'U'); // stringhe vuote string s4,s5; // due modalità di assegnazione s4=s1; s5.assign(s2); system("cls"); cout<<"Contenuto delle stringhe s1, s2, s3, s4 e s5"<<endl<< s1<<endl<<s2<<endl<<s3<<endl<<s4<<endl<<s5<<endl; // concatenazione di stringhe con + e += string s6(s1+" bello"); s1 += " bellino"; // append di una stringa a partire dall'indice 5 incluso fino a 7 incluso s4.append(s1,5,3); s4[s2.length ()-1]='u'; s5.append(" bellino"); cout<<"\nContenuto delle stringhe s1, s2, s3, s4, s5 e s6"<<endl<< s1<<endl<<s2<<endl<<s3<<endl<<s4<<endl<<s5<<endl<<s6<<endl; system("pause");return(0); }

Page 90: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 89 di 299

Esempio, verificare se due stringhe sono o meno l’anagramma l’una dell’altra, l’algoritmo sort ordina i caratteri in modo crescente, in modo da ottenere una “firma” di confronto. #include <iostream> #include <string> #include <algorithm> using namespace std; bool anagrammi(const string& parola1, const string& parola2); int main(void) { string p1,p2; system("cls"); cout<<"Inserisci la prima parola:\t"; cin>>p1; cout<<"Inserisci la seconda parola:\t"; cin>>p2; if (anagrammi(p1, p2)) cout << "I due termini sono anagrammi."<<endl; else cout << "Riprova, i due termini non sono anagrammi."<<endl; system("pause");return(0); } bool anagrammi(const string& parola1, const string& parola2) { string firma1(parola1), firma2(parola2); sort(firma1.begin(), firma1.end()); sort(firma2.begin(), firma2.end()); return (firma1 == firma2); }

basic_string implementa tre funzioni per la conversione dei dati. 1. data per accedere ad un array di caratteri. 2. c_str una stringa con terminatore nullo, per chiamare le funzioni di libreria C. 3. copy copia l’array in un buffer già creato senza terminatore. Esempio. string str("Ciao, mondo!"); char stringa[20]; // buffer già esistente size_t lenStringa = str.copy(stringa, 5); // copia i primi 5 caratteri di str in stringa

Page 91: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 90 di 299

stringa[lenStringa] = '\0'; // aggiunge terminatore alla fine cout << stringa; // Ciao // stringa C-like cout << strlen(str.c_str()); // 12 // stringa non terminata cout << str.data(); // crash basic_string permette di reperire sequenze di caratteri all’interno della stringa, ovvero delle sotto stringhe. Esempio, funzione substr(partenza, numerocaratteri). string str("Ciao, mondo!"); cout << str.substr(6, 5); string str("www.ubertini.it"); cout << str.find("."); // 3 cout << str.rfind("."); // 12 cout << str.find_first_not_of("w."); // 4 cout << str.find_last_of("z"); // npos Nell’ultima istruzione si cerca l’ultima (z) presente nella stringa str, solo che non si trova, il valore restituito sarà la costante npos (no position) che è impostata sul valore limite per il buffer di una stringa (-1 dato che un size_t è senza segno) e pertanto usarlo come indice è un errore che genera un’eccezione di tipo range_error. string str("www.ubertini.it"); cout << str[str.find_first_not_of("w.")]; // u cout << str[str.find_last_of("z")]; Esempio, funzione replace(inizio, numerocaratteri, sostituzione). string str("www.ubertini.it"); size_t inizio = str.find(".") + 1; size_t fine = str.rfind("."); size_t n = fine - inizio; str.replace(inizio, n, "andreasperelli"); cout << str; //www.andreasperelli.it In basic_string manca del tutto il supporto per alcune operazioni fondamentali, come la conversione in maiuscolo o in minuscolo, l’eliminazione degli spazi e la ricerca avanzata di sotto stringhe. È possibile costruire facilmente delle funzioni che si occupino di farlo ed è per questo motivo che è giusto che queste non siano inglobate in basic_string, seguendo la regola che vuole che si preferiscano le funzioni non membro non friend definite esternamente alla classe, ai metodi. Il problema è che queste funzioni non membro non friend non ci sono. Pavol Droba ha scritto un’implementazione di tutti gli algoritmi relativi alle stringhe che mancavano nella STL (Standard Template Library) e l’ha presentata alla comunità di Boost, www.boost.org, questa l’ha adottata inserendola nel file header <boost/algorithm/string.hpp>.

Page 92: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 91 di 299

Esempio, conversione da minuscolo a maiuscolo e viceversa usando C++ che definisce le funzioni toupper e tolower ma queste lavorano solo su singoli caratteri. #include <iostream> #include <string> #include <algorithm> using namespace std; template<typename Iterator> inline void to_upper(Iterator begin, Iterator end) { transform(begin, end, begin, toupper); } int main(void) { string str = "ciclismo"; system("cls"); to_upper(str.begin(), str.end()); cout << str<<endl; system("pause");return(0); }

Esempio, la funzione boost::to_upper e boost::to_lower è implementata allo stesso modo ma rende le cose più semplici, grazie ad una chiamata a boost::Range. Grazie a Range è possibile passare il contenitore, al posto dei due iteratori. #include <iostream> #include <boost/algorithm/string.hpp> using namespace std; using namespace boost; int main(void) { string str = "ciclismo"; system("cls"); to_upper(str); cout << str<<endl; system("pause");return(0); } Se si ha un effettivo bisogno d’indicare una parte della stringa, si può sempre creare un iterator_range che va costruito prima della chiamata, passando i due estremi come parametri del suo costruttore. Per evitare di dover specificare sempre boost:: è bene dichiarare, prima dell’uso, using namespace boost. #include <iostream> #include <boost/algorithm/string.hpp> using namespace std; using namespace boost; int main(void) { string str = "www.ubertini.it"; system("cls"); size_t inizio = str.find(".") + 1; size_t fine = str.rfind("."); iterator_range<string::iterator> ubertini(str.begin() + inizio,str.begin() + fine);

Page 93: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 92 di 299

to_upper(ubertini); cout << str<<endl; system("pause");return(0); }

Molti algoritmi esposti dalla libreria boost::string_algo sono forniti in due forme: in place e per copia. Si pensi ad esempio a std::replace che sostituisce gli elementi compresi fra due iteratori. Esiste anche std::replace_copy che non tocca l’originale ma crea una sequenza con gli elementi già sostituiti. La libreria Boost segue la stessa convenzione. boost::to_upper converte una stringa in maiuscolo. boost::to_upper_copy copia la stringa passata in una nuova, trasformata in maiuscolo. Esistono due versioni di to_upper_copy, a seconda delle esigenze, la prima è quella facile che prende una stringa e ne restituisce una copia trasformata. #include <iostream> #include <boost/algorithm/string/case_conv.hpp> using namespace std; using namespace boost; int main(void) { string str = "ciclismo"; system("cls"); string str2(to_upper_copy(str)); cout << str2<<endl; system("pause");return(0); } Se si desidera effettuare una copia di un raggio ridotto di una stringa di partenza si usa la versione to_upper_copy(IteratoreCopia, Range Originale), dove IteratoreCopia è un inseritore. #include <iostream> #include <boost/algorithm/string.hpp> using namespace std; using namespace boost; int main(void) { string str = "www.ubertini.it", str2; system("cls"); size_t inizio = str.find(".") + 1; to_upper_copy(back_inserter(str2),"www.ubertini.it"); cout << str2<<endl; system("pause");return(0); }

Nella STL manca una funzione destinata al trimming, infatti capita di trovare delle stringhe delimitate da spazi.

Page 94: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 93 di 299

È possibile progettare una funzione di trimming usando find_first_not_of per trovare il primo carattere sinistro che non sia uno spazio e find_last_not_of per il lato destro. boost::String_algo si occupa di questo, mediante le funzioni trim, trim_left, e trim_right. #include <iostream> #include <boost/algorithm/string.hpp> using namespace std; using namespace boost; int main(void) { string str = " www.ubertini.it "; string str2(trim_left_copy(str)); string str3(trim_right_copy(str)); system("cls"); cout << str<<endl; cout << str2<<endl; cout << str3<<endl; system("pause");return(0); }

A volte, a dare fastidio non sono soltanto gli spazi ma anche altri caratteri: tabulazioni, punti, asterischi e zeri. Le funzioni di trimming trim_left_if, trim_right_if e trim_if di specificare un predicato per l’identificazione dei caratteri da tagliare. Esempio, dato un indirizzo relativo pieno di punti e (/) si vuole trasformarlo in uno assoluto, quindi eliminare ogni carattere (.) o (/) che compaia agli estremi della stringa. #include <iostream> #include <boost/algorithm/string.hpp> using namespace std; using namespace boost; int main(void) { string str("../../../ciclismo/"); system("cls"); string str2 = "C:/" + trim_copy_if(str, is_any_of("./")); cout << str2<<endl; system("pause");return(0); }

La funzione is_any_of è un predicato restituisce true se un carattere corrisponde a quelli indicati. #include <iostream> #include <boost/algorithm/string.hpp> using namespace std; using namespace boost;

Page 95: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 94 di 299

int main(void) { system("cls"); cout << all("Ubertini 777", is_digit())<<endl; // falso cout << all("777", is_digit())<<endl; //vero cout << all("Ubertinio 777", is_alpha())<<endl; // falso cout << all("Ubertini", is_alpha())<<endl; // vero cout << all("accada", is_from_range('a', 'd'))<<endl; // vero cout << all("avvenga", is_from_range('a', 'd'))<<endl; // falso system("pause");return(0); }

Esempio, progettare la funzione GameOver che si occupa di mostrare a video il messaggio seguente. Complimenti, Pinco Palla! Hai finito il gioco in 37.5 secondi, con un punteggio di 750000. GameOver ha due compiti distinti. 1. Costruire la stringa a partire dai dati della classe. 2. Richiamare una funzione che la mostri in una finestra di dialogo. Poiché ogni funzione deve avere una sola responsabilità, si è strutturata la classe nel modo seguente. class Partita { public: void GameOver() { string messaggio(CreaStringaGameOver()); MostraMessaggio(messaggio); } // resto dell’interfaccia private: string CreaStringaGameOver(); string nome_; long punteggio_; float secondi_; }; Per eseguire compiti di questo tipo si usa la classe stringstream, un typedef, in realtà di basic_stringstream<char> è definita nel file header <sstream> e rappresenta l’astrazione di una stringa. Usandola è possibile inserire informazioni in una stringa trattandola come uno stream qualsiasi, per poi ottenerne una copia grazie alla funzione str. Implementazione di CreaStringaGameOver, ottenuta con stringstream. string Partita::CreaStringaGameOver() { // dichiarazione stringstream messaggio;

Page 96: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 95 di 299

// inserimento messaggio << "Complimenti, "<< nome_ << "!"<<endl<< "Hai finito il gioco in "<< secondi_ << " secondi"<< " con un punteggio di "<< punteggio_ << "!"<<endl; // conversione a stringa return messaggio.str(); } Vantaggi di stringstream. 1. Evita il pericolo dei buffer overflow, dal momento che la stringa alla quale lo stream fa

riferimento è ridimensionata automaticamente. 2. È una soluzione sicura rispetto ai tipi, dal momento che il tipo dei parametri concatenati

non è richiesto al programmatore ma controllato subito e staticamente dal compilatore. 3. Permette di concatenare molti tipi diversi, attraverso una sintassi omogenea, oltre a

variabili e costanti di tipo primitivo, infatti, possono essere concatenati gli oggetti appartenenti a qualunque classe per la quale sia stato previsto un operatore d’inserimento in stream e, transitivamente, possono essere concatenati anche oggetti implicitamente convertibili in un tipo per il quale sia stato previsto un operatore d’inserimento in stream.

4. Fa parte della libreria standard e quindi oltre ad essere portabile e facile da mantenere, ha rapporti con il resto delle classi della STL, se il framework sottostante decidesse, ad esempio, di supportare in una nuova versione i caratteri estesi, invece di quelli normali, dovremmo solo ritoccare i tipi in wstring e wstringstream i typedef che stanno per basic_string<wchar_t> e basic_stringstream<wchar_t>, senza cambiare una riga del resto del codice.

5. Permette l’uso dei manipolatori standard, consentendo così la formattazione della stringa in modo facilmente comprensibile.

6. Può essere usato per estrarre informazioni da una stringa, sfruttando il fatto che il comportamento di uno stream è bidirezionale.

Esempio, una finestra di dialogo contiene dei campi testuali, il cui contenuto è di tipo string, in uno di questi si richiede l’anno di nascita per assicurarsi che l’utente sia maggiorenne. int CalcolaEtà(const string& txtAnnoDiNascita) { stringstream tmp(txtAnnoDiNascita); int annoDiNascita; tmp >> annoDiNascita; return (AnnoAttuale() - annoDiNascita); } stringstream può essere usato nelle due direzioni: si possono estrarre informazioni da una stringa, processarle e reinserirle, anche nello stesso stringstream di partenza. Il campo txtAnnoNascita può contenere valori non numerici, stringstream permette di sapere se un’operazione d’inserimento o estrazione è andata a buon fine, interrogando lo stato dello stream. Di questo stato sono significativi solo quattro bit che corrispondono a delle costanti di tipo ios_base::iostate.

Costante Funzione Cosa significa se è impostato

goodbit good() Lo stream è a posto e nessuno degli altri tre bit è impostato.

eofbit eof() Lo stream ha incontrato il carattere EOF.

failbit fail() Un’operazione di lettura o scrittura non è andata a buon fine.

badbit bad() È avvenuto un errore grave e lo stream è in uno stato incoerente.

Page 97: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 96 di 299

Per sfruttare queste informazioni, il metodo più semplice è utilizzare lo stream stesso come fosse un’espressione, grazie all’operatore di conversione a bool, lo stream restituisce il valore di goodbit. if (!(tmp >> annoDiNascita)) { // goodbit non è impostato, l’estrazione non è andata a buon fine } Allungando il codice sorgente e richiamando una funzione esplicita si ottiene codice più facile da capire. int CalcolaEta(const string& txtAnnoDiNascita) { stringstream tmp(txtAnnoDiNascita); int annoDiNascita; tmp >> annoDiNascita; if (!tmp.good()) { throw ("Sono stati inseriti dati errati"); } return (AnnoAttuale() - annoDiNascita); } Una procedura più veloce, soprattutto quando si effettuano molte estrazioni successive, è quella di chiedere allo stream stesso di generare un’eccezione automaticamente, in caso di errore nel parsing. La funzione exceptions (stato) permette di specificare in quale stato lo stream invocherà un’eccezione, si possono indicare più stati, componendoli con l’operatore (|). Esempio, leggere tre numeri dallo stesso stream: se un inserimento fallisce (failbit) o lo stream incontra un errore indefinito (badbit), è lanciata un’eccezione di tipo ios_base::failure. stringstream tmp(txtAnnoDiNascita); tmp.exceptions(ios_base::failbit | ios_base::badbit); try { long indirizzo, telefono, partitaIva; tmp >> indirizzo >> telefono >> partitaIva; } catch(ios_base::failure&) { // gestisce l’eccezione in caso di errore nel parsing CreaFinestraDiDialogo("Sono stati inseriti dati errati"); } Svantaggi di stringstream. 1. Costringe a inserimenti a cascata poco leggibili che richiedono al programmatore una

certa disciplina nella formattazione del codice. 2. Non fornisce prestazioni ottimali in termini di velocità, dal momento che costringe il

programmatore a usare almeno una variabile temporanea, inoltre, usando memoria dinamica, può portare a riallocazioni interne.

3. Costringe a mescolare il testo di formato con i dati da inserire nella stringa, il che è una delle cause dei problemi di leggibilità e rende molto difficile l’internazionalizzazione delle risorse.

4. È una soluzione prolissa, si è costretti a scrivere tutto un ciclo di dichiarazione, inserimento, conversione solo per trasformare un intero in una stringa.

Page 98: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 97 di 299

Usando stringstream come estrattore, un programmatore C avrà pensato: posso fare lo stesso usando funzioni come atoi, senza dover dichiarare buffer. int CalcolaEta(const string& txtAnnoDiNascita) { return AnnoAttuale() - atoi(txtAnnoDiNascita.c_str()); } Quando si programma in C++ l’approccio C diretto ha sempre gravi svantaggi. Senza sovraccaricamento, infatti, la libreria C è costretta a trattare atoi, atol e atof come funzioni distinte, obbligando il programmatore finale a scegliere e a cambiare le chiamate nel caso in cui cambi il tipo. Allo stesso modo, le possibilità di conversione sono molto limitate e non esiste una soluzione C generica e sicura. Se poi atoi fallisce nella lettura, si entra nel regno del comportamento indefinito. Anche con soluzioni C più sicure come strtol è difficile orizzontarsi in caso di errore. Infine, le funzioni come atoi sono unidirezionali e non permettono di passare, ad esempio, da un intero ad una stringa. La funzione MFC CString::FormatMessage, appoggiandosi sull’API FormatMessage di KERNEL32, offre molte possibilità, fra le quali i segnaposti posizionali, alla boost::format. Rimangono i problemi d’insicurezza rispetto ai tipi. L’idea di avere una funzione semplice e diretta per le conversioni facili è ottima: va solo implementata in maniera C++. La libreria boost offre la soluzione scritta da Kevlin Henney: lexical_cast, è possibile convertire un qualsiasi tipo Sorgente che definisca un operator<<(ostream&, Sorgente&), in qualsiasi tipo Destinazione che definisca un operator>> (istream&, Destinazione&), con la stessa sintassi degli operatori di conversione standard. lexical_cast<Destinazione>(sorgente); Esempio, riscrivere il codice precedente combinando la semplicità di atoi e la sicurezza di stringstream. int CalcolaEta(const string& txtAnnoDiNascita) { return (AnnoAttuale - lexical_cast<int>(txtAnnoDiNascita)); } In caso di errore nel parsing, lexical_cast lancia un’eccezione di tipo bad_cast. In questo modo, se lexical_cast fallisce, si potrà intervenire con un opportuno blocco try..catch. Ma in questo caso specifico, è ancor meglio lasciare l’incombenza della gestione dell’eccezione al chiamante. In questo modo, infatti, è possibile eseguire molte validazioni, usando un unico blocco try..catch. int DialogInformazioniSulGiocatore::Convalida() { try { int età = CalcolaEta(); int livello = CalcolaLivello(); // altre eventuali valutazioni } catch() { throw ("Sono stati inseriti dati errati "); } }

Page 99: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 98 di 299

Poiché lexical_cast è una funzione di conversione generica, può anche funzionare in maniera inversa, trasformando, ad esempio, un intero in una stringa. Esempio, CreaStringaGameOver implementata con lexical_cast, sfrutta il collegamento fra oggetti string. string Partita::CreaStringaGameOver() { return "Complimenti " + nome_ + "!\nHai finito il gioco in " + lexical_cast<string>(secondi_) + " secondi, " + "con un punteggio di " + lexical_cast<string>(punteggio_); } lexical_cast è implementato usando gli stream, quindi, tutte le considerazioni di ordine prestazionale viste per stringstream si applicano anche a lexical_cast. Inoltre, non è possibile utilizzare formattatori e manipolatori con lexical_cast e la scrittura, per frasi complesse, soffre di problemi di leggibilità ancor maggiori di stringstream, dovuti alla continua ripetizione di: lexical_cast<tipo>. I programmatori C++ di derivazione C avranno pensato alla seguente implementazione. string Partita::CreaStringaGameOver() { // creazione buffer const int DimBuffer = 255; char buffer[DimBuffer]; // riempimento static const char* messaggio = "Complimenti, %s!\n Hai finito il gioco in %.2f secondi con un punteggio di %d\n"; sprintf(buffer, messaggio, nome_.c_str(), secondi_,punteggio_); // costruzione implicita std::string(char[]) return (buffer); } Nella prima fase è allocato un buffer, la cui memoria è riservata direttamente sullo stack. Questo è vantaggioso sotto il profilo delle prestazioni, dal momento che il buffer è allocato una volta sola e resta sotto diretto dominio del programmatore. sprintf è una funzione di libreria C che lavora con il sistema dell’ellissi: problemi di sicurezza rispetto ai tipi. sprintf(buffer, "%s", variabile); Non si può sapere se l’operazione andrà a buon fine o meno, perché non si conosce il tipo di variabile, se fosse un char[] allora funzionerebbe tutto bene. Se, invece, fosse un qualsiasi altro tipo, invece, il tipo richiesto nel messaggio e quello della variabile non corrisponderebbero più. int variabile = 7; sprintf(buffer, "%s", variabile); Il compilatore non si accorge dal momento che i parametri passati per ellissi non sono considerati a compile-time ma solo dinamicamente, durante il run-time, quindi sprintf non è sicuro rispetto ai tipi e funziona solo con tipi primitivi e non con tipi definiti dall’utente. stringstream funziona con qualunque tipo preveda un operatore d’inserimento in stream. #include <iostream> using namespace std; const int N = 7;

Page 100: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 99 di 299

int main(void) { char buffer[N]; sprintf(buffer, "123456789\n"); system("cls"); cout << buffer; system("pause"); return(0); }

Buffer overflow: si sta tentando d’inserire una stringa di molti caratteri in un buffer di 7. Alcuni compilatori permettono un’estensione di sprintf chiamata snprintf o _snprintf che tronca il buffer in caso di overflow. #include <iostream> using namespace std; const int N = 7; int main(void) { char buffer[N]; _snprintf(buffer, 7, "123456789\n"); system("cls"); cout << buffer; system("pause");return(0); } Ma _snprintf non è una funzione C++ standard e il codice che dovesse usarlo smetterebbe di essere portabile, inoltre, il comportamento di _snprintf può variare a seconda delle implementazioni. Esempio, scrivere una classe specifica che permetta la formattazione della stringa, in modo sicuro rispetto ai tipi e agli overflow. Si utilizza boost::format che è una soluzione efficace e cross-platform, di accedere a format includendo il file header <boost/format.hpp>. Un format è un oggetto simile a stringstream che, però, permette di associare un testo di formato, nel costruttore. Una volta ottenuto un oggetto format, è possibile inviarlo in uno stream o convertirlo a

Page 101: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 100 di 299

stringa, con la funzione str. format saluto("Ciao, mondo!"); cout << saluto<<endl; string str = saluto.str(); Il testo di formato può contenere degli specificatori, proprio come sprintf. I parametri non saranno passati per ellissi ma tramite l’operatore (%), in un secondo tempo. string Partita::CreaStringaGameOver() { format messaggio ("Complimenti, %s!\nHai finito il gioco in %.2f secondi, con un punteggio di %d\n"); messaggio % nome_ % secondi_ % punteggio_; return (messaggio.str()); } Non c’è il rischio di buffer overflow, dal momento che non c’è alcun buffer. Se il tipo passato non corrispondesse a quello indicato nel testo di formato, non succede niente, infatti, gli specificatori sono messi soltanto per indicare possibili criteri di formattazione ma non hanno alcun valore per l’identificazione di tipo. Pertanto, boost::format è sicuro rispetto ai tipi e agli overflow. Se non servono formattazioni particolari, si può evitare di specificare i tipi. format messaggio ("Complimenti, %1%!\nHai finito il gioco in %2% secondi, con un punteggio di %3%\n") % nome_ % secondi_ % punteggio; Lo specificatore %1%, infatti, identifica il primo dei parametri passati. È per questo che il C++ è dotato di una libreria standard per la manipolazione delle stringhe attraverso un nuovo tipo: string. std::string (o basic_string<T> Evita ogni genere di problema permettendo operazioni di copia, assegnazione, ricerca, sostituzione e iterazione sotto forma di metodi, oltre a queste, si possono usare anche tutti gli algoritmi STL di <algorithm> e l’integrazione con le altre componenti della libreria standard come stringstream. In generale, evitare di usare e passare gli array: usare i vector oppure i boost::array. Non usare <cstdio> usare gli stream. In C++ un oggetto di tipo string ha due vantaggi rispetto al suo analogo in C. 1. È un tipo vero e proprio, con le sue proprietà e i suoi metodi, con tanto di funzioni che

ne fanno uso, ad esempio l’I/O con i classici operatori è già ridefinito per fare uso anche di oggetti di tipo string e contiene al proprio interno molte informazioni utili per il compilatore e il programmatore, ad esempio il numero dei caratteri della stringa, in altre parole le posizioni occupate all’interno del buffer dei dati, il numero di caratteri massimo che il buffer può contenere in un determinato momento, in altre parole la dimensione in byte, tale valore non è una costante ma varia in funzione del contenuto della stringa: qualora fosse necessario il buffer è ridimensionato automaticamente.

2. Una stringa non è un array di caratteri che termina con un “carattere di terminazione”: nessuna delle funzioni della libreria standard fa implicito riferimento all’esistenza di tale carattere né, quindi, il programmatore se ne deve curare.

È necessario includere il file header <string>, è possibile dichiarare una variabile di tipo string nel seguente modo.

Page 102: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 101 di 299

string s1; Questa stringa non contiene alcun carattere e la sua lunghezza è 0, è possibile inizializzare una stringa contestualmente alla sua dichiarazione. // inizializzazione con costruttore ad un parametro string s2("Sono la stringa numero 2!"); // inizializzazione da costante string s3 = "Io la numero 3!"; // s3bis è inizializzata con il contenuto di s3 string s3bis(s3); Tra le funzioni di libreria più importanti ci sono gli overload degli operatori più comuni, infatti, è possibile ad esempio concatenare due stringe utilizzando l’operatore (+). string s2 = "Stringa concatenata"; string s1_s2 = s1 + s2; In questo caso la concatenazione è avvenuta contestualmente ad un’inizializzazione ma è del tutto possibile concatenare una stringa ad un’altra stringa già esistente. s3 = s3 + s2; s3 += s2; Per stampare una stringa basta utilizzare l’overload dell’operatore di estrazione (<<). string s4 = "Prova di stampa"; cout << s4; Esempio. string s5 = "Ci"; string s6 = "ao!"; // stampo una stringa concatenata cout << (s5 + s6) << endl; // concateno i flussi di stampa! cout << s5 << s6 << endl; L’overload dell’operatore d’inserimento (>>) consente d’immettere da tastiera una stringa di lunghezza arbitraria. #include <iostream> #include <string> using namespace std; int main (void) { string nome; system("cls"); cout << "Ciao! Come ti chiami? "; cin >> nome; cout << "Ciao " << nome << "! Io sono HAL 9000"<<endl; system("pause");return(0); }

Page 103: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 102 di 299

In questo caso cin utilizza come terminatore di stringa il carattere blank. Per ottenere il nome completo bisogna utilizzare la funzione getline che inserisce nella stringa l’intera sequenza di caratteri battuta da tastiera prima di premere invio. getline(cin,nome);

Un altro overload è l’operatore di uguaglianza (==), utilizzandolo, infatti, è possibile testare in maniera facile ed elegante una condizione di uguaglianza tra due stringhe. #include <iostream> #include <string> using namespace std; void test(const string& a, const string& b); int main (void) { system("cls"); string s7 = "Ciao!"; string s8 = "Ciao!"; string s9 = "Hello!"; test(s7,s8); test(s7,s9); system("pause");return(0); } void test(const string& a, const string& b) { cout << "Le stringhe (" << a << ") e (" << b << ") "; if (a == b) cout << "\tSono uguali!"<<endl; else cout << "\tNon sono uguali!"<<endl; }

È possibile utilizzare anche l’overload dell’operatore di disuguaglianza (!=) e degli operatori relazionali (>,<,>=,<=). Le comparazioni che svolgono questi operatori sono di tipo lessicale, questo vuol dire che una stringa sarà valutata maggiore di un’altra stringa se il primo carattere, partendo da sinistra, per il quale le due stringhe differiscono è, in ordine alfabetico, dopo il corrispondente carattere nella seconda stringa. Esempio, date le seguenti stringhe. string casa = "casa"; string castello = "castello";

Page 104: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 103 di 299

Si può vedere che il primo carattere per cui esse differiscono è quello in posizione tre cominciando a contare da 0. Per la variabile casa questo carattere è a, mentre per castello questo carattere è t, questo vuol dire che la condizione seguente. (castello > casa) Restituirà il valore true, proprio perché la t è dopo la a nell’alfabeto. Dimensione Sfrutta i seguenti metodi. size dice di quanti caratteri è composta la stringa. capacity restituisce la massima lunghezza della stringa, prima che le sia allocata nuova

memoria. Esempio, verificare la fluttuazione delle dimensioni del buffer associato ad una stringa in funzione del suo contenuto. #include <iostream> #include <string> using namespace std; int main(void) { string s,c; float tot; system("cls"); cout << "Inserisci una stringa:\t"; cin >> c; s.append(c); cout << "Occupazione: "; cout << s.size() << " su " << s.capacity(); tot = ((float)s.size()/(float)s.capacity())*100; cout << "\t[" << tot << "%]" << endl; system("pause");return (0); }

La dimensione cambia all’improvviso, non appena si supera una certa soglia di riempimento. Si è fatto uso del casting esplicito per il calcolo della variabile tot: questo è stato necessario per via del fatto che le funzioni size e capacity restituiscono un intero e la divisione tra s.size() e s.capacity(), siccome è sempre vero che s.size()<s.capacity(), avrebbe sempre dato come risultato 0 per via del troncamento delle cifre decimali. La funzione append(string) prende la stringa su cui è invocata e le concatena la stringa passata come parametro. Per la gestione delle dimensioni di una stringa esiste anche la funzione seguente. void reserve(size_type _Count = 0 ) Permette di specificare la dimensione minima del buffer associato ad una stringa, mediante la specifica del parametro passatole. Risulta di utile applicazione qualora lo spazio allocato per la stringa sia insufficiente, oppure se si deve avere una stringa di una certa dimensione prefissata e nota: in questo modo si

Page 105: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 104 di 299

evita il sovraccarico di lavoro dato dal ridimensionamento del buffer. Manipolazione Concatenare, inserire, rimuovere o prelevare caratteri o intere stringhe oppure alterare le maiuscole o eliminare gli spazi superflui. Verificare se una stringa contiene o meno un certo carattere o un’intera sotto stringa oppure ci si può chiedere se due stringhe sono uguali e in base a quale criterio. Si deve verificare la validità del contenuto della stringa, controllando se essa aderisce o meno ad uno specifico modello formale. La funzione insert(int,string) inserisce nella stringa su cui è invocata, alla posizione specificata, la stringa passatale per parametro, le posizioni si contano a partire da 0. #include <iostream> #include <string> using namespace std; int main(void) { string s1 = "Uno "; string s2 = s1; system("cls"); s2.append("Tre "); string s3 = s2; s3.insert(s1.size(),"Due "); cout<<s3<<endl; system("pause");return (0); }

Il principale vantaggio della funzione insert è quello di evitare di sovrascrivere parti della stringa originale, evitando al programmatore di dover controllare la stringa. Sia la funzione append sia la funzione insert fanno side-effect ed è per questo che non si è scritto il codice seguente. #include <iostream> #include <string> using namespace std; int main(void) { string s1 = "Uno "; string s2 = s1; system("cls"); s2.append("Tre "); string s3 = s2; s3 = s2.insert(s1.size(),""); cout<<s3<<endl; system("pause");return (0); }

Il codice precedente non è sbagliato ma non fa ciò che deve fare: al termine dell’esecuzione s2 ed s3 avranno contenuto identico ma, oltre ad aver creato s3 si è modificato il contenuto

Page 106: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 105 di 299

di s2. La funzione replace (int, int, string) che sostituisce una porzione specificata dai due parametri interi che indicano il punto da cui iniziare la sostituzione e il numero di caratteri da sostituire, della stringa su cui è invocata con la stringa passatale come parametro. Il lavoro compiuto da questa funzione è notevole: non solo sostituisce una porzione di stringa ma se necessario, qualora la stringa da inserire abbia lunghezza maggiore della porzione da sostituire, ridimensiona anche la stringa, effettuando gli opportuni sfasamenti atti ad evitare sovrascritture. #include <iostream> #include <string> using namespace std; int main(void) { string s = "Pippo va in biclicletta perche' gli piace!"; system("cls"); s.replace(12,11,"macchina"); cout<<s<<endl; system("pause");return (0); }

La funzione erase (int,int) si occupa di eliminare un certo numero di caratteri dalla stringa su cui è invocata, a partire dalla posizione specificata dal suo primo parametro. #include <iostream> #include <string> using namespace std; int main(void) { string t = "Pippo, che brutta bicicletta che hai!"; system("cls"); cout << "Prima:\t" << t << endl; t.erase(11,7); cout << "Dopo:\t" << t << endl; system("pause");return (0); }

Ricerca Funzioni il cui scopo è quello di permettere di trovare, all’interno di una stringa, le occorrenze di un’altra sotto stringa o di un certo carattere. Queste funzioni fanno parte del gruppo di funzioni chiamato find.

Nome Significato

find() Ricerca un dato carattere o gruppo di caratteri all’interno di una stringa.

rifind() Come find ma la ricerca parte dalla fine della stringa e procede a ritroso.

find_first_off() Ricerca in una stringa la prima occorrenza di uno dei

Page 107: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 106 di 299

caratteri passati come parametro.

find_first_not_off() Ricerca in una stringa la prima occorrenza di un carattere che non è presente tra quelli passati come parametro.

find_last_off() Ricerca in una stringa l’ultima occorrenza di uno dei caratteri passati come parametro.

find_last_not_off() Ricerca in una stringa l’ultima occorrenza di un carattere che non è presente tra quelli passati come parametro.

Il valore restituito da queste funzioni rappresenta la posizione del primo carattere della stringa cercata, il conteggio inizia dalla posizione 0. Qualora la ricerca non vada a buon fine è restituito il valore npos, accessibile, mediante il (::) dalla stessa classe string. #include <iostream> #include <string> using namespace std; void cerca(char c, string s); int main(void) { string s_ricerca = "questa è una stringa per la ricerca"; system("cls"); cerca('s',s_ricerca); cerca('z',s_ricerca); system("pause");return (0); } void cerca(char c, string s) { int pos; if ((pos = s.find(c)) == string::npos) cout << "Il carattere " << c << " non e' presente nella stringa"<<endl; else cout << "Il carattere " << c << " si trova in posizione " << pos << endl; }

Un altro modo di utilizzare find è quello di adoperare la sua versione a due parametri, nella quale uno, di tipo int, specifica la posizione dalla quale partire per cominciare la ricerca. Questo si rivela molto utile quando si utilizza find all’interno di un ciclo, in quanto permette di analizzare singolarmente ogni occorrenza del testo ricercato. #include <iostream> #include <string> using namespace std; int main(void) { string s_ricerca = "Oggi e' una bella giornata di sole e di vento!"; string da_ricercare = "di"; int pos = 0; system("cls"); while ((pos = s_ricerca.find(da_ricercare,pos)) != string::npos) { cout << "Trovato \"" << da_ricercare << "\"in posizione " << pos << endl; pos++; } system("pause");return (0);

Page 108: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 107 di 299

}

Si è ciclato tra le varie occorrenze della stringa "di" presenti all'interno della stringa più grande, senza per questo dovere compiere strane operazioni di copia e incolla sulla stringa originale che, infatti, non è stata toccata per nulla. Questo, oltre ad essere auspicabile da un punto di vista concettuale perché una ricerca non è un’operazione di modifica, è anche molto utile dal punto di vista pratico in quanto, l’intera operazione è effettuata con efficienza e non spreca tempo di calcolo. La funzione rfind (reverse find) ricerca all’inverso, ha lo stesso comportamento, ad eccezione del fatto che la ricerca parte dalla fine della stringa e procede all’indietro, per cui la prima posizione restituita rappresenta l’ultima occorrenza del testo che si sta cercando. Le altre quattro funzioni, svolgono delle ricerche particolari: trovano infatti la prima o l’ultima occorrenza in una stringa, di un carattere presente nel gruppo di caratteri specificato. Tale gruppo di caratteri può anche essere specificato “al negativo”, in altre parole facendo trovare il primo o ultimo carattere che non compare nella lista data. #include <iostream> #include <string> using namespace std; int main(void) { string s_occorrenze = "Stringa per la ricerca di occorrenze"; string s_caratteri = "abcd"; system("cls"); // find_first_of int pos = s_occorrenze.find_first_of(s_caratteri); cout << "Il primo carattere di " << s_caratteri <<" presente e'\t\t"<< s_occorrenze[pos] << " in posizione " << pos<< endl; // find_last_of pos = s_occorrenze.find_last_of(s_caratteri); cout << "L'ultimo carattere di " << s_caratteri <<" presente e'\t\t"<< s_occorrenze[pos] << " in posizione " << pos<< endl; // find_first_not_of pos = s_occorrenze.find_first_not_of(s_caratteri); cout << "Il primo carattere non presente in "<< s_caratteri << " e'\t"<< s_occorrenze[pos] << " in posizione " << pos<< endl; // find_last_not_of pos = s_occorrenze.find_last_not_of(s_caratteri); cout << "L'ultimo carattere non presente in "<< s_caratteri << " e'\t"<< s_occorrenze[pos] << " in posizione " << pos<< endl; system("pause");return (0); }

Per la prima funzione, il primo carattere del gruppo "abcd" è proprio la "a" che è presente in posizione sei nella stringa.

Page 109: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 108 di 299

Espressioni regolari – RE (Regular Expression) – regexp - regex Il C++ non supporta le RE nativamente. Una libreria di gestione delle espressioni regolari è Regex++ di John Maddock, grazie a boost::regex. Per installare boost::regex, accedere al prompt dei comandi e posizionarsi nella cartella seguente. I:\ESERCIZI\VISUAL C++\BOOST_1_47_0\BOOST_1_47_0\LIBS\REGEX\BUILD e lanciare il seguente comando. nmake –fvc10.mak install Per cancellare tutti i file temporanei creati durante la compilazione lanciare il seguente comando. nmake –fvc10.mak clean Inserire nel progetto la libreria compilata.

#include <iostream> #include <boost/regex.hpp> using namespace std; using namespace boost; int main(void) { system("cls"); regex r("Ciao!"); cout << regex_match("Ciao!", r)<<endl; system("pause");return(0); }

La riga regex r("Ciao!") costruisce un oggetto r, di tipo regex, boost::regex è un typedef definito nel seguente modo. typedef boost::basic_regex<char> boost::regex; Anche la classe string è definita nello stesso modo. typedef std::basic_string<char> std::string; Regex è in effetti molto simile a string, perché ricalca l’interfaccia dei contenitori standard.

Page 110: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 109 di 299

Esistono regex e wregex, proprio come esistono string e wstring. La stringa ("Ciao!") passata nel costruttore di boost::regex è la RE vera e propria, una volta definita, è possibile ottenere l’oggetto corrispondente come std::string, con la funzione str. regex r("Ciao!"); string s(r.str()); Ovviamente, una RE non ha molto senso come contenitore alternativo a string, lo scopo di una RE è fungere da modello in base al quale, poi, si effettueranno delle validazioni: nell’esempio questo è svolto dall’algoritmo regex_match(testo_da_validare, regex). Una stringa è considerata valida rispetto ad una RE solo se è identica a essa, infatti, nell’esempio regex_match restituisce true e quindi è stampato a schermo il valore uno. Da questo punto di vista, la chiamata a regex_match potrebbe essere sostituita da un confronto con un oggetto string. cout << (string("Ciao!") == r.str()); Proprio come gli oggetti string, le RE sono case-sensitive e pertanto se nell’esempio si cambiasse anche solo una maiuscola, regex_match restituirà inesorabilmente false. #include <iostream> #include <boost/regex.hpp> using namespace std; using namespace boost; int main(void) { system("cls"); regex r("Ciao!"); cout << regex_match("ciao!", r)<<endl; system("pause");return(0); }

Prima differenza con gli oggetti string: il costruttore di RE prevede un secondo parametro, all’interno del quale è possibile combinare dei flag che permettono di modificare i requisiti della RE. Esempio, icase rende i confronti case insensitive. #include <iostream> #include <boost/regex.hpp> using namespace std; using namespace boost; int main(void) { system("cls"); // regex case insensitive regex r("ciao!", regex::icase); regex_match("ciao!", r); cout << regex_match("ciao!", r)<<endl; regex_match("CIAO!", r); cout << regex_match("ciao!", r)<<endl; system("pause");return(0);

Page 111: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 110 di 299

}

Per indicare l’ingresso di un numero a 4 cifre si può scrivere la RE seguente. \d\d\d\d La corrispondente applicazione per la validazione è la seguente. #include <iostream> #include <boost/regex.hpp> using namespace std; using namespace boost; int main(void) { system("cls"); regex r("\\d\\d\\d\\d"); cout << regex_match("1234", r)<<endl; cout << regex_match("123", r)<<endl; system("pause");return(0); }

Il (\\) all’inizio dei 4 (d), è una logica conseguenza del fatto che in C++ il (\) è un carattere speciale che serve a introdurre i caratteri di escape e che per essere scritto testualmente dev’essere a sua volta preceduto da un (\). Anche le RE hanno i loro caratteri riservati, chiamati metacaratteri: .[{()\*+?|^$. Oltre alle classi di carattere predefinite, è possibile indicare qualunque insieme di caratteri attraverso l’uso delle ([]). La RE: \d[abc]\d ad esempio, valida qualsiasi stringa costituita da una cifra, un carattere a scelta fra (a), (b) e (c) e un’altra cifra. Ad esempio, la stringa: “1a2” è valida, la stringa “2d1” non lo è, perché (d) non fa parte della classe [abc]. Per snellire la specifica delle classi, è comodo fare riferimento ai range. Esempio, validare una coppia di coordinate della scacchiera, in altre parole permettere stringhe come “a1”, “b8”, “h5”,ma non “x9” o altri codici senza senso. Con le classi composte si può scrivere. [abcdefgh][12345678] È una scrittura lunga, usando dei range con la sintassi [inizio-fine], si può ridurre il tutto. [a-h][1-8] Può essere molto utile usare l’operatore di negazione (^), se si vuole evitare che l’utente inserisca un carattere non italiano, ad esempio, si può usare il gruppo seguente. [^jkwxy]

Page 112: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 111 di 299

Le RE usate finora sono orientate ai singoli caratteri più che alle stringhe. Esempio, validare un codice di 16 cifre, come quello di una carta di credito. \d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d \d{16} Le due scritture sono equivalenti, {n} è un quantificatore che convalida la stringa solo se l’espressione che lo precede si trova ripetuta esattamente n volte. #include <iostream> #include <boost/regex.hpp> using namespace std; using namespace boost; int main(void) { system("cls"); regex r("\\d{4}[-/\\s]*\\d{4}[-/\\s]*\\d{4}[-/\\s]*\\d{4}[-/\\s]*"); cout << regex_match("1234567890123456", r)<<endl; // true cout << regex_match("1234-5678-9012-3456", r)<<endl; // true cout << regex_match("1234 - 5678 / 9012 /-/3456", r)<<endl; // true! cout << regex_match("123 - 5678 - 9012 - 3451",r)<<endl; // false system("pause");return(0); }

Analizzare la RE, scomporla togliendo i (\\), riducendoli a uno solo. \d{4}[-/\s]*\d{4}[-/\s]*\d{4}[-/\s]*\d{4}[-\s/]* Quindi analizzarla: si richiedono 4 numeri, seguiti da un carattere qualsiasi fra (-), (/) e (\s) ovvero tutti i caratteri di spazio. Questo insieme di caratteri è seguito da (*) vuol dire che può essere ripetuto 0 o più volte, poi il tutto si ripete uguale per altre tre volte. Questa RE serve quindi a validare un insieme di 4 numeri, seguito da un insieme di separatori (/ -), per 4 volte. La RE è un po’ lunga, la prima soluzione potrebbe essere la seguente. \d{4}[-/\s]*{4} Ripetere quattro volte il gruppo “cifre più separatori”: non funziona, il quantificatore si applica solo all’ultimo elemento, quindi, in pratica si sta “moltiplicando per quattro” soltanto [-/\s]*, per ripetere anche le cifre insieme ai separatori, si devono usare le parentesi tonde. (\d{4}[-/\s]*){4} Adesso il quantificatore si applica a tutto il blocco, un’espressione inclusa fra parentesi tonde è chiamata in gruppo.

Page 113: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 112 di 299

Quantificatore Sintassi Esattamente n volte {n} Da n a m volte estremi compresi {n, m} n o più volte {n, } Zero o più volte {0, } * Una o più volte {1, } + Zero o una volta {0, 1} ? È possibile scrivere. \d{15, 16}. Questa scrittura permette di trattare anche quelle carte di credito che hanno 15 cifre e non solo quelle che ne hanno 16. Il quantificatore {n,m}, infatti, valida la stringa solo se l’espressione che lo precede è ripetuto almeno n volte e al più m volte. È possibile scrivere \w{6, }. Si usa questa RE per chiedere all’utente d’inserire una password di almeno 6 caratteri. Il quantificatore {n, }, infatti, valida la stringa solo se l’espressione che lo precede è scritta almeno n volte. L’algoritmo regex_search è utile per implementare delle ricerche testuali, si comporta in maniera diversa da regex_match: non richede una corrispondenza perfetta ma la cerca in mezzo alla stringa passata. #include <iostream> #include <boost/regex.hpp> using namespace std; using namespace boost; int main(void) { system("cls"); regex r("(\\d{4}[-/\\s]*){4}"); cout << regex_match("000-1234567890123456",r)<<endl; // false cout << regex_search("000-1234567890123456",r)<<endl; // true system("pause");return(0); }

La RE della carta di credito è ora utilizzata sulla stringa "000-1234567890123456". Questa non corrisponde perfettamente alla RE, in quanto contiene 3 caratteri iniziali e non 4 come richiesto. Pertanto, regex_match svolge il suo lavoro restituendo false, regex_search, invece, fa un passo in più e cerca di andare avanti per trovare una corrispondenza. E ci riesce, perché dal quinto carattere in poi ci sono 16 cifre, proprio come richiesto dalla RE: regex_search quindi i restituire true. Esempio, progettare una RE per individuare gli indirizzi email. [\w\.]+@[\w\.]+\.\w+ Una parola di almeno un (.), seguita da una (@), seguita da un’altra parola di almeno un (.), seguita da un (.), seguita da una parola di almeno un carattere. #include <iostream> #include <boost/regex.hpp>

Page 114: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 113 di 299

using namespace std; using namespace boost; int main(void) { system("cls"); regex r("[\\w\\.]+@[\\w\\.]+\\.\\w+"); string pagina("Per richiedere materiale relativo al corso scrivere a [email protected]"); cout << regex_search(pagina, r)<<endl; // true! system("pause");return(0); }

Ma non serve a molto sapere se una pagina contiene o meno degli indirizzi email. Si desidera sapere quali sono, per questo, si deve richiamare un overload di regex_search che permetta di aver accesso al risultato della ricerca. #include <iostream> #include <boost/regex.hpp> using namespace std; using namespace boost; int main(void) { system("cls"); regex r("[\\w\\.]+@[\\w\\.]+\\.\\w+"); string pagina("Per richiedere materiale scrivere a [email protected] oppure [email protected]"); smatch result; if (regex_search(pagina, result, r)) cout << result<<endl; system("pause");return(0); }

regex_search stampa l’indirizzo grazie all’oggetto result, di tipo smatch che serve a contenere i risultati di una ricerca effettuata su un oggetto std::string (pagina). smatch è, infatti, un typedef della classe generica match_results. template <class Iterator,class Allocator=std::allocator<sub_match<Iterator> > class match_results; typedef match_results<const wchar_t> wcmatch; typedef match_results<const char*> cmatch; typedef match_results<std::string::const_iterator> smatch; typedef match_results<std::wstring::const_iterator> wsmatch; In questo caso si è usato smatch, perché si è dichiarato pagina come std::string. Se si fosse dichiarato come const char*, si sarebbe dovuto usare cmatch. La funzione regex_search che si è richiamata ha tre parametri: il testo su cui effettuare la ricerca, l’oggetto in cui immagazzinare il risultato e infine la RE. Nell’esempio, però c’è un secondo indirizzo email, evidentemente regex_search si ferma al primo risultato trovato.

Page 115: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 114 di 299

Per accedere anche ai successivi si deve utilizzare un ciclo e ancora un nuovo overload di regex_search. #include <iostream> #include <boost/regex.hpp> using namespace std; using namespace boost; int main(void) { system("cls"); regex r("[\\w\\.]+@[\\w\\.]+\\.\\w+"); string pagina("Per richiedere materiale scrivere a [email protected] oppure [email protected]"); smatch result; string::const_iterator it = pagina.begin(); string::const_iterator end = pagina.end(); while (regex_search(it, end, result, r)) { cout << result << endl; it = result.suffix().first; } system("pause");return(0); }

Si è usato un overload di regex_search che prende non più un oggetto string ma due iteratori. Ogni volta che è stampato un risultato, s’incrementa l’iteratore it, puntandolo al primo carattere, first, di ciò che sta dopo il testo trovato, suffix. Il ciclo continua, finché non sono più trovate corrispondenze. Esempio, scomporre gli indirizzi di email in modo tale da separarne le componenti. ([\w\.]+)(@)([\w\.]+)\.(\w+) Così facendo si è scomposto l’indirizzo in quattro gruppi. 1. Username (1). 2. Chiocciola (2). 3. Dominio (3). 4. Dominio di primo livello (4). #include <iostream> #include <boost/regex.hpp> using namespace std; using namespace boost; int main(void) { system("cls"); regex r("([\\w\\.]+)(@)([\\w\\.]+)\\.(\\w+)"); string pagina("Per richiedere materiale scrivere a [email protected] oppure [email protected]"); smatch result; string::const_iterator it = pagina.begin(); string::const_iterator end = pagina.end();

Page 116: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 115 di 299

while (regex_search(it, end, result, r)) { cout << "Indirizzo:\t" << result[0] << endl<< "Username:\t" << result[1] << endl<< "Chiocciola:\t" << result[2] << endl<< "Dominio:\t" << result[3] << endl<< "Primo livello:\t" << result[4] << endl<<endl; it = result.suffix().first; } system("pause");return(0); }

Gli oggetti di tipo match_results sono utili anche come contenitori, result[0] contiene l’intera corrispondenza trovata e gli elementi da uno in poi corrispondono ai singoli sotto gruppi appena descritti. Alcuni utenti potrebbero scrivere il proprio indirizzo come usernameATdominio.com oppure username at dominio.com. La nuova RE è la seguente. regex r("([\\w\\.]+)(@|at| at )([\\w\\.]+)\\.(\\w+)",regex::icase); Il gruppo della (@), ora, comprende più alternative, separate dall’operatore pipe (|) che si comporta come un or logico. In result[2] si può avere (@), (at) o ( at ) a seconda dei casi. Gli indirizzi di email con (at) o ( at ) non si potranno mai usare per spedire la posta, quindi si deve sostituire il secondo gruppo con una (@). Si possono concatenare le stringhe in questo modo. string indirizzo = result[1] + "@" + result[3] + "." + result[4]; Si tratta di una soluzione lunga, grazie all’algoritmo regex_replace, possono essere riuniti i risultati in un testo di formattazione, proprio come si fa nella creazione di stringhe con boost::format o sprintf. string indirizzo(result[0]); indirizzo = regex_replace(indirizzo, r, "$1@$3.$4")); regex_replace prende in ingresso un testo da modificare, una RE di partenza e un testo di formato. Nel testo di formato ci si può riferire ai singoli sotto gruppi con l’operatore ($) permettendo così una scrittura estremamente compatta e variabile.

VARIABILI NON INIZIALIZZATE Il codice seguente dovrebbe fare la somma dei numeri da 1 a 10. #include <iostream>

Page 117: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 116 di 299

using namespace std; int main(void) { int sum=0; system("cls"); for(int x = 1; x <= 10; ++x) sum += x; cout << "La somma vale: " << sum << endl; system("pause");return (0); } Lanciare l’applicazione, compare la seguente finestra.

La variabile fa riferimento ad una locazione di memoria che in quel momento non è assegnata, in questo modo si evita l’azzeramento del valore per risparmiare qualche ciclo di clock nei casi in cui l’assegnazione è prevista solo in un momento successivo. Una volta compilato, il codice C++ diventa puro linguaggio macchina, a contatto diretto con registri e CPU, senza alcuna rete di sicurezza. #include <iostream> using namespace std; int main(void) { int sum=0; system("cls"); for(int x = 1; x <= 10; ++x) sum += x; cout << "La somma vale: " << sum << endl; system("pause");return (0); }

SETLOCALE Il codice seguente non stampa i caratteri accentati ma simboli strani. #include <iostream> using namespace std; int main(void) { system("cls"); cout << "Oggi è una bella giornata!\n" ; system("pause");return (0); }

Questo comportamento accade perchè per default la console è settata come ANSI.

Page 118: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 117 di 299

#include <iostream> using namespace std; int main(void) { system("cls"); char* loc = setlocale(LC_ALL,"italian"); cout << "Oggi è una bella giornata!\n" ; system("pause");return (0); }

Page 119: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 118 di 299

MICROSOFT VISUAL C++ CLR

INTRODUZIONE Supporta il modello di programmazione dinamico e a componenti.

Per creare codice managed è possibile utilizzare le opzioni seguenti. /clr: genera un misto di codice managed e unmanaged. /clr: pure: genera codice managed e dati unmanaged. /clr: safe: genera solo codice managed. /clr:oldSyntax: compila il codice C++ scritto con sintassi .NET versione 1.x e genera

codice managed e unmanaged.

#PRAGMA REGION Organizza logicamente porzioni di codice che svolgono operazioni correlate.

VARIABILE HANDLE A differenza del puntatore che punta nell’area di memoria heap, sullo stack, invece, è creato l’handle, vale a dire la “maniglia” che permette di raggiungere e manipolare l’oggetto in memoria. Un handle è un riferimento ad un oggetto e si rappresenta con (^) subito dopo il nome del tipo oppure deve precedere il nome della variabile, gli indirizzi degli handle non possono essere manipolati, per esempio sommando degli offset. La durata della vita di un oggetto sullo heap è determinata dal processo di GC di .NET. Quando l’oggetto non ha nessun handle utilizzato, ad esempio perché tutti sono usciti dal proprio scope di utilizzo, l’oggetto in memoria è marcato come “distruggibile” e sarà cancellato appena possibile. int^ intHandle1 = 3; int ^intHandle2 = 7; int ^intHandle3, ^intHandle4; L’operatore di riferimento lavora in maniera analoga all’operatore d’indirizzamento ma agisce sugli handle, in altre parole sugli oggetti .NET. using namespace System; int main(array<System::String ^> ^args) { int x=1; // rifX è un riferimento ad un int int %rifX=x;

Page 120: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 119 di 299

// si assegna un valore a rifX rifX=2; Console::Clear(); // camba il valore cui rifX si riferisce, cioè x vale 2 Console::WriteLine(x); // x Console::ReadLine(); return (0); } int i(0); // equivalente a int i =0; double d(1.0); // equivalente a double d = 0.0 Non confondere un carattere Unicode a 16 bit con il tipo char che rappresenta, invece, un carattere ASCII a 8 bit. Per creare un carattere Unicode basta utilizzare come prefisso una (L). // il carattere ch contiene l’equivalente Unicode del carattere ‘a’ Char ch=L'a';

PARAMETRI DEL MAIN /* l'array args contiene la stringa via linea di comando, la proprietà Length restituisce per l'array una lunghezza pari a 1 il metodo WriteLine stampa la stringa Hello seguita dall'elemento 0 dell'array args, in pratica dal suo primo e unico elemento */ using namespace System; int main(array<System::String ^> ^args) { Console::Clear(); if(args->Length>0) Console::WriteLine("Hello, "+args[0]); Console::ReadLine(); return (0); }

BOXING using namespace System; int main(array<System::String ^> ^args) { Console::Clear(); Object^ obj = 42; // box del valore intero 42 int^ boxint = (int^)obj; // int^ è un boxed int Console::WriteLine("Valore di obj, boxint:\t{0,3}, {1,3}", obj, boxint); *boxint = 101; // cambia il valore in boxed int object Console::WriteLine("Valore di obj, boxint:\t{0,3}, {1,3}", obj, boxint); obj = 87; // box del valore intero 87 Console::WriteLine("Valore di obj, boxint:\t{0,3}, {1,3}", obj, boxint); int i = (int)obj; // unbox e copia il valore da un Object^ int j = *boxint; // copia il valore da un int^ Console::WriteLine("Valore di i, j:\t\t{0,3}, {1,3}", i, j); Console::ReadLine();return (0); }

Page 121: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 120 di 299

CASTING using namespace System; // dynamic cast void DynamicCast() { Object^ obj = "123"; IComparable^ icomp = dynamic_cast<IComparable^>(obj); if (icomp) { int value = icomp->CompareTo("ABC"); Console::WriteLine("L'oggetto è stato IComparable, risultato:\t{0}", value); } else Console::WriteLine("La variabile obj è stato non IConvertible."); } // safe cast void SafeCast() { Object^ obj = "Andrea"; try { Version^ v = safe_cast<Version^>(obj); Console::WriteLine("Oggetto Version^ è: \{0}\n", v->ToString()); } catch (InvalidCastException^) { Console::WriteLine("L'oggetto non è stato Version^."); } String^ str = safe_cast<String^>(obj); Console::WriteLine("L'oggetto è stato una stringa:\t\t\t{0}", str); Decimal d(19.99); float f = safe_cast<float>(d); Console::WriteLine("Valore di float dalla conversione:\t\t{0}", f); } // C-style cast: conversione esplicita void ExplicitTypeConversion() { Object^ obj = "Sperelli"; try { Version^ v = (Version^)obj; Console::WriteLine("Oggetto Version^ è:\t{0}\n", v->ToString()); } catch (InvalidCastException^) { Console::WriteLine("L'oggetto non è stato Version^."); } String^ str = (String^)obj; Console::WriteLine("L'oggetto è stato una stringa:\t\t\t{0}", str); Decimal d(20.01); float f = (float)d; Console::WriteLine("Valore di float dalla conversione:\t\t{0}", f); } int main(array<System::String ^> ^args) { Console::Clear(); DynamicCast(); SafeCast(); ExplicitTypeConversion(); Console::ReadLine();return (0); }

Page 122: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 121 di 299

ENUM using namespace System; enum Directions {Nord,Est,Sud,Ovest }; void NativeEnums() { Directions course = Nord; course = static_cast<Directions>((static_cast<int>(course) + 1)); Console::Write("Direzione corrente:\t\t\t"); Console::WriteLine(course); } enum class Suits { Spade,Cuori,Clubs,Diamanti }; void ClrEnums() { Suits card = Suits::Spade; ++card; Console::Write("Suite corrente:\t\t\t\t"); Console::WriteLine(card); } enum class SmallEnum : char { Value = 'A' }; void UnderlyingType() { Console::WriteLine("Dimsensione di SmallEnum:\t\t{0}", sizeof(SmallEnum)); } [Flags] enum class Clearance { NoAccess = 0x00, TopSecret = 0x01, Confidential = 0x02, MarketResearch = 0x04 }; void FlagsEnums() { Clearance employee = Clearance::Confidential | Clearance::MarketResearch; Console::WriteLine("L'impiegato ha un permesso top secret?\t{0}",(employee & Clearance::TopSecret) == Clearance::TopSecret); Console::WriteLine("Permessi dell'impiegato:\t\t{0}", employee); } int main(array<System::String ^> ^args) { Console::Clear(); NativeEnums(); ClrEnums(); UnderlyingType(); FlagsEnums(); Console::ReadLine();return (0); }

Page 123: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 122 di 299

ARRAY Gli array hanno molte limitazioni. Sono statici: il numero di elementi è fissato a compile-time. Non hanno un controllo di accesso agli indici dei propri elementi. Non è possibile confrontare due array con gli operatori relazionali. Non è possibile assegnare un array ad un altro. Per superare le limitazioni indicate, il C++ mette a disposizione il template di classe vector. Gli array essendo degli oggetti, sono dichiarati come handle. Gli array jagged sono dei particolari tipi di array a più dimensioni che, però, non sono da confondere con gli array multidimensionali. Essi sono, in pratica, vettori di vettori e possono essere non rettangolari, vale a dire, facendo sempre l’esempio a due dimensioni, un array di tale tipo ha ancora più righe ma ognuna di esse può avere diverso numero di colonne. using namespace System; void StampaVettore(array<int>^ Arr) { for(int i = 0; i < Arr->Length; ++i) Console::Write("{0,3}", Arr[i]); Console::WriteLine(); } void StampaMatrice(array<int,2>^ Arr) { for(int i = 0; i < Arr->GetLength(0); ++i) { Console::Write(" "); for(int j = 0; j < Arr->GetLength(1); ++j) Console::Write("{0,3}", Arr[i,j]); Console::WriteLine(); } } void StampaJaggedArray(array<array<int>^>^ Arr) { for(int i = 0; i < Arr->Length; ++i) { Console::Write(" "); for(int j = 0; j < Arr[i]->Length; j++) Console::Write("{0,3}", Arr[i][j]); Console::WriteLine(); } } void vettore() { // crea e inizializza un vettore lungo 10 array<int>^ arr1 = gcnew array<int>(10) {0, 1, 2, 3, 4}; // crea e inizializza un vettore, la lunghezza è data dall'inizializzazione array<int>^ arr2 = gcnew array<int> {0, 1, 2, 3, 4}; // crea un vettore lungo 10 w lo carica array<int>^ arr3 = gcnew array<int>(10); for(int i = 0; i < arr3->Length; ++i) arr3[i] = i*i; Console::Write("Vettore 1: "); StampaVettore(arr1); Console::Write("Vettore 2: "); StampaVettore(arr2); Console::Write("Vettore 3: "); StampaVettore(arr3); }

Page 124: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 123 di 299

void matrice() { array<int,2>^ arr4 = gcnew array<int,2>(3,5); // il metodo GetLength ritorna la lunghezza dell'array for(int i = 0; i < arr4->GetLength(0); ++i) { for(int j = 0; j < arr4->GetLength(1); ++j) arr4[i,j] = i*j; } Console::WriteLine("\nMatrice: "); StampaMatrice(arr4); } /* array di dimensione 1, lunghezza 4, ogni elemento dell’array è a sua volta un array d’interi Ognuno degli array d’interi dev’essere a sua volta inizializzato */ void JaggedArrays() { array<array<int>^>^ arr5 = gcnew array<array<int>^>(4); for(int i = 0; i < arr5->Length; ++i) { arr5[i] = gcnew array<int>(i+1); for (int j = 1; j <= i+1; j++) arr5[i][j-1] = j; } Console::WriteLine("\nJaggedArrays: "); StampaJaggedArray(arr5); } void ArrayCovariance() { array<Object^>^ arr1; array<String^>^ arr2; array<Version^>^ arr3; arr1 = gcnew array<String^>(5); try { arr1[0] = gcnew Version; Console::WriteLine("\nAssegnazione array funziona."); } catch (ArrayTypeMismatchException^) { Console::WriteLine("\nAssegnazione array NON funziona!"); } try { arr2 = (array<String^>^)arr1; Console::WriteLine("Cast to array<String^>^ funziona."); } catch (InvalidCastException^) { Console::WriteLine("Cast to array<String^>^ NON funziona!"); } try { arr3 = (array<Version^>^)arr1; Console::WriteLine("Cast to array<Version^>^ funziona."); } catch (InvalidCastException^) { Console::WriteLine("Cast to array<Version^>^ NON funziona!"); } } // Passing array array<String^>^ ReturnArray() { return gcnew array<String^> { "Rosso", "Verde", "Giallo", "Viola","Blu", "Nero", "Indaco" }; } void TakeArray(array<Object^>^ Arr) { for(int i = 0; i < Arr->Length; ++i) Console::WriteLine(" {0}", Arr[i]); }

Page 125: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 124 di 299

void PassingArrays() { array<String^>^ ingredients = ReturnArray(); Console::WriteLine("\nColori: "); TakeArray(ingredients); } int main(array<System::String ^> ^args) { Console::Clear(); vettore(); matrice(); JaggedArrays(); ArrayCovariance(); PassingArrays(); Console::ReadLine();return (0); }

Esempio, creare due oggetti di tipo vector in grado di contenere valori interi. #include <iostream> #include <string> #include <vector> using namespace std; int main (void) { // oggetti di tipo vector inizializzati a 0 vector<int> n1(20); vector<int> n2(15); // inizializzazione n1[2] = 20; n1[4] = 40; n2[1] = 10; n2[3] = 30; // array dinamico: push_back aggiunge un elemento alla fine n2.push_back (7); // dimensione dei vettori cout<<"Dimensione di n1:\t"<<n1.size()<<endl;

Page 126: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 125 di 299

cout<<"Dimensione di n2:\t"<<n2.size()<<endl; // l’accesso ad un elemento fuori dal range genera un’eccezione // solo se si usa la funzione at e non se si usa l’accesso [] try { int e = n1.at(4); //int e = n1.at(34); cout<<"Valore dell'elemento:\t"<<e<<endl; } catch (out_of_range &exc) { cout<<"Eccezione: "<<exc.what ()<<endl; } // assegna n2 a n1 n1 = n2; n1[3] = 77; if (n1==n2) cout<<"n1 e n2 sono uguali!"<<endl; else cout<<"n1 e n2 NON sono uguali!"<<endl; system("pause");return(0); }

Esempio. #include <iostream> #include <vector> #include <algorithm> using namespace std; // classe per la creazione di un function object class FunzioneDoppio { public: void operator()(int &x) { x = 2*x; } }; int main(void) { system("cls"); cout << "Gestione array."<<endl<<endl; // riempio un vettore con interi da 1 a 10 vector<int> v1; for(int j=1;j<11;++j) v1.push_back(j); // stampo il vettore originale cout << "Elementi di v1"<<endl; for(int j=0;j<v1.size();++j) cout << v1[j] << " "; cout<<endl<<endl; // istanzio il function object FunzioneDoppio f; // applico il for a tutti gli elementi del vettore tramite for_each() for_each(v1.begin(),v1.end(),f);

Page 127: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 126 di 299

// stampo il vettore raddoppiato cout << "Elementi di v1 raddoppiati"<<endl; for( int j=0;j<v1.size();++j) cout << v1[j] << " "; cout<< endl<<endl; // riempio il vettore con numeri casuali non ordinati vector<int> v2; v2.push_back(10); v2.push_back(1); v2.push_back(-3); v2.push_back(4); v2.push_back(0); // stampo il vettore originale cout << "Elementi di v2 disordinati"<<endl; for(int j=0;j<v2.size();++j) cout << v2[j] << " "; cout<< endl<<endl; // ordino il vettore sort(v2.begin(),v2.end()); // stampo il vettore ordinato cout << "Elementi di v2 ordinati"<<endl; for(int j=0;j<v2.size();++j) cout << v2[j] << " "; cout<< endl; system("pause"); return (0); }

Esempio. #include <iostream> #include <vector> #include <list> #include <algorithm> using namespace std; bool twice (int elem1, int elem2 ) { return 2 * elem1 == elem2; } int main(void) { vector <int> v1, v2; list <int> L1; vector <int>::iterator Iter1, Iter2; list <int>::iterator L1_Iter, L1_inIter; for ( int i = 0 ; i <= 5 ; ++i ) v1.push_back( 5 * i );

Page 128: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 127 di 299

for ( int i = 0 ; i <= 5 ; ++i ) v1.push_back( 5 * i ); for ( int ii = 4 ; ii <= 5 ; ++ii ) L1.push_back( 5 * ii ); for ( int iii = 2 ; iii <= 4 ; ++iii ) v2.push_back( 10 * iii ); system("cls"); cout << "Vettore v1 =\t( " ; for ( Iter1 = v1.begin( ) ; Iter1 != v1.end( ) ; ++Iter1) cout << *Iter1 << " "; cout << ")" << endl; cout << "Lista L1 =\t( " ; for ( L1_Iter = L1.begin( ) ; L1_Iter!= L1.end( ) ; ++L1_Iter ) cout << *L1_Iter << " "; cout << ")" << endl; cout << "Vettore v2 =\t( " ; for ( Iter2 = v2.begin( ) ; Iter2 != v2.end( ) ; ++Iter2 ) cout << *Iter2 << " "; cout << ")" << endl; // cerca v1 per la prima occorrenza in L1 vector <int>::iterator result1; result1 = search (v1.begin( ), v1.end( ), L1.begin( ), L1.end( ) ); if ( result1 == v1.end( ) ) cout <<endl<< "Non ci sono occorrenze di L1 in v1!" << endl; else cout <<endl<< "La prima occorrenza di L1 in v1 si trova nella posizione: "<< result1 - v1.begin( ) << endl; // Searching v1 for a match to L1 under the binary predicate twice vector <int>::iterator result2; result2 = search (v1.begin( ), v1.end( ), v2.begin( ), v2.end( ), twice ); if ( result2 == v1.end( ) ) cout <<endl<< "Non ci sono occorrenze di v2 in v1!" << endl; else cout <<endl<< "La prima occorrenza di v1 che e' la meta' in v2 si trova nella posizione: " << result2 - v1.begin( ) << endl; system("pause");return (0); }

Esempio, merge di array. #include <iostream> #include <algorithm> #include <vector> #include <functional> #include <iterator> using namespace std; int main (void)

Page 129: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 128 di 299

{ int d1[4] = {1,2,3,4}; int d2[8] = {11,13,15,17,12,14,16,18}; vector<int,allocator<int> > v1(d1+0, d1+4), v2(d1+0, d1+4); vector<int,allocator<int>>v3(d2+0,d2+8),v4(d2+0,d2+8),v5(d2+0, d2+8), v6(d2+0, d2+8); vector<int,allocator<int> > v7; // merge v1 e v2 merge(v1.begin(), v1.end(), v2.begin(), v2.end(), v3.begin()); merge(v1.begin(), v1.end(), v2.begin(), v2.end(), v4.begin(), less<int>()); vector<int,allocator<int> >::iterator mid = v5.begin(); advance(mid,4); inplace_merge(v5.begin(),mid,v5.end()); mid = v6.begin(); advance(mid,4); inplace_merge(v6.begin(), mid, v6.end(), less<int>()); // merge v1 e v2 merge(v1.begin(), v1.end(), v2.begin(), v2.end(), back_inserter(v7)); ostream_iterator<int,char,char_traits<char> > out(cout," "); copy(v1.begin(),v1.end(),out); cout << endl; copy(v2.begin(),v2.end(),out); cout << endl; copy(v3.begin(),v3.end(),out); cout << endl; copy(v4.begin(),v4.end(),out); cout << endl; copy(v5.begin(),v5.end(),out); cout << endl; copy(v6.begin(),v6.end(),out); cout << endl; copy(v7.begin(),v7.end(),out); cout << endl; // merge v1 e v2 merge(v1.begin(),v1.end(),v2.begin(),v2.end(),ostream_iterator<int,char,char_traits<char> >(cout," ")); cout<<endl; system("pause"); return (0); }

Esempio, ordinamento di array. #include <iostream> #include <algorithm> #include <vector> #include <iterator> #include <functional> using namespace std;

Page 130: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 129 di 299

int main (void) { int d1[4] = {1,2,3,4}; int d2[4] = {1,3,2,4}; vector<int,allocator<int> > v1(d1+0, d1+4), v2(d2+0, d2+4); make_heap(v1.begin(), v1.end()); make_heap(v2.begin(), v2.end(), less<int>()); ostream_iterator<int,char,char_traits<char> > out(cout," "); copy(v1.begin(), v1.end(), out); cout << endl; copy(v2.begin(), v2.end(), out); cout << endl; pop_heap(v1.begin(), v1.end()); pop_heap(v2.begin(), v2.end(), less<int>()); copy(v1.begin(), v1.end(), out); cout << endl; copy(v2.begin(), v2.end(), out); cout << endl; push_heap(v1.begin(), v1.end()); push_heap(v2.begin(), v2.end(), less<int>()); copy(v1.begin(),v1.end(),out); cout << endl; copy(v2.begin(),v2.end(),out); cout << endl; // sort sort_heap(v1.begin(), v1.end()); sort_heap(v2.begin(), v2.end(), less<int>()); copy(v1.begin(), v1.end(), out); cout << endl; copy(v2.begin(), v2.end(), out); cout << endl; system("pause");return (0); }

Esempio, ordinamento di array. #include <iostream> #include <vector> #include <algorithm> using namespace std; struct associate { int num; char chr; associate(int n, char c) : num(n), chr(c){}; associate() : num(0), chr('\0') {}; }; bool operator< (const associate &x, const associate &y)

Page 131: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 130 di 299

{ return x.num < y.num; } ostream& operator<< (ostream &s, const associate &x) { return s << "<" << x.num << ";" << x.chr << ">"; } int main (void) { system("cls"); vector<associate,allocator<associate> >::iterator i, j, k,x; associate arr[20] = {associate(-4, ' '), associate(16, ' '), associate(17, ' '), associate(-3, 's'), associate(14, ' '), associate(-6, ' '), associate(-1, ' '), associate(-3, 't'), associate(23, ' '), associate(-3, 'a'), associate(-2, ' '), associate(-7, ' '), associate(-3, 'b'), associate(-8, ' '), associate(11, ' '), associate(-3, 'l'), associate(15, ' '), associate(-5, ' '), associate(-3, 'e'), associate(15, ' ')}; vector<associate,allocator<associate> > v(arr+0, arr+20), v1((size_t)20), v2((size_t)20),v3((size_t)20); copy(v.begin(), v.end(), v1.begin()); copy(v.begin(), v.end(), v2.begin()); copy(v.begin(), v.end(), v3.begin()); // sort vettore v1. sort(v1.begin(), v1.end()); // stable_sort vettore v2 stable_sort(v2.begin(), v2.end()); // partial_sort vettore v3 partial_sort(v3.begin( ), v3.begin( ) + 10, v3.end( ) ); cout << "Vettore\t\tOrdinato\tStable_sort\tPartial_sort" << endl; for(i = v.begin(), j = v1.begin(), k = v2.begin(), x = v3.begin(); i != v.end(); ++i, ++j, ++k,++x) cout << *i << "\t\t" << *j << "\t\t" << *k <<"\t\t" << *x<< endl; system("pause"); return 0; }

STRING Una stringa è un array di caratteri, quindi può essere definito in questo modo.

Page 132: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 131 di 299

typedef string std::vector<char>; Uno dei tanti vantaggi di questa scrittura sta nel fatto che è generica e permette di definire anche stringhe di caratteri che non siano char. typedef wstring std::vector<wchar_t>; Questa scrittura crea un problema: caratteri diversi richiedono funzioni di valutazione, confronto, ricerca e manipolazione diverse. Per questo si può pensare di definire una classe abstract in questo modo. class std::character { public: character(int); // costruttore da intero // confronti fra caratteri virtual bool operator==(const character& b) = 0; virtual bool operator<(const character& b) = 0; … }; character può essere ereditata dai tipi concreti che definiscono un singolo insieme di caratteri. class std::ASCIIChar : public std::character {…}; class std::UnicodeChar : public std::character {…}; class std::ChineseChar : public std::character {…}; I tipi di caratteri ereditati possono essere utilizzati in array per creare stringhe. typedef std::ASCIIString std::vector<ASCIIChar>; typedef std::UnicodeString std::vector<UnicodeChar> Il C++ non ha seguito questo approccio, per almeno due motivi. 1. Efficienza: le funzioni virtuali sono più lente e le sottoclassi devono memorizzare una

vtable. 2. Praticità: al posto di definire la superclasse character, la STL usa il tipo char_traits che

è una classe template ed è definita nel modo seguente, con una struttura vuota. template<typename Character> struct char_traits {}; Una classe traits è un insieme di tipi e metodi statici che descrivono il tipo passato come parametro, char_traits, quindi, è vuota perché in sé non ha alcun’importanza: quelle che contano sono le sue specializzazioni. Esempio. // specializzazione di char_traits per i char template<> struct char_traits<char> { // definizione dei tipi typedef char char_type; // tipo di carattere typedef int int_type; // tipo intero corrispondente typedef streampos pos_type; // tipo di offset negli stream standard // altre definizioni di tipo: funzioni di confronto fra caratteri. static bool eq(const char_type& a , const char_type&b); // a == b?

Page 133: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 132 di 299

static bool lt(const char_type& a , const char_type& b); // a < b? // altre funzioni di confronto: funzioni su array static size_t length(const char_type* str); // quanto è lunga str? // confronta due stringhe static int compare(const char_type* str1,const char_type* str2); … }; Si definiscono dei tipi e dei metodi statici, se char_traits è l’equivalente generico della superclasse character, char_traits<char> è l’equivalente di ASCIICharacter. Tutte le altre specializzazioni di char_traits per altri tipi di caratteri hanno lo stesso aspetto. Esempio. // specializzazione di char_traits per i wchar_t template<> struct char_traits<wchar_t> { // definizione dei tipi typedef char char_type; // tipo di carattere typedef int int_type; // tipo interocorrispondente … // funzioni di confronto fra caratteri static bool eq(const char_type& a , const char_type& b); // a == b? … }; Grazie a char_traits, possono essere scritte funzioni e classi che si comportino diversamente a seconda del parametro passato: polimorfismo statico. Per sfruttarlo, occorre abbandonare l’idea di un vector<char> e creare un nuovo contenitore che faccia riferimento internamente a char_traits. La STL lo chiama basic_string e lo definisce nel modo seguente. template< typename Character, typename Traits = char_traits<Character>, typename Allocator = allocator<Character> > class std::basic_string; Il programmatore deve soltanto specificare il tipo di carattere, Character e, se esiste una specializzazione char_traits <Character>, basic_string sarà automaticamente in grado di accedere alle funzioni. Inoltre, se si vuole, è possibile specificare un char_traits diverso e personalizzato. La STL definisce anche i seguenti tipi. typedef std::string std::basic_string<char>; typedef std::wstring std::basic_string<wchar_t>; basic_string si basa, nelle sue operazioni fondamentali su char_traits. Esempio, operatore di confronto. std::string stringa1 = "Ciao", stringa2 = "CIAO"; std::cout << (stringa1 == stringa2); // vero? falso? "Ciao" e "CIAO" sono uguali: basic_string non risponde direttamente ma lo chiede a

Page 134: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 133 di 299

char_traits. Per la specializzazione char_traits<char> della libreria standard la risposta è no. Il confronto è un’operazione case sensitive: fa differenza fra maiuscole e minuscole. Se si volesse una stringa che non faccia differenza fra maiuscole e minuscole, basterebbe implementare un char_traits appropriato. struct char_traits_indulgente : char_traits<char> { static bool eq(char c1, char c2) { return toupper(c1) == toupper(c2); } static int compare(const char* s1, const char* s2,size_t n) { return memicmp(s1, s2, n); } }; Il nuovo char_traits_indulgente deriva direttamente da char_traits, ridefinendo le funzioni di confronto fra caratteri eq e fra stringhe compare. Derivare da classi concrete prive di distruttore virtuale non è corretto ma in questi casi è consentito, dato che non si usa char_traits_indulgente in modo polimorfico. Esempio, usare char_traits_indulgente per creare una stringa_indulgente. typedef basic_string<char,char_traits_indulgente> stringa_indulgente; La nuova stringa userà le funzioni di confronto “indulgenti” che non fanno differenza fra maiuscole e minuscole. stringa_indulgente stringa1 = "ciao", stringa2 = "CIAO"; std::cout << (stringa1 == stringa2); // vero basic_string definisce costruttori, distruttore, operatore d’assegnamento, di uguaglianza, di confronto e alcuni metodi ottimizzati per la gestione della stringa e offre tutte le possibilità di un contenitore STL: le funzioni push_back e append inseriscono degli elementi in coda. insert inserisce dei caratteri in mezzo, erase li rimuove e clear svuota tutto il buffer. size o length restituiscono il numero di caratteri della stringa e capacity la reale dimensione corrente del buffer. resize aumenta le dimensioni del buffer aggiungendo un carattere alla fine. E ([]) e at possono essere usati per l’accesso casuale. string str("CIAO…"); // costruttore da char[] str.erase(4, 3); // str = "CIAO" str += "MONDO"; // str = "CIAOMONDO" str.push_back('!'); // str = "CIAOMONDO!" str.insert(4, ", "); // str = "CIAO, MONDO!" str.resize(14, '!'); // str = "CIAO, MONDO!!!" cout << str.length(); // 14 cout << str.capacity(); // 14 o più cout << str[15]; // comportamento indefinito cout << str.at(15); // eccezione out_of_range In quanto contenitore STL, basic_string permette di accedere agli iteratori e quindi è possibile utilizzarla su tutti gli algoritmi previsti dalla libreria <algorithm>. Esempio. using namespace System;

Page 135: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 134 di 299

int main(array<System::String ^> ^args) { array<String^, 2>^ rubrica = gcnew array<String^, 2>(3,2); rubrica[0,0] = "Andrea"; rubrica[0,1] = "390 5634982"; rubrica[1,0] = "Guido"; rubrica[1,1] = "300 7341092"; rubrica[2,0] = "Remo"; rubrica[2,1] = "777 123435"; Console::Clear(); Console::WriteLine("Nome\tTelefono"); Console::WriteLine("-------------------"); for(int i=0; i<3; i++) { for(int j=0; j<2; j++) Console::Write("{0}\t", rubrica[i,j]); Console::WriteLine(); } Console::ReadLine();return (0); }

GESTIONE DEGLI ERRORI

Proprietà Descrizione

String^ Message Restituisce una stringa che descrive l’eccezione.

String^ Source Restituisce o imposta il nome dell’applicazione o dell’oggetto che ha generato l’eccezione.

String^ StackTrace Restituisce la rappresentazione dello stack di chiamate al momento dell’eccezione.

String^ HelpLink Restituisce o imposta il link alla documentazione sull’eccezione generata.

Exception^ InnerException

Restituisce l’istanza di System.Exception che ha causato l’eccezione corrente, in altre parole se un’eccezione A è stata lanciata da una precedente eccezione B, allora la proprietà InnerException di A restituirà l’istanza B.

MethodBase^ TargetSite

Restituisce il metodo in cui è stata generata leccezione.

void PrintExceptionInfo(Exception^ ex) { Console::WriteLine("Messaggio: {0}",ex->Message); Console::WriteLine("Sorgente: {0}",ex->Source); Console::WriteLine("StackTrace: {0}",ex->StackTrace); Console::WriteLine("HelpLink: {0}",ex->HelpLink); Console::WriteLine("InnerException: {0}",ex->InnerException); Console::WriteLine("Nome del metodo: {0}",ex->TargetSite->Name); }

LIBRERIA DI CLASSI DEL .NET FRAMEWORK Ogni classe deriva dalla superclasse System::Object, quindi scrivere il codice seguente. ref class Prova { }; È come scrivere. ref class Prova : System::Object { };

Page 136: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 135 di 299

Metodo Descrizione

virtual string ToString() Restituisce una stringa che rappresenta l’oggetto.

virtual int GetHashCode() Restituisce un intero, utilizzabile come valore di hash, per la ricerca dell’oggetto in un elenco di oggetti.

virtual bool Equals(Object^ o) Effettua un test di uguaglianza con un’altra istanza della classe.

static bool Equals(Object^ a,Object^ b)

Effettua un test di uguaglianza fra due istanze della classe.

static bool ReferenceEquals(Object^ a,Object^ b)

Effettua un test di uguaglianza per verificare se due riferimenti si riferiscono alla stessa istanza della classe.

Type^ GetType() Restituisce un oggetto derivato da System.Type che rappresenta il tipo dell’istanza.

Object^ MemberwiseClone() Effettua una copia dei dati contenuti nell'oggetto, creando un’altra istanza.

virtual void Finalize() Distruttore dell’istanza.

Metodo ToString È uno dei più utilizzati in qualsiasi tipo di applicazione, in quanto serve a fornire una rappresentazione testuale del contenuto di un oggetto. Esso è un metodo virtual nella classe System::Object, dunque ogni classe può fornire un override di esso, in modo da restituire una stringa significativa. Ad esempio i tipi numerici predefiniti forniscono tale override in modo da restituire il valore sotto forma di stringa. int i=100; String^ str=i.ToString(); // restituisce “100” Se non si ridefinisce il metodo nelle classi, sarà invocato il metodo della classe System::Object che restituirà una rappresentazione più generica. #include <iostream> #include <String> #include <stdlib.h> using namespace std; using namespace System; ref class Studente { private: int matricola; String^ cognome; String^ nome; public: Studente(int m, String^ n, String^ c) { matricola=m; cognome=c; nome=n; } }; int main(void) { system("cls"); Studente^ studente=gcnew Studente(1,"Andrea","Sperelli");

Page 137: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 136 di 299

Console::WriteLine(studente); system("pause");return (0); }

L’applicazione invoca il metodo studente->ToString() e stamperà solo il nome della classe stessa, mentre ci si aspetterebbe una stringa che contenga matricola, nome e cognome dello studente. Per far ciò, è necessario un override del metodo ToString. #include <iostream> #include <String> #include <stdlib.h> using namespace std; using namespace System; ref class Studente { private: int matricola; String^ cognome; String^ nome; public: Studente(int m, String^ n, String^ c) { matricola=m; cognome=c; nome=n; } virtual String^ ToString() override { return String::Format("Matricola {0} - {1} {2}",matricola, cognome,nome); } }; int main(void) { system("cls"); Studente^ studente=gcnew Studente(77,"Andrea","Sperelli"); Console::WriteLine(studente); system("pause");return (0); }

Metodi Equals e ReferenceEquals Effettuano il confronto di due istanze e sono richiamati in diverse altre classi per testare l’uguaglianza di due oggetti, ad esempio nelle collezioni standard il metodo seguente è utilizzato per verificare se esse contengono o meno una certa istanza. bool Equals(Object^ o); La classe System::Object fornisce un’implementazione del metodo Equals che verifica l’uguaglianza dei riferimenti. Senza fornire un override del metodo, il codice seguente si comporterebbe.

Page 138: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 137 di 299

Studente^ st1=gcnew Studente(1234,"Andrea", "Sperelli"); Studente^ st2=st1; Studente^ st3=gcnew Studente(1234,"Andrea", "Sperelli"); Console::WriteLine(st3->Equals(st1)) L’applicazione stampa false, dunque st3 e st1, pur rappresentando logicamente lo stesso Studente, sono considerati diversi, in quanto si riferiscono a due istanze in memoria distinte della classe Studente. Bisogna scrivere un metodo nella classe Studente, facendo l’override del metodo Equals che confronti la matricola. #include <iostream> #include <String> #include <stdlib.h> using namespace std; using namespace System; ref class Studente { private: int matricola; String^ cognome; String^ nome; public: Studente(int m, String^ n, String^ c) { matricola=m; cognome=c; nome=n; } virtual bool Equals(Object^ obj) override { if(dynamic_cast<Studente^>(obj)!=nullptr) return this->matricola==(dynamic_cast<Studente^>(obj))->matricola; return false; } }; int main(void) { system("cls"); Studente^ st1=gcnew Studente(1234,"Andrea", "Sperelli"); Studente^ st2=st1; Studente^ st3=gcnew Studente(1234,"Andrea", "Sperelli"); Console::WriteLine(st3->Equals(st1)); system("pause");return (0); }

Potrebbe essere necessario comunque dovere confrontare che siano uguali due riferimenti, in pratica che in realtà due variabili referenzino la stessa istanza in memoria, a tal scopo la classe System::Object fornisce il metodo static ReferenceEquals che verifica l’uguaglianza dei riferimenti di due oggetti. int main(void) { system("cls"); Studente^ st1=gcnew Studente(1234,"Andrea", "Sperelli");

Page 139: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 138 di 299

Studente^ st2=st1; Console::WriteLine("Object::ReferenceEquals(st4,st5) = "+Object::ReferenceEquals(st1,st2)); st2=gcnew Studente(1234,"Andrea", "Sperelli"); Console::WriteLine("Object::ReferenceEquals(st4,st5) = "+Object::ReferenceEquals(st1,st2)); system("pause");return (0); }

La prima chiamata a Object::ReferenceEquals restituisce true in quanto st1 e st2 sono due handle dello stesso oggetto Studente in memoria heap, mentre dopo avere costruito una nuova istanza ed assegnatala a st2, la stessa chiamata restituisce false, in quanto ora si hanno appunto due istanze diverse della classe Studente. Metodo GetHashCode Restituisce un valore intero, utilizzabile per lavorare con collezioni tipo tabelle hash e memorizzare oggetti tipo chiave/valore. Una hash table è formata da tante locazioni e la locazione in cui memorizzare o ricercare una coppia chiave/valore è determinata dal codice hash ricavato dalla chiave. Ad esempio quando si deve ricercare un dato oggetto, è ricavato il codice hash dalla chiave e nella posizione indicata da tale codice, sarà poi ricercata la chiave stessa e quindi se trovata tale chiave, può essere ricavato il valore corrispondente. La BCL fornisce una classe Hashtable che utilizza il metodo GetHashCode per ricavare il codice hash ed il metodo Equals per confrontare gli oggetti da memorizzare o memorizzati. Infatti, se si dovesse effettuare nella classe un override del metodo Equals, il compilatore avviserà con un warning se non s’implementerà anche l’override del metodo GetHashCode. L’implementazione del metodo richiede il ritorno di un valore int e, dunque, è necessario trovare un algoritmo che restituisca dei valori hash con una distribuzione casuale, con una certa velocità per questioni di performance e che restituisca valori hash uguali per oggetti equivalenti. Ad esempio, due stringhe uguali dovrebbero restituire lo stesso codice hash. public value struct Point { public: int x; int y; virtual int GetHashCode() override { return x ^ y; } }; Il metodo restituisce lo XOR fra le coordinate x e y del punto. Metodo GetType Restituisce un’istanza della classe System::Type che può essere utilizzata per ottenere una grande varietà d’informazioni a run-time sul tipo corrente di un oggetto, ad esempio il namespace, il nome completo, i metodi di una classe, quindi è il punto di accesso alla tecnologia di reflection, la quale consente di esplorare il contenuto di un tipo qualunque.

Page 140: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 139 di 299

APPLICAZIONE SDK (SOFTWARE DEVELOPMENT KIT) All’inizio l’unica possibilità per sviluppare applicazioni grafiche in ambiente Windows, era quella di utilizzare il linguaggio C e l’SDK, una collezione di API che potevano essere richiamate all’interno di un’applicazione C. In Visual Studio, grazie ad un wizard, fare clic su File/Nuovo/Progetto… (CTRL+MAIUSC+N), nella finestra Nuovo progetto, selezionare le voci seguenti Altri linguaggi/Visual C++/Win32/Progetto console Win32. Nella finestra Creazione guidata applicazione Win32, selezionare Tipo di applicazione: Applicazione Windows.

File FINESTRA.VCXPROJ File di progetto principale generato tramite una creazione guidata applicazione, contiene informazioni sulla versione del Visual C++ che ha generato il file e informazioni sulle piattaforme, le configurazioni e le caratteristiche del progetto. File FINESTRA.VCXPROJ.FILTERS File dei filtri generati tramite una creazione guidata applicazione, contiene informazioni sull’associazione tra i file del progetto e i filtri. File FINESTRA.RC Elenco di tutte le risorse utilizzate dall’applicazione, include le icone, le bitmap e i cursori. File RESOURCE.H File d’intestazione standard che definisce i nuovi ID risorse. File FINESTRA.ICO - SMALL.ICO File d’icona utilizzato come icona dell’applicazione (32x32), (16x16), è inclusa dal file di risorse principale FINESTRA.RC. File STDAFX.H - STDAFX.CPP Sono utilizzati per generare il file d’intestazione precompilato FINESTRA.PCH e il file dei

Page 141: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 140 di 299

tipi precompilato STDAFX.OBJ. File FINESTRA.CPP // Finestra.cpp : definisce il punto d'ingresso dell'applicazione #include "stdafx.h" #include "Finestra.h" #define MAX_LOADSTRING 100 // variabili globali: HINSTANCE hInst; // istanza corrente WCHAR szTitle[MAX_LOADSTRING]; // testo della barra del titolo WCHAR szWindowClass[MAX_LOADSTRING];// nome della classe di finestre principale // dichiarazioni con prototipo delle funzioni incluse in questo modulo di codice ATOM MyRegisterClass(HINSTANCE hInstance); BOOL InitInstance(HINSTANCE, int); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); // TODO: inserire qui il codice // inizializzare le stringhe globali LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadStringW(hInstance, IDC_FINESTRA, szWindowClass, MAX_LOADSTRING); // si registrano le caratteristiche principali dell’applicazione MyRegisterClass(hInstance); // eseguire l'inizializzazione dall'applicazione if (!InitInstance (hInstance, nCmdShow)) return FALSE; HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_FINESTRA)); // variabile di tipo messaggio MSG msg; // ciclo di messaggi principale: while (GetMessage(&msg, nullptr, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return (int) msg.wParam; } // funzione: MyRegisterClass: registra la classe di finestre. // questa funzione e il relativo utilizzo sono necessari solo se si desidera che il codice // sia compatibile con i sistemi Win32 precedenti alla funzione 'RegisterClassEx' // aggiunta a Windows 95. È importante chiamare questa funzione // in modo da ottenere piccole icone in formato corretto associate all'applicazione. ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEXW wcex; // si definiscono le caratteristiche dell’applicazione come icone, stile della finestra // principale, colori font, menu wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW;

Page 142: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 141 di 299

wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_FINESTRA)); wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_FINESTRA); wcex.lpszClassName = szWindowClass; wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); return RegisterClassExW(&wcex); } // funzione: InitInstance: salva l'handle d’istanza e crea la finestra principale // in questa funzione l'handle di istanza e' salvato in una variabile globale e // la finestra di programma principale e' creata e visualizzata. BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { hInst = hInstance; // memorizzare l'handle di istanza nella variabile globale // questa funzione crea la finestra principale dell’applicazione HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr); if (!hWnd) return FALSE; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; } // funzione: WndProc elabora i messaggi per la finestra principale. // WM_COMMAND - elabora il menu dell'applicazione // WM_PAINT - disegna la finestra principale // WM_DESTROY - inserisce un messaggio di uscita e restituisce un risultato LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_COMMAND: { int wmId = LOWORD(wParam); // analizzare le selezioni di menu switch (wmId) { case IDM_ABOUT: DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); break; case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } } break; case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); // TODO: aggiungere qui il codice di disegno che usa HDC EndPaint(hWnd, &ps);

Page 143: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 142 di 299

} break; case WM_DESTROY: PostQuitMessage(0); // si è ricevuto il messaggio di uscita break; default: // i messaggi non gestiti sono passati alla funzione di default return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } // gestore dei messaggi della finestra Informazioni su INT_PTR CALLBACK About(HWND hDlg,UINT message,WPARAM wParam,LPARAM lParam) { UNREFERENCED_PARAMETER(lParam); switch (message) { case WM_INITDIALOG: return (INT_PTR)TRUE; case WM_COMMAND: if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { EndDialog(hDlg, LOWORD(wParam)); return (INT_PTR)TRUE; } break; } return (INT_PTR)FALSE; }

Il cuore di un’applicazione Windows SDK è la funzione WinMain; al suo interno è richiamata RegisterClass per registrare la finestra principale dell’applicazione, la registrazione consiste nel definire alcune caratteristiche di una finestra come l’indirizzo della routine di gestione dei messaggi, la presenza di icone e menu, i colori di sfondo. Una volta registrata la finestra dell’applicazione è possibile creare fisicamente la finestra tramite la funzione CreateWindow che permette anche di definirne lo stile, posizione, dimensione, presenza di scrollbar laterali e/o orizzontali. La finestra, una volta creata, non è visibile fino a che non sono chiamate le funzioni ShowWindow e UpdateWindow. Successivamente, l’applicazione inizia il loop dei messaggi, ovvero si effettua un ciclo while che chiama le funzioni GetMessage, TranslateMessage e DispatchMessage le quali rispettivamente esaminano la coda dei messaggi e, se un nuovo messaggio è presente, lo interpretano e lo inviano alla corrispondente funzione di gestione: message handler. Nell’esempio la funzione di gestione dei messaggi è la CALLBACK WndProc che si occupa

Page 144: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 143 di 299

d’implementare la reazione dell’applicazione ad ognuno dei messaggi riconosciuti. Ad esempio, in risposta al messaggio WM_PAINT che è inviato tutte le volte che è necessario ridisegnare la finestra, la funzione WndProc provvede a visualizzare il messaggio a video. La funzione WndProc è definita funzione di callback perché non è chiamata esplicitamente dall’applicazione ma è chiamata direttamente dal loop dei messaggi ogni volta che se ne presenti la necessità, ovvero è arrivato un nuovo messaggio. I messaggi non gestiti direttamente dall’applicazione sono passati alla funzione DefWindowProc per segnalare al SO che il messaggio non è gestito e quindi è sufficiente un trattamento di default da parte di Windows. Il gestore del messaggio WM_DESTROY richiama la funzione PostQuitMessage per inviare un messaggio WM_QUIT alla coda dei messaggi e causare in questo modo la corretta terminazione dell’applicazione. Il messaggio WM_DESTROY è inviato ad un’applicazione immediatamente prima della chiusura della finestra principale ed è a cura del programmatore inviare il messaggio WM_QUIT altrimenti l’applicazione non potrebbe terminare correttamente. In conclusione, un’applicazione Windows non ha un vero e proprio main ma si limita ad aprire una o più finestre e a posizionarsi in un ciclo di attesa loop dei messaggi, aspettando che il SO invii messaggi all’applicazione in risposta ai vari eventi che si possono verificare durante l’esecuzione dell’applicazione. I messaggi in arrivo diretti ad un’applicazione sono accodati in attesa dell’elaborazione e si continua l’esecuzione in questo modo fino all’arrivo di un messaggio di richiesta di terminazione dell’applicazione WM_QUIT che è inviato quando l’utente preme il tasto (X) su di una finestra, oppure seleziona la voce File/Esci. La complessità di sviluppo delle applicazioni Windows deriva pertanto dalla necessità di prevedere in fase di progetto tutti i possibili messaggi che dovranno essere gestiti e quindi richiederanno la scrittura di una routine di gestione apposita.

APPLICAZIONE MFC (MICROSOFT FOUNDATION CLASSES) Lo scopo di MFC è d’incapsulare all’interno di classi le API di Windows e di fornire, inoltre, un ambiente di programmazione Windows OO (Object Oriented) che aiuti il programmatore nel complesso meccanismo di sviluppo delle applicazioni dotate d’interfaccia grafica. Le classi utilizzate da Visual C++ per la gestione dell’interfaccia grafica e per la gestione degli eventi non fanno parte dello standard del C++ ma appartengono alla libreria MFC che è collocata tra le API di Windows e l’applicazione.

Le classi MFC forniscono un’architettura di sviluppo che semplifica la gestione dei compiti più ripetitivi di ogni applicazione Windows come la gestione dei messaggi e offre alcuni strumenti per separare l’implementazione dei dati di un’applicazione dalla sua rappresentazione grafica raggiungendo un livello di astrazione maggiore rispetto all’SDK. L’utilizzo di MFC e del Visual C++ aggiunge alle applicazioni Windows tutti i benefici derivanti dalle tecniche OO come l’incapsulamento, l’ereditarietà e le funzioni virtuali. È possibile, infatti, ereditare da una qualsiasi classe MFC e creare in poco tempo potenti elementi d’interfaccia personalizzati e avvalersi, inoltre, della libreria di template e container messi a disposizione del programmatore da STL; le classi MFC hanno una struttura ad albero, la superclasse da cui derivano tutte è la classe CObject.

Page 145: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 144 di 299

Classi per la gestione dell’architettura dell’applicazione CWinApp: deriva da CwinThread, ProcessShellCommand elabora gli argomenti della linea di comando, OnFileOpen è File/Apri. CDocument: contiene metodi per accedere ai file. Classi per la gestione della memoria CArray, CString. Classi per la gestione delle eccezioni CException. Classi per la gestione delle finestre CWind contiene le proprietà della finestra. CDialog finestra di dialogo. CFrameWind, CView, CFormView. Classi per la gestione dei file CFile. Classi per la gestione dei DB (DataBase) CDataBase, CRecordSet, CRecordView per l’accesso ODBC (Open DB Connectitvity). CDaoWorkspace, CDaoDatabase per l’accesso DAO (Data Access Object). Classi per la gestione della grafica CDC (Class Device Control), GDI (Graphic Device Interface). Fare clic su File/Nuovo/Progetto… (CTRL+MAIUSC+N), nella finestra Nuovo progetto, selezionare le voci seguenti Altri linguaggi/VisualC++/Win32/Progetto console Win32. Nella finestra Creazione guidata applicazione Win32, selezionare Tipo di applicazione: Applicazione Windows, Opzioni aggiuntive: Progetto vuoto. Fare clic con il pulsante destro del mouse su Esplora soluzioni, dal menu contestuale selezionare Aggiungi/Nuovo elemento… (CTRL+MAIUSC+A)/File di C++. Fare clic su Progetto/Proprietà di Finestra…, nella finestra che si apre selezionare Proprietà di configurazione /Generale/Uso di MFC/Usa MFC in una DLL condivisa, per comunicare al compilatore che si vuole linkare dinamicamente la libreria MFC all’eseguibile.

File FINESTRA.CPP #include <afxwin.h> // classe principale applicazione MFC class CMyApp : public CWinApp { public:

Page 146: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 145 di 299

// il metodo InitInstance dev’essere implementato in CMyApp virtual BOOL InitInstance (); }; // classe per la finestra dell'applicazione class CMainWindow : public CFrameWnd { public: // metodo per la creazione della finestra principale è anche il costruttore CMainWindow (); protected: // dichiarazione della mappa dei messaggi e delle funzioni di gestione dei msg afx_msg void OnPaint (); DECLARE_MESSAGE_MAP (); }; // istanziamo un oggetto di tipo applicazione MFC CMyApp myApp; // override del metodo InitInstance BOOL CMyApp::InitInstance () { m_pMainWnd = new CMainWindow; m_pMainWnd->ShowWindow (m_nCmdShow); m_pMainWnd->UpdateWindow (); return TRUE; } // mappa dei messaggi gestiti dall’applicazione BEGIN_MESSAGE_MAP (CMainWindow, CFrameWnd) ON_WM_PAINT () END_MESSAGE_MAP () /* override della funzione di creazione della finestra principale necessaria per modificare il titolo (il costruttore della classe CmainWindow provvede a creare la finestra) */ CMainWindow::CMainWindow () { Create(NULL, _T("Applicazione MFC"), WS_OVERLAPPEDWINDOW, rectDefault ); } // gestione dell’evento (messaggio) WM_PAINT void CMainWindow::OnPaint () { // istanziamo una client area CRect rect; GetClientRect (rect); // istanziamo un device context CPaintDC dc (this); }

Un’applicazione MFC non ha una funzione WinMain, le funzionalità sono svolte da un oggetto di tipo applicazione che dev’essere istanziato in maniera globale derivandolo dalla superclasse CwinApp e deve esistere un solo oggetto di tipo applicazione e se si vuole che l’applicazione abbia una finestra è necessario effettuare l’implementazione del metodo InitInstance nella sottoclasse, in questo caso CMyApp, per crearne una e poi visualizzarla tramite i metodi ShowWindow e UpdateWindow.

Page 147: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 146 di 299

La funzione virtuale InitInstance è utilizzata per effettuare qualsiasi operazione d’inizializzazione al run e deve sempre terminare con il return TRUE. La finestra dell’applicazione è creata dinamicamente all’interno di InitInstance tramite l’istruzione m_pMainWnd = new CmainWindow; in questo modo è creata una nuova istanza di un oggetto appartenente alla classe CMainWindow derivata dalla superclasse CFrameWnd e se ne è implementato il costruttore, istruzione Create. La superclasse CFrameWnd fornisce l’implementazione di una generica finestra che può essere contenitore per altre finestre oppure essere utilizzata per accedervi direttamente. L’applicazione deve gestire il refresh del messaggio all’interno della finestra in risposta al messaggio WM_PAINT, inviato dal SO che avviene all’interno del metodo OnPaint della classe CMainWindow. Per poter disegnare o scrivere del testo all’interno di una finestra è necessario disporre di un oggetto chiamato Device Context che serve ad astrarre ciò che si vuole disegnare dal destinatario fisico del disegno; non è possibile effettuare alcun tipo di output diretto su di una finestra senza l’utilizzo di un Device Context. Una volta istanziato l’oggetto di tipo Device Context è possibile richiamarne il metodo DrawText per inviare del testo ad un’area rettangolare all’interno della finestra definita istanziando un oggetto di classe CRect. Il metodo GetClientRect restituisce un riferimento ad un oggetto di tipo CRect che rappresenta la parte di una finestra su cui è possibile effettuare un output, è necessario questo passaggio intermedio dato che l’area di una finestra varia sempre a causa del ridimensionamento effettuato dall’utente e quindi non è possibile stabilire in maniera assoluta le coordinate e l’ampiezza dell’area utilizzabile. Il loop dei messaggi e le funzioni di callback che erano utilizzate con l’SDK sono state incapsulate all’interno di MFC e quindi il programmatore deve solo utilizzare le macro BEGIN_MESSAGE_MAP e END_MESSAGE_MAP per specificare quali saranno i messaggi gestiti dall’applicazione. Quando si dichiara la mappa dei messaggi è necessario indicare sia la superclasse sia la sottoclasse di cui si dovranno gestire i messaggi e inoltre è necessario che la sottoclasse contenga un metodo per ogni messaggio gestito, la CMainWindow risponde all’evento ON_WM_PAINT dichiarato all’interno della mappa dei messaggi con la funzione OnPaint. Risorse Sono una parte fondamentale di ogni applicazione Windows dotata d’interfaccia grafica, dal menu contestuale del progetto selezionare Aggiungi/Risorsa…

Page 148: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 147 di 299

Create le risorse dell’applicazione, è necessario salvare il file FINESTRA.RC e per indicare nel sorgente che si utilizzano includere il file header "resource.h" MFC non è solo un’interfaccia OO verso le API di Windows ma è anche una serie di meccanismi applicativi e di tecniche di sviluppo di cui l’architettura Document/View costituisce una parte fondamentale. L’architettura applicativa Document/View può essere definita come un insieme di classi e di tecniche di programmazione che permette di astrarre la rappresentazione dei dati di un’applicazione Document, dalla sua visualizzazione grafica all’interno di una finestra View. Il vantaggio di questo tipo di architettura, oltre a quello intrinseco di separare i dati dall’interfaccia grafica, è rappresentato dal fatto che MFC si occupa di gestire gli aspetti grafici dell’applicazione lasciando quindi al programmatore il compito di scrivere il codice concentrandosi sulle classi che gli servono per risolvere il problema specifico. Per fare un esempio, l’architettura Document/View permette di gestire il salvataggio e la lettura dei dati di un’applicazione senza praticamente dover scrivere codice e gestisce, inoltre, in maniera trasparente altre situazioni come la richiesta di salvare prima di uscire se un documento è stato modificato. Un altro vantaggio di Document/View consiste nell’elevato grado di astrazione della vista rispetto al dispositivo fisico di output; infatti, effettuare operazioni di output su di una vista permette di raggiungere l’indipendenza rispetto al fatto che poi la vista sia mappata fisicamente sullo schermo oppure sulla stampante, rendendo l’implementazione di sottoprogrammi di stampa un compito più semplice. Un’applicazione Document/View può, inoltre, aggiornare il registro, specificando la propria estensione dei file in modo che sia sufficiente fare doppio clic su di un file salvato dall’applicazione per poterlo caricare ed eseguire allo stesso tempo; è inoltre possibile aprire direttamente un nuovo file semplicemente trascinandolo all’interno della finestra dell’applicazione, tecnica drag & drop. Archiettura Document/View La struttura base dell’architettura di un’applicazione Document/View è formata da una finestra container o frame window (CFrameWnd) che rappresenta la finestra principale dell’applicazione, la quale agisce da contenitore per una o più viste (CView) che non sono altro che rappresentazioni dei dati dell’applicazione memorizzati all’interno di un oggetto appartenente alla classe documento (CDocument). Le applicazioni Document/View possono essere di tipo SDI (Single Document Interface) ovvero che possono gestire una singola vista per volta di un oggetto documento all’interno della finestra contenitore e MDI ovvero dotate di viste multiple su oggetti documento multipli. MFC permette, inoltre, di creare a run-time documenti, viste e finestre container tramite il processo chiamato Dynamic Object Creation. Timer Le applicazioni che devono eseguire operazioni a intervalli regolari hanno a disposizione l’oggetto timer, espresso in milli secondi. Per gestire i timer con MFC è necessario utilizzare due funzioni CWnd::SetTimer e CWnd::KillTimer. Ogni volta che l’intervallo fissato è trascorso il timer deve comunicarlo all’applicazione e per fare questo possono essere utilizzati due metodi. 1. Inviando ad una finestra specificata il messaggio WM_TIMER. 2. Chiamando una funzione definita all’interno dell’applicazione detta funzione di callback. Gestire il messaggio WM_TIMER è il metodo più semplice ma se si hanno timer multipli in un’applicazione è preferibile utilizzare il metodo della funzione di callback. Windows non è un SO real-time quindi un timer settato ad un intervallo di 700 millisecondi non dà alcuna garanzia che scatterà con la massima precisione, piuttosto la media, dopo un centinaio di attivazioni, convergerà verso questo valore.

Page 149: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 148 di 299

Un’applicazione Windows generica trascorre la maggior parte del suo tempo a elaborare i messaggi in arrivo dal SO in risposta ad operazioni d’input da parte dell’utente finale. Quando nella coda non sono disponibili messaggi un’applicazione si mantiene in uno stato d’inattività (idle). Per ottimizzare l’efficienza di un’applicazione si può utilizzare questo momento d’inattività per portare avanti delle elaborazioni in background che non hanno bisogno d’interagire con l’utente, per esempio la cancellazione di file temporanei o la scrittura d’informazioni in un file di log. La classe applicazione MFC CWinApp mette a disposizione per questo tipo di elaborazioni una routine di gestione chiamata OnIdle in cui si può posizionare del codice da eseguire durante il ciclo d’idle. Fare clic su File/Nuovo/Progetto… (CTRL+MAIUSC+N), nella finestra Nuovo progetto, selezionare, Altri linguaggi/Visual C++/MFC/Applicazione MFC.

Page 150: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 149 di 299

Page 151: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 150 di 299

Scegliere un’interfaccia basata su finestre di dialogo, è un pannello all’interno del quale s’inseriscono i vari controlli, fare clic sul pulsante Fine, compare la finestra seguente.

La creazione guidata applicazione ha generato i seguenti file. File FINESTRA.H File d’intestazione principale per l’applicazione, include altre intestazioni specifiche del progetto quale RESOURCE.H e dichiara la classe di applicazione CFinestraApp. File RESOURCE.H File d’intestazione principale standard che definisce i nuovi ID risorse. File STDAFX.H, STDAFX.CPP Sono utilizzati per generare il file d’intestazione precompilato HELLO.PCH e il file dei tipi precompilato STDAFX.OBJ. File FINESTRADLG.H e FINESTRADLG.CPP Questi file contengono la classe CfinestraDlg che definisce il comportamento della finestra di dialogo principale dell’applicazione; il modello della finestra di dialogo si trova in FINESTRA.RC e può essere modificato in Visual C++. File FINESTRA.CPP File di origine principale dell’applicazione contenente la classe di applicazione CFinestraApp. File FINESTRA.RC Elenco di tutte le risorse utilizzate dall’applicazione, include le icone, le bitmap e i cursori memorizzati nella sotto cartella RES.

Page 152: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 151 di 299

File RES\FINESTRA.ICO Icona dell’applicazione, è inclusa dal file di risorse principale FINESTRA.RC. File RES\FINESTRA.RC2 Contiene le risorse non modificate da Visual C++, inserire in questo file tutte le risorse non modificabili dall’editor di risorse.

Il prefisso TODO: indica le parti del codice sorgente da aggiungere o personalizzare. Fare clic su Debug/Avvia debug (F5), appare la finestra con la barra del titolo, il menu controllo con informazioni sull’applicazione.

La scheda Visualizza/Visualizzazione classi (CTRL+MAIUSC+C) visualizza la struttura

Page 153: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 152 di 299

delle classi impostata dal sistema per la costruzione dell’interfaccia grafica, oltre alla struttura delle classi, non legate a elementi grafici, definite dal programmatore. Fare clic su Visualizza/Altre finestre/Visualizzazione risorse (CTRL+MAIUSC+E). Doppio clic su IDD_FINESTRA_DIALOG, è visualizzata la finestra dell’applicazione.

La scheda Visualizza/Casella degli strumenti (CTRL+ALT+X) visualizza i controlli che possono essere posizionati all’interno di una finestra.

Posizionare un pulsante in alto a sinistra nella finestra, tutti i controlli possiedono un insieme di proprietà predefinite, per impostarle, fare clic su Visualizza/Finestra Proprietà (F4).

Page 154: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 153 di 299

Un evento è un avvenimento asincrono che si verifica quando l’applicazione è in esecuzione, per esempio l’apertura/chiusura di finestre, i clic del mouse. Sono asincroni in quanto non è possibile prevedere quando accadranno. Quando si verifica un evento, il SO lo intercetta e individua l’oggetto che ha determinato l’evento, chiamato origine dell’evento, quindi il gestore dell’evento, una funzione, indica il comportamento da seguire per quell’evento. Windows gestisce gli eventi basandosi sull’invio e la ricezione di messaggi. Allora bisogna distinguere tra sorgente del messaggio e il destinatario del messaggio, quindi ogni messaggio è caratterizzato da tre caratteristiche. 1. Un puntatore alla finestra destinataria. 2. Un identificativo del messaggio: message ID. 3. Parametri per interpretare il messaggio.

Finestre di dialogo Servono per trasmettere messaggi di errore, la sintassi è la seguente. MessageBox("Messaggio","Titolo",[ListaProprietà]) Dove. Messaggio indica il messaggio da visualizzare all’interno della finestra. Titolo indica il titolo della finestra. ListaProprietà sono le proprietà, opzionali che determinano l’aspetto grafico. Adesso bisogna associare l’evento al pulsante, in pratica il metodo dell’oggetto finestra che descrive il comportamento del controllo pulsante. Selezionare il pulsante Visualizza, fare clic con il tasto destro e dal menu contestuale selezionare Aggiungi gestore eventi…

Page 155: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 154 di 299

Si apre la creazione guidata seguente.

Tipo di messaggio: è l’elenco dei messaggi che supporta il controllo selezionato. Nome gestore funzioni: è la funzione che il programmatore deve definire per rispondere al messaggio, il nome è il seguente: prefisso On seguito dal nome del messaggio BnClicked e dall’elemento che lo ha generato Mfcbutton1. Per esempio, la funzione OnBnClickedMfcbutton1 è in risposta al messaggio BN_CLICKED che è generato dal clic sul controllo di nome Mfcbutton1. Questa funzione è vista da Visual Studio come un metodo della classe relativa alla finestra contenitore del controllo. void CFinestraDlg::OnBnClickedMfcbutton1()

Page 156: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 155 di 299

{ MessageBox(_T("Perdita dati"),_T("Attenzione"),MB_ICONEXCLAMATION|MB_OKCANCEL|MB_APPLMODAL); } Eseguire l’applicazione.

Page 157: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 156 di 299

MODULO 3

BORLANDC BorlandC Debugger Tools Funzioni di gestione dello schermo Funzioni sonore BorlandC avanzato BorlandC++ Assembly – BorlandC

Page 158: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 157 di 299

BORLANDC

COMPILAZIONE CLI Compilatore Il compilatore ricerca i file include nel percorso della cartella INCLUDE di BorlandC. Si specifica questo percorso con l’opzione (-I). INSTALL scrive un file di configurazione TURBOC.CFG che imposta questo percorso alla cartella dove ha copiato tutti i file H di BorlandC. Può essere modificato, per cambiare il percorso di default. File TURBO.CFG -IC:\COMPILER\BORLANDC\INCLUDE -LC:\COMPILER\BORLANDC\LIB File TLINK.CFG -LC:\COMPILER\BORLANDC\LIB C:\COMPILER\BORLANDC\BIN>BCC Borland C++ Version 3.1 Copyright (c) 1992 Borland International Syntax is: BCC [ options ] file[s] * = default; -x- = turn switch x off -1 80186/286 Instructions -2 80286 Protected Mode Inst. -Ax Disable extensions -B Compile via assembly -C Allow nested comments -Dxxx Define macro -Exxx Alternate Assembler name -G Generate for speed -Hxxx Use pre-compiled headers -Ixxx Include files directory -K Default char is unsigned -Lxxx Libraries directory -M Generate link map -N Check stack overflow -Ox Optimizations -P Force C++ compile -Qxxx Memory usage control -S Produce assembly output -Txxx Set assembler option -Uxxx Undefine macro -Vx Virtual table control -Wxxx Create Windows application -X Suppress autodep. output -Yx Overlay control -Z Suppress register reloads -a Generate word alignment -b * Treat enums as integers -c Compile only -d Merge duplicate strings -exxx Executable file name -fxx Floating point options -gN Stop after N warnings -iN Max. identifier length -jN Stop after N errors -k Standard stack frame -lx Set linker option -mx Set Memory Model -nxxx Output file directory -oxxx Object file name -p Pascal calls -r * Register variables -u * Underscores on externs -v Source level debugging -wxxx Warning control -y Produce line number info -zxxx Set segment names File CIAO.C #include <stdio.h> #include <conio.h> int main (void) { clrscr(); printf ("\nCiao, mondo in BorlandC ");

Page 159: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 158 di 299

getch(); return(0); } c:\compiler\borlandc\bin>bcc ciao.c Borland C++ Version 3.1 Copyright (c) 1992 Borland International ciao.c: Turbo Link Version 5.1 Copyright (c) 1992 Borland International Available memory 4192120 Il comando provoca la compilazione e il linkig dell’applicazione con i corretti file di libreria.

C:\Compiler\BORLANDC\BIN>bcc -c ciao.c Borland C++ Version 3.1 Copyright (c) 1992 Borland International ciao.c: Available memory 4192120 In questo caso, il comando provoca solo la compilazione dell’applicazione. Linker C:\COMPILER\BORLANDC\BIN>TLINK Turbo Link Version 5.1 Copyright (c) 1992 Borland International Syntax: TLINK objfiles, exefile, mapfile, libfiles, deffile @xxxx indicates use response file xxxx /m Map file with publics /x No map file at all /i Initialize all segments /l Include source line numbers /L Specify library search paths /s Detailed map of segments /n No default libraries /d Warn if duplicate symbols in libraries /c Case significant in symbols /3 Enable 32-bit processing /o Overlay switch /v Full symbolic debug information /P[=NNNNN] Pack code segments /A=NNNN Set NewExe segment alignment /ye Expanded memory swapping /yx Extended memory swapping /e Ignore Extended Dictionary /t Create COM file (same as /Tdc) /C Case sensitive exports and imports /Txx Specify output file type /Tdx DOS image (default) /Twx Windows image (third letter can be c=COM, e=EXE, d=DLL) Con TLINK è necessario che il primo file oggetto presente sulla linea di comando sia uno dei moduli d’inizializzazione. Esiste un modulo d’inizializzazione per ognuno dei modelli di memoria, si deve scegliere quel modulo che corrisponde al modello di memoria usato in compile-time. Inoltre, dev’essere collegato anche il file di libreria appropriato. Modulo d’inizializzazione Libreria Modello di memoria C0T.OBJ CT.LIB Tiny C0S.OBJ CS.LIB Small C0C.OBJ CC.LIB Compact

Page 160: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 159 di 299

C0M.OBJ CM.LIB Medium C0L.OBJ CL.LIB Large C0H.OBJ CH.LIB Huge C:\Compiler\BORLANDC\BIN>tlink c0s.obj ciao.obj,ciao.exe, ,cs.lib Turbo Link Version 5.1 Copyright (c) 1992 Borland International File CIAO.MAP La mappa del linker dell’applicazione indica la posizione in memoria di segmenti, di variabili public e globali e di funzioni: utile in fase di debugging. Start Stop Length Name Class 00000H 01D65H 01D66H _TEXT CODE 01D70H 01D70H 00000H _FARDATA FAR_DATA 01D70H 01D70H 00000H _FARBSS FAR_BSS 01D70H 01D70H 00000H _OVERLAY_ OVRINFO 01D70H 01D70H 00000H _1STUB_ STUBSEG 01D70H 020C7H 00358H _DATA DATA 020C8H 020C9H 00002H _CVTSEG DATA 020CAH 020CFH 00006H _SCNSEG DATA 020D0H 020D0H 00000H _CONST CONST 020D0H 020DBH 0000CH _INIT_ INITDATA 020DCH 020DCH 00000H _INITEND_ INITDATA 020DCH 020DCH 00000H _EXIT_ EXITDATA 020DCH 020DCH 00000H _EXITEND_ EXITDATA 020DCH 0211DH 00042H _BSS BSS 0211EH 0211EH 00000H _BSSEND BSSEND 02120H 0219FH 00080H _STACK STACK Detailed map of segments 0000:0000 02C2 C=CODE S=_TEXT G=(none) M=c0.ASM ACBP=28 01D7:0000 0000 C=FAR_DATA S=_FARDATA G=(none) M=c0.ASM ACBP=68 01D7:0000 0000 C=FAR_BSS S=_FARBSS G=(none) M=c0.ASM ACBP=68 01D7:0000 0000 C=OVRINFO S=_OVERLAY_ G=(none) M=c0.ASM ACBP=68 01D7:0000 0000 C=STUBSEG S=_1STUB_ G=(none) M=c0.ASM ACBP=68 01D7:0000 00AA C=DATA S=_DATA G=DGROUP M=c0.ASM ACBP=68 01D7:00AA 001A C=DATA S=_DATA G=DGROUP M=..\OUTPUT\CIAO.C ACBP=48 01D7:00C4 0002 C=DATA S=_DATA G=DGROUP M=ATEXIT ACBP=48 01D7:0358 0000 C=DATA S=_CVTSEG G=DGROUP M=c0.ASM ACBP=48 01D7:035A 0000 C=DATA S=_SCNSEG G=DGROUP M=c0.ASM ACBP=48 01D7:0360 0000 C=CONST S=_CONST G=DGROUP M=c0.ASM ACBP=48 01D7:0360 0000 C=INITDATA S=_INIT_ G=DGROUP M=c0.ASM ACBP=48 01D7:036C 0000 C=INITDATA S=_INITEND_ G=DGROUP M=c0.ASM ACBP=28 01D7:036C 0000 C=EXITDATA S=_EXIT_ G=DGROUP M=c0.ASM ACBP=48 01D7:036C 0000 C=EXITDATA S=_EXITEND_ G=DGROUP M=c0.ASM ACBP=28 01D7:036C 0000 C=BSS S=_BSS G=DGROUP M=c0.ASM ACBP=48 01D7:03AE 0000 C=BSSEND S=_BSSEND G=DGROUP M=c0.ASM ACBP=28 0212:0000 0080 C=STACK S=_STACK G=(none) M=c0.ASM ACBP=74 Address Publics by Name 01D7:0000 idle DATASEG@ 0000:02BE idle DGROUP@ Address Publics by Value 0000:0000 idle __turboCrt 0000:0000 Abs __cvtfak 01D7:0358 __RealCvtVector

Page 161: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 160 di 299

01D7:036C __atexittbl Program entry point at 0000:0000

IDE (INTEGRATED DEVELOPMENT ENVIRONMENT) Via linea di comando, per entrare nell’IDE si usa il comando seguente. c:\compiler\borlandc\bin>bc Da Windows si usa un collegamento. Start/Tutti i programmi/Compilatori/Borland/C

L’IDE consente di editare, compilare, linkare ed eseguire un’applicazione, senza mai uscire dall’ambiente.

Preprocessore Preprocessore via linea di comando. C:\COMPILER\BORLANDC\BIN>CPP Borland C++ Preprocessor Version 3.1 Copyright (c) 1992 Borland International Syntax is: CPP [ options ] file[s] * = default; -x- = turn switch x off -Ax Disable extensions -C Allow nested comments

Page 162: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 161 di 299

-Dxxx Define macro -Ixxx Include files directory -P * Include source line info -Uxxx Undefine macro -gnnn Stop after N warnings -innn Maximum identifier length N -jnnn Stop after N errors -mc Compact model -mh Huge Model -ml Large Model -mm Medium Model -ms * Small Model -mt Tiny Model -nxxx Output file directory -oxxx Output file name -p Pascal calls -w Enable all warnings -wxxx Enable warning xxx -w-xxx Disable warning xxx Esempio. #include <stdio.h> #include <conio.h> #define NAME "urm" #define BEGIN { #define END } int main (void) BEGIN clrscr(); printf("\n\n%s", NAME); getch();

return(0); END C:\COMPILER\BORLANDC\BIN>CPP -P CIAO.C Borland C++ Preprocessor Version 3.1 Copyright (c) 1992 Borland International ciao.c: Available memory 4217364 File CIAO.I ciao.c 1: C:\COMPILER\BORLANDC\INCLUDE\stdio.h 1: … C:\COMPILER\BORLANDC\INCLUDE\stdio.h 250: C:\COMPILER\BORLANDC\INCLUDE\_defs.h 1: … C:\COMPILER\BORLANDC\INCLUDE\_defs.h 106: C:\COMPILER\BORLANDC\INCLUDE\_nfile.h 1: … C:\COMPILER\BORLANDC\INCLUDE\_nfile.h 16: C:\COMPILER\BORLANDC\INCLUDE\_null.h 1: … C:\COMPILER\BORLANDC\INCLUDE\_null.h 17: ciao.c 2: C:\COMPILER\BORLANDC\INCLUDE\conio.h 1: … C:\COMPILER\BORLANDC\INCLUDE\conio.h 157: ciao.c 3: ciao.c 4: ciao.c 5: ciao.c 6: int main (void) ciao.c 7: { ciao.c 8: clrscr();

Page 163: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 162 di 299

ciao.c 9: printf("\n\n%s", "urm"); ciao.c 10: getch(); ciao.c 11: return(0); ciao.c 12: } ciao.c 13: Stampa su stampante #include <stdio.h> int main (void) { fprintf (stdprn, "Hello, world! \n"); fprintf (stdprn, "\f"); /* form feed */ fprintf (stdprn, "\x00E Hello, world! \n"); /* 00E shift out printer enlangerd*/ return (0); } Argomenti dalla linea di comando nell’ambiente IDE. Fare clic su Run/Arguments…

THELP (Turbo Help) È un’utility residente TSR (Terminate and Stay Resident) che consente di accedere alle informazioni della guida online. Si usa quando non si utilizza l’IDE ma si sta utilizzando un editor differente o con la versione a linea di comando, oppure con un altro prodotto, per esempio Turbo Debugger; richiede circa 21 KB di memoria. Per utilizzare THELP è necessario caricarlo prima dell’applicazione che s’intende utilizzare: assicurarsi che TCHELP.TCH, il file di testo contenente le informazioni della guida online, sia nella cartella corrente o si utilizzi l’opzione (/F) per specificare la cartella in cui si trova, oppure si modifichi il file TCHELP.CFG. C:\COMPILER\BORLANDC\BIN>THELP /? Turbo Help Version 3.0 Copyright (c) 1992 Borland International USAGE: THELP [options] /C#xx Select color: #=color number, xx=hex color value /Fname Full pathname of help file /H,/? Display this help screen /Kxxyy Hotkey: xx=shift state, yy=scan code /U Remove Thelp from memory /Wx,y,w,h Set the window size (Ex:/W0,0,80,25) Prima di accedere all’applicazione: questo è necessario solamente una volta. Dopo che ci si trova nell’applicazione, è possibile attivare THELP in ogni momento. È sufficiente posizionare il cursore nell’elemento di cui si desidera visualizzare la guida e premere il tasto di attivazione, il tasto di default è il 5 del tastierino numerico.

Page 164: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 163 di 299

Inoltre il programmatore assembly alle prime armi può trovare un utile aiuto all’acquisizione delle modalità esecutive di un’applicazione in questo modo. c:\> bcc -S filename <INVIO> Il compilatore BORLANDC genera codice assembly. c:\> DEBUG filename <INVIO> c:\> TD filename <INVIO> Gli ambienti di debugging: debug di MS-DOS e TurboDebugger permettono l’esecuzione di un’istruzione alla volta e contemporaneamente seguire le variazioni che subiscono i registri e la memoria nel corso dell’applicazione.

Page 165: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 164 di 299

DEBUGGER

INTRODUZIONE La Borland mette a disposizione due debugger. 1. Nell’ambiente IDE sotto l’opzione Debug. 2. TD (TurboDebugger), è un’applicazione vera e propria. Le differenze tra il debugger integrato e il debugger stand-alone, dal punto di vista delle potenzialità, sono numerose. Per controllare un’applicazione semplice il debugger presente nell’IDE è più che sufficiente ma quando l’applicazione si fa più grande è necessario utilizzare TD. È bene chiarire subito, per non generare troppi entusiasmi che i debugger non trovano da soli gli errori ma è grazie al gran numero d’informazioni ottenibili tramite questi strumenti che gli errori saltano fuori, quasi, da soli.

DEBUGGER INTEGRATO (ALT+D) Il file dev’essere compilato con: O/C/A/Debug on. Corretti gli errori, per generare un EXE più piccolo, selezionare O/B/ None. Trace R/T (F7) l’unità base di run non è l’istruzione ma la riga evidenziata dalla barra di esecuzione; per evitare il tracing dentro le funzioni corrette R/S (F8), in Basic si chiamano TRON e TROFF, per tracciare l’attività di un’applicazione si usavano, in mancanza di strumenti di debug, istruzioni printf per controllare come precedeva l’applicazione. Watches Visualizza variabili, espressioni, durante il run D/W/A (CTRL+F7) con F6 si entra nella window di watch e ci si sposta con le frecce, INS inserisce una nuova espressione e DEL la cancella, con ENTER si entra in edit watch e si modifica. Reset Chiude i file, libera l’heap, azzera lo stack R/P (CTRL+F2). #include <stdio.h> void swapp(int x,int y); int main(void) { int x=20,y=30,w=10,z=40; swapp(x,w); swapp(y,x); swapp(z,y); printf (“W = %d X = %d Y = %d Z = %d”,w,x,y,z); getch();return(0); } void swapp(int x,int y) { int t; t=x; x=y; y=t; }

Page 166: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 165 di 299

D/W/A &z, w, z, x, y È possibile modificare il source ma con F7 o F8 comparirà la stringa seguente. SOURCE MODIFIED, REBUILD [Y/N] per Y bisogna ricompilare. Go to cursor Noioso caricare 100 elementi step by step. Run fino al cursore R/G (F4) poi F7. #include <stdio.h> #include <conio.h> #include <stdlib.h> const int N=100; typedef int list[N]; void sort (list ll,int cc); int main(void) { int i,c=N;list l; clrscr(); for (i=0;i<N;i++) l[i]=rand(); sort (l,c); for (i=0;i<N;i++) printf (“%d\n”,l[i]); getch();return(0); } void sort (list ll,int cc) { } Breakpoints Punti di arresto al massimo 21, per visualizzare dati, modificarli, per vedere la reazione con valori limite, posizionare il cursore sulla linea D/T (CTRL+F8) linea evidenziata il run si ferma utilizzo le opzioni del debug; STOP e CONT del Basic. Toggle breakpoints È possibile marcare una riga per fermare il run. Navigating Individua funzioni S/L. Inspect Ispeziona e modifica i dati D/I (ALT+F4), otto finestre, ordinal, pointer, array, struct, union, function, class, type, constant. Evaluate/Modify Permette di valutare e modificare in modo interattivo variabili, strutture, espressioni D/E (CTRL+F4), al limite è una calcolatrice, pericoloso modificare in run-time, si hanno tre campi: Expression, Result New, Value. #include <stdio.h> struct { int count; char name[14]; } cliente={5000,"rossi"}; int list[5]={1, 2, 3, 4, 5}; int *ptr=list; int main(void) { return(0)

Page 167: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 166 di 299

;} list:: {1, 2, 3, 4, 5}.list [2], 3: 3, 4, 5. list[2], 3x: 0x3, 0x4, 0x5. list, m: 01 00 02 00 03 00 04 00 05 00. ptr, p: DS:01A4. *ptr, 3: 1, 2, 3. cliente: {5000, "rossi"}. cliente, r: {count: 5000, name:"rossi"}

Call stack D/C (CTRL+F3) mostra l’elenco e la sequenza delle chiamate a funzioni al momento attive nello stack, al massimo 128. #include <stdio.h> #include <conio.h> double power (int base, int exp); int main(void) { clrscr(); printf (”2^14 %f”, power(2,14)); getch(); return(0); } double power (int base, int exp) { if (exp<=0) return 1; else return base*power (base, exp-1); }

Page 168: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 167 di 299

DEBUGGER STAND-ALONE C:\COMPILER\BORLANDC\BIN>TD /? Syntax: TD [options] [program [arguments]] -x- = turn option x off -c<file> Use configuration file <file> -do,-dp,-ds Screen updating: do=Other display, dp=Page flip, ds=Scree -h,-? Display this help screen -i Allow process id switching -k Allow keystroke recording -l Assembler startup -m<#> Set heap size to # kbytes -p Use mouse -r Use remote debugging -rn<L;R> Debug on a network with local machine L and remote machin -rp<#> Set COM # port for remote link -rs<#> Remote link speed: 1=slowest, 2=slow, 3=medium, 4=fast -sc No case checking on symbols -sd<dir> Source file directory <dir> -sm<#> Set spare symbol memory to # Kbytes (max 256Kb) -vg Complete graphics screen save -vn 43/50 line display not allowed -vp Enable EGA/VGA palette save -w Debug remote Windows program (must use -r as well) -y<#> Set overlay area size in Kb -ye<#> Set EMS overlay area size to # 16Kb pages TD presenterà le seguenti finestre. In alto a sinistra il segmento di codice con il puntatore posizionato sulla prossima

istruzione da eseguire. In basso a sinistra l’immagine del segmento dati. A destra l’immagine dei registri della CPU e dello stack. Rispetto alla forma usata per rappresentare le istruzioni va solo notato che il debugger ha sostituito i nomi simbolici, identificatori, con gli indirizzi calcolati, posti tra parentesi quadre. Come scrivere applicazioni per il debugging. Un’istruzione per riga.

Page 169: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 168 di 299

Programmazione modulare. Funzioni non più lunghe di venticinque righe. Utilizzare il passaggio parametri. Progettare funzioni loosely coupled, lascamente connesse. Test e debugging una sezione per volta.

Page 170: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 169 di 299

TOOLS

tdstrip Consente di eliminare la tabella dei simboli da un’applicazione EXE generata da TLINK con l’opzione (/v); questo è un metodo più veloce di eliminare la tabella rispetto a ripetere il link senza l’opzione (/v). Può anche rimuovere le informazioni per il debugging da un file OBJ. Quest’utility può anche essere utilizzata per rimuovere la tabella dei simboli e porla in un file separato, operazione utile quando si vuole convertire un file in formato EXE in un file COM e mantenere in ogni modo la tabella dei simboli di debugging, pone la tabella dei simboli in un file TDS che è trovato da TD quando carica un’applicazione senza la tabella dei simboli. Solo alcuni file EXE possono essere convertiti in file COM: l’applicazione deve iniziare alla locazione 0100H e non può contenere fixup di segmenti. C:\COMPILER\BORLANDC\BIN>TDSTRIP Turbo Debugger Symbol Table Stripper Version 3.1 (c) 1988,92 Borland Intl Syntax: TDSTRIP [options] exefile|objfile [outfile] -s Symbol table is put in a file with the same name as exefile but with an extension of .tds. If you specify an outfile, a symbol table will be put in outfile. If you don’t specify the -s option, the symbol table is removed from the exefile. If you specify an outfile, the original exefile is left unchanged and a version with no symbol table is put in outfile. Ignored on .OBJ’s -c COM file is generated from the EXE file. Ignored on .OBJ’s If you don’t supply an extension with exefile, .exe is presumed. If you don’t supply an extension with outfile, .exe is added when you don’t use -s, .obj is added if an .obj name is provided, and .tds is added when you do use -s. Turbo Debugger will look for the symbol file when it loads an exefile that does not have a symbol table.

tdmap Converte un file MAP, un file ASCII creato dal linker contenente tutti i simboli pubblici di un’applicazione in formato TD e l’aggiunge ad un file EXE. C:\COMPILER\BORLANDC\BIN>TDMAP TDMAP Version 2.5 Copyright (c) 1988, 1991 Borland International, Inc. Syntax: TDMAP [options] MapName [OutName] [options] TDMAP converts mapfile MapName into Turbo Debugger format. -B Assign a type of byte array to all symbols (default is word) -C Treat symbols as case-sensitive -Exxx For any source files named in the map file with no extension, use extension xxx -W New Executable ( Microsoft Windows ) map file -Q Provide no status output Examples: TDMAP MyProg TDMAP PasProg -Epas TDMAP -C small.map small.com

Page 171: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 170 di 299

TDMAP a.map a.tds -Easm -B

TDDEV Mostra i driver di dispositivo caricati nel sistema, la memoria che utilizzano e quali vettori d’interrupt controllano; questa utility è adatta al debugging dei driver di dispositivo. C:\COMPILER\BORLANDC\BIN>TDDEV /? TDDEV Version 1.0 Copyright (c) 1990 Borland International TDDEV produces a report showing the device drivers loaded into the system as well as how much memory each uses, and what interrupt vectors are taken over. TDDEV accepts the following command line syntax: TDDEV [Options] Options may be preceded by either / or -. Valid options are as follows: /R raw, unsorted report. /? write help screen.

tdmem Visualizza la memoria del PC correntemente disponibile, includendo la memoria estesa ed espansa, se esistono, oltre alla memoria convenzionale. Ciò è utile per il debugging di applicazioni residenti e driver di dispositivo. C:\COMPILER\BORLANDC\BIN>TDMEM /? TDMEM Version 1.0 Copyright (c) 1990 Borland International TDMEM produces a report showing what memory resident programs are installed, how much memory each uses, and what interrupt vectors are taken over. TDMEM accepts the following command line syntax: TDMEM [Options] Options may be preceded by either / or -. Valid options are as follows: /V verbose report. /? write this help screen.

Page 172: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 171 di 299

touch C:\COMPILER\BORLANDC\BIN>TOUCH Touch Version 3.0 Copyright (c) 1992 Borland International Usage is: TOUCH filename ...

tdump Consente di esaminare la struttura di qualsiasi file servendosi della loro estensione per determinarne il formato di visualizzazione. Le estensioni riconosciute comprendono numerosi formati di file, compresi i file EXE, OBJ e LIB. Se l’estensione non è riconosciuta, è fornito un listato esadecimale dei contenuti del file. Il formato dell’output può essere controllato servendosi delle opzioni della riga di comando all’avviamento dell’applicazione. Non si limita a visualizzare i contenuti di un file, bensì ne mostra la costruzione; inoltre verifica che la struttura del file corrisponda alla sua estensione, per cui può essere utilizzato per verificare l’integrità di un file. C:\COMPILER\BORLANDC\BIN>TDUMP Turbo Dump Version 3.1 Copyright (c) 1988, 1992 Borland International Syntax: TDUMP [options] InputFile [ListFile] [options] -a ASCII file display -a7 Display file in 7-Bit mode -b# Offset into file for display -e Force Executable file display -el Disable line number display -er Disable relocation record display -ex Disable New Executable Display -h Force hexadecimal file display -l Library format file Display -m Disable C++ name de-mangling -o Force .OBJ format file display -oc CRC Check .OBJ file records -oiID Include .OBJ file record "ID" (i.e. -oiPUBDEF) -oxID Exclude .OBJ file record "ID" -v Verbose display TDUMP will display the following file types & information: DOS Executable files New Executable files & Linear Executable files OMF files (.OBJ & .LIB) Turbo Debugger symbolic debug information Microsoft symbolic debug information

Page 173: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 172 di 299

tdremote C:\COMPILER\BORLANDC\BIN>TDREMOTE /? TD Remote Program Loader Version 3.1 Copyright (c) 1988, 1992 Borland Intl Syntax: TDREMOTE [options] -rn<Name> Run on a network with remote machine named <Name> -rp<#> Set COM # port for remote link: 1=COM1, 2=COM2 -rs<#> Set link speed: 1=9600 bps, 2=19Kbps, 3=38Kbps, 4=115Kbps -w Write options to exe file The default is -rp1 and -rs4 (use COM1 at 115 Kbps). TDREMOTE needs a "null modem" or "printer" cable connecting the serial ports of the two systems. A "straight through" cable will not work. The cable must swap the transmit data (pin 2) and receive data (pin 3) signals. You must also make sure that the speed (set by the -rs switch) is the same on both ends. First, try running the link at the highest speed (-rs4). If this doesn’t work, switch to a lower speed (-rs3, -rs2 or -rs1) and try again. The faster you run the link, the quicker the response of Turbo Debugger.

tdrf C:\COMPILER\BORLANDC\BIN>TDRF TD Remote File Utility Version 3.1 Copyright (c) 1988, 1992 Borland Intl Syntax: TDRF [options] command [arguments] -rn<L;R> Debug on a network with local machine L and remote machine R -rp<#> Set COM # port for remote link: 1=COM1, 2=COM2 -rs<#> Set link speed: 1=9600 bps, 2=19Kbps, 3=38Kbps, 4=115Kbps -w Write options to exe file The default is -rp1 and -rs4 (use COM1 at 115 Kbps). "command" can be either a word similar to the equivalent DOS command or a single letter abbreviation. You can use wildcards with the dir, copy, del and copyfrom commands. The commands are: Command Letter Arguments Description copy, copyto t 1 or 2 Copy file to remote system copyfrom f 1 or 2 Copy file from remote system

Page 174: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 173 di 299

del, erase e 1 Delete file dir d 0 or 1 Directory ren r 2 Rename file md m 1 Make directory rd k 1 Remove directory cd c 0 or 1 Change drive/directory

tfremote C:\COMPILER\BORLANDC\BIN>TFREMOTE /? TF Remote Program Loader Version 2.1 Copyright (c) 1990,92 Borland International Syntax: TFREMOTE [options] -rn<Name> Run on a network with remote machine named <Name> -rp<#> Set COM # port for remote link: 1=COM1, 2=COM2 -rs<#> Set link speed: 1=9600 bps, 2=19Kbps, 3=38Kbps, 4=115Kbps -w Write options to exe file The default is -rp1 and -rs4 (use COM1 at 115 Kbps). TFREMOTE needs a "null modem" or "printer" cable connecting the serial ports of the two systems. A "straight through" cable will not work. The cable must swap the transmit data (pin 2) and receive data (pin 3) signals. You must also make sure that the speed (set by the -rs switch) is the same on both ends. First, try running the link at the highest speed (-rs4). If this doesn’t work, switch to a lower speed (-rs3, -rs2 or -rs1) and try again. The faster you run the link, the quicker the response of Turbo Profiler.

temc C:\COMPILER\BORLANDC\BIN>TEMC TEMC Version 2.4a Copyright (c) 1989, 1992 Borland International Turbo Editor Macro Language Compiler Syntax: TEMC [-c] [-u] <input-file>[.TEM] <config-file>[.TC] Use the -c option to create a new command table. The default is to update the existing table. Use the -u option to write to the CUA command table The default is to write to the ALT command table.

trancopy C:\COMPILER\BORLANDC\BIN>TRANCOPY TRANCOPY 2.0 Copyright (c) 1991 Borland International Syntax: TRANCOPY [-r] <source project> <destination project> Merges the transfer programs in <source project> and <destination project> and writes the results to <destination project>. -r causes the transfer programs in <source project> to replace those in <destination project>.

trigraph C:\COMPILER\BORLANDC\BIN>TRIGRAPH Trigraph Version 1.0 Copyright (c) 1992 Borland International Filter to remove or insert trigraphs in C source files, and to handle backslash splicing outside of macros. Works directly on source files, creating a backup file with the extension .BAK, unless -n or -x switch is used. USAGE: trigraph [options] <file spec> [<file spec> ...]

Page 175: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 174 di 299

options: -nXXX write output files to directory ‘XXX’ -u UNDO insert trigraphs -xXXX create output files with extension ‘XXX’

EMSTEST C:\COMPILER\BORLANDC\BIN>EMSTEST Borland C++ EMS Test Program Version 2.0 │ Copyright (C) 1991 Borland International Inc. │ PART ONE - EMS DETECTION Expanded Memory Manager Software not found. Expanded Memory not found or unusable - Program halted.

grep C:\COMPILER\BORLANDC\BIN>GREP Turbo GREP Version 3.0 Copyright (c) 1992 Borland International Syntax: GREP [-rlcnvidzuwo] searchstring file[s] Options are one or more option characters preceeded by "-", and optionally followed by "+" (turn option on), or "-" (turn it off). The default is "+". -r+ Regular expression search -l- File names only -c- match Count only -n- Line numbers -v- Non-matching lines only -i- Ignore case -d- Search subdirectories -z- Verbose -u- Update default options -w- Word search -o- UNIX output format Default set: [0-9A-Z_] A regular expression is one or more occurrences of: One or more characters optionally enclosed in quotes. The following symbols are treated specially: ^ start of line $ end of line . any character \ quote next character * match zero or more + match one or more [aeiou0-9] match a, e, i, o, u, and 0 thru 9 [^aeiou0-9] match anything but a, e, i, o, u, and 0 thru 9

OBJXREF C:\COMPILER\BORLANDC\BIN> OBJXREF OBJXREF Version 3.0 Copyright (c) 1992 Borland Intl Syntax: objxref [options] fileset [fileset ...] Report format options: /RR* public refs and defs /RP public definitions /RM publics by module /RX externals by module /RS module sizes by segment /RC module sizes by class /RU unreferenced publics /RV verbose - all report formats (* = default) Control options: /I ignore case /F include full libraries /V verbose output /Z show zero length segments /Nn selective report by name /On output file Response file options: @n input from free format ASCII file (filename.ext) /Ln input from a linker response file (filename.ext) /Pn input from a TC project file (filename[.PRJ]) /Dn search directories (separate with ‘;’) fileset (file names and extensions may contain wild cards):

Page 176: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 175 di 299

List of OBJs and Libs (default extentions is .OBJ)

TDNMI Utility per il reset dell’interruttore di breakout è un’utility residente che compie periodicamente tale operazione. C:\COMPILER\BORLANDC\BIN> TDNMI /? TD NMI handler Version 1.50 Copyright (c) 1988, 1992 Borland Intl Syntax: TDNMI [-p[<#>]] -p enables the handler for the Periscope I board at the default base address of 300 -p<#> enables the handler for the Periscope I board at the specified address of <#> hex

TURBOPROFILER Sebbene le ottimizzazioni siano compiute automaticamente durante la compilazione, esse non sono sempre sufficienti, spesso è necessario ottimizzare ulteriormente l’applicazione. L’algoritmo che è alla base di ogni applicazione, può essere anch’esso ottimizzato. I profiler sono applicazioni in grado di registrare il numero di chiamate alle istruzioni o ai sottoprogrammi; il tempo impiegato da ogni singola riga di un’applicazione, da ogni sottoprogramma per compiere l’elaborazione richiesta. Conoscendo quali sono le parti dell’applicazione che necessitano di più tempo, la fase di ottimizzazione può essere concentrata solo su quelle parti. Il profiler, inoltre, può registrare il numero di chiamate alle istruzioni o ai sottoprogrammi, il tempo impiegato da ogni sottoprogramma per compiere l’elaborazione richiesta. Tutte le informazioni ottenute dalla sessione di profiling possono essere memorizzate in un rapporto che può essere stampato e utilizzato in seguito per modificare l’applicazione sotto test nelle parti dove è maggiore l’utilizzo del tempo. Per abilitare TurboProfiler è possibile utilizzare due modalità. 1. Utilizzare il menu system dell’IDE.

2. Utilizzare il prompt di MS-DOS. Turbo Profiler Version 2.1 Copyright (c) 1990, 1992 Borland International. C:\COMPILER\BORLANDC\BIN>TPROF /? Syntax: TPROF [options] [program [arguments]] -x- = turn option x off -b Run in batch mode -c<file> Use configuration file <file> -do,-dp,-ds Screen updating: do=Other display, dp=Page flip, ds=Screen swap -h,-? Display this help screen -m<#> Set heap size to # kbytes -p Use mouse -r Use remote analysis -rn<L;R> Profile on a network with local machine L and remote machine R

Page 177: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 176 di 299

-rp<#> Set COM # port for remote link -rs<#> Remote link speed: 1=slowest, 2=slow, 3=medium, 4=fast -sc No case checking on symbols -sd<dir> Source file directory <dir> -vg Complete graphics screen save -vn 43/50 line display not allowed -vp Enable EGA/VGA palette save -w Profile remote Windows program (must use -r as well) -y<#> Set overlay area size in Kb -ye<#> Set EMS overlay area size to # 16Kb pages La finestra posta in fondo al video illustra quali sono le singole linee e le funzioni che utilizzano maggiormente il tempo dell’applicazione. I tempi riportati sono significativi solo nel rapporto percentuale. Turbo Profiler Version 2.1 Copyright (c) 1990, 1992 Borland International.

Page 178: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 177 di 299

FUNZIONI DI GESTIONE DELLO SCHERMO

MODALITà CUI Per ogni carattere sono necessari due byte. 1. Il primo rappresenta il carattere da visualizzare. 2. Il secondo contiene il valore relativo al colore con il quale il carattere stesso dev’essere

visualizzato: attributo video.

25 (R) 40 (C) = 2 KB

char

25 (R) 80 (C)= 4 KB Le funzioni di gestione dello schermo in modalità testo operano tramite finestre, la finestra di default è l’intero schermo, l’angolo in alto a sinistra ha coordinate (1,1). I/O cprintf(), cputs(), putch(), getche(), cgets() Controllo attributi highvideo(), lowvideo(), normvideo(), textattr(), textbackground(), textcolor(), textmode() Gestione schermo clrscr(), clreol(), delline(), gettext(), gotoxy(), insline(), movetext(), puttext(), window() Funzioni di stato dello schermo gettextinfo(), wherex(), wherey() Esempi. #include <stdio.h> #include <conio.h> int main (void) { textmode (0); /* textcolor (0+128);textbackground (7); */ textattr (7*16 | 0+128); printf (" \x07 \a \x07 \a "); gotoxy (10,13); cprintf(" CIAO MONDO "); getch ();textmode (2); return (0); } #include <stdio.h> #include <dos.h> #include <conio.h> int main (void) { char nome[15],cognome[15],classe[15]; int eta,i; clrscr(); printf ("COME TI CHIAMI ? \n"); printf ("\nNOME: "); scanf ("%s",nome); printf ("\nCOGNOME: "); scanf ("%s",cognome); printf ("\nQUANTI ANNI HAI ? "); scanf ("%s",eta);clrscr();

Page 179: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 178 di 299

for (i=1;i<=eta;i++) printf (" * ");printf ("\n\n"); for (i=1;i<=eta;i++) { textcolor(i);cputs(nome);printf (" ");cputs(cognome);printf (" "); delay(20*i);} printf ("\n");for (i=1;i<=eta;i++) printf (" * "); gotoxy(45,20);printf ("PREMERE UN TASTO PER CONTINUARE "); getch();return(0); }

Il PC possiede una serie di caratteri grafici, sono elencati nella tabella del codice ASCI, ogni carattere è un insieme predefinito di punti che forma il “disegno” di una lettera o di un simbolo. Per compiere le prime applicazioni di grafica non è quindi necessario conoscere tutte le caratteristiche della scheda grafica ma è sufficiente possedere un po’ di pazienza e di fantasia.

Page 180: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 179 di 299

MODALITà GUI Le caratteristiche di una scheda grafica sono elencate di seguito. Risoluzione: definisce i punti che è possibile accendere e spegnere sullo schermo,

maggiore è la risoluzione migliore è la qualità del grafico. Palette: il numero di colori che si possono visualizzare contemporaneamente, più colori

sono disponibili maggiore è la qualità del grafico. Pixel (Picture Element): è l’unità più piccola che il programmatore può manipolare nel

creare grafici; il pixel non specifica un colore ma è un indice della palette, manipolando la palette si cambia il colore sullo schermo anche se il valore del pixel non è cambiato.

480 (R) 640 (C) 307200

pixel

600 (R) 800 (C) 480000 Viewport È un oggetto simile alla finestra di testo dove operano tutte le funzioni grafiche, per default, l’intero schermo è una viewport. Funzioni di controllo della modalità video Prima di usare una qualsiasi funzione grafica bisogna settare la scheda grafica. void far initgraph(int far *graphdriver,int far *graphmode, char far *pathtodriver); Dove. graphdriver: carica nella memoria un driver grafico che corrisponde al numero puntato della variabile. graphmode: punta ad un intero che specifica la modalità video. pathdriver: punta ad una stringa che contiene la directory. I driver grafici sono contenuti nei file BGI (Borland Graphics Interface) permettono al programmatore di creare grafici, senza che l’applicazione sia vincolata al tipo di scheda grafica, inoltre contengono anche la possibilità di scrivere utilizzando vari tipi di fonts. O/L/L Graphics library on (graphics.lib) C:\BORLANDC\BGI> *.BGI (driver) *.CHR (fonts) Costante Valore DETECT 0 CGA (Color Graphics Adapter) 1 MCGA (Multi Color Graphics Array) 2 EGA (Enhanced Graphics Adapter) 3 EGA64 4 EGAMONO 5 IBM8514 6 HERCMONO 7 ATT400 8 VGA (Video Graphics Array) 9 PC3270 10 DETECT: individua automaticamente il tipo di scheda grafica presente nel PC e seleziona la modalità video con la risoluzione più elevata. Standard per le schede grafiche presenti sul mercato: VGA, 640*480 con 16 colori, una palette, è formata da 16 colori scelti in un insieme di 64 colori.

Page 181: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 180 di 299

Esempio di palette e colori in modalità quattro colori. Palette Numero del colore 0 1 2 3 0 * * * * 1 * * * * 2 * * * * 3 * * * * Per cambiare palette si usa la funzione seguente. void far setpalette(int colornum, int color); Associa il valore di color ad un indice della tabella seguente. Costante Valore BLACK 0 BLUE 1 GREEN 2 CYAN 3 RED 4 MAGENTA 5 BROWN 6 LIGHTGRAY 7 DARKGRAY 8 LIGHTBLUE 9 LIGHTGREEN 10 LIGHTCYAN 11 LIGHTRED 12 LIGHTMAGENTA 13 YELLOW 14 WHITE 15 BLINK 128 Per cambiare il colore dello sfondo si usa la funzione seguente. void far setbkcolor(int color); Per uscire dalla modalità grafica e ritornare in modalità testo si usa la funzione seguente. void far closegraph(void); Funzioni grafiche di base Sono quelle che permettono di disegnare punti, linee e cerchi. void far putpixel(int x, int y, int color); // disegna un pixel void far line(int x1, int y1, int x2, int y2); disegna una linea void far linerel(int dx, int dy); void far lineto(int x, int y); void far arc(int x, int y, int stangle, int endangle, int radius); void far circle(int x, int y, int radius); // disegna un cerchio void far pieslice(int x, int y, int stangle, int endangle, int radius); int far getcolor(void); void far setcolor(int color); // setta il colore void far floodfill(int x, int y, int border); // colora una forma chiusa

Page 182: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 181 di 299

void far setfillstyle(int pattern, int color); // colore di riempimento void far getfillpattern(char far *pattern); void far setfillpattern(char far *upattern, int color); I valori di pattern sono elencati nella tabella seguente. Nome Valore Significato EMPTY_FILL 0 Riempie con il colore dello sfondo SOLID_FILL 1 Riempie con il colore pieno LINE_FILL 2 Riempie di linee LTSLASH_FILL 3 Riempie di barre chiare SLASH_FILL 4 Riempie di barre BKSLASH_FILL 5 Riempie di controbarre LTBKSLASH_FILL 6 Riempie di controbarre chiare HATCH_FILL 7 Riempie con ombreggiature chiare XHATCH_FILL 8 Riempie con ombreggiature INTERLEAVE_FILL 9 Riempie con interallacciamento WIDE_DOT_FILL 10 Riempie di punti distanziati CLOSE_DOT_FILL 11 Riempie di punti vicini USER_FILL 12 Riempie con un pattern definito dall’utente Scrittura di caratteri in modalità grafica La biblioteca grafica possiede due tipi di font. 1. Scalabili (vettoriali, vector font, stroke, outline), memorizza i movimenti per riprodurre

una particolare forma; TGA (Truevision TArga), TIFF (Tagged Image File Format). 2. Non scalabili (mappa di bit, bit-mapped-font, raster-font) memorizza la particolare forma

pixel per pixel; CGM (Computer Graphics Metafile), PCX (Paintbrush), PIC (Lotus, Storyboard).

Per scrivere sul video in modalità grafica si usa la funzione seguente, il cursore non è visibile, la sua posizione corrente però è mantenuta come se fosse visibile. void far outtext(char far *textstring); void far outtextxy(int x, int y, char far *textstring); È possibile visualizzare il testo in molti font, direzioni e dimensioni diverse con la funzione seguente. void far settextstyle(int font, int direction, int charsize); Nome Valore Significato DEFAULT_FONT 0 8x8 bit mapped TRIPLEX_FONT 1 Triplex neretto SMALL_FONT 2 Corpo piccolo neretto SANS_SERIF_FONT 3 Sans serif neretto GOTHIC_FONT 4 Gotico neretto Esempio. #include <graphics.h> #include <stdlib.h> #include <stdio.h> #include <conio.h> char *fname[] = { "DEFAULT font", "TRIPLEX font", "SMALL font",

Page 183: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 182 di 299

"SANS SERIF font", "GOTHIC font" }; int main(void) { int gdriver = DETECT, gmode, errorcode; int style, midx, midy; int size = 1; initgraph(&gdriver, &gmode, "c:\\compiler\\borlandc\\bgi"); errorcode = graphresult(); if (errorcode != grOk) { printf("Errore grafico. %s\n", grapherrormsg(errorcode)); printf("Premi un tasto per uscire."); getch(); exit(1); } midx = getmaxx() / 2; midy = getmaxy() / 2; settextjustify(CENTER_TEXT, CENTER_TEXT); for (style=DEFAULT_FONT; style<=GOTHIC_FONT; style++) { cleardevice(); if (style == TRIPLEX_FONT) size = 4; settextstyle(style, HORIZ_DIR, size); outtextxy(midx, midy, fname[style]);getch(); } closegraph();return (0); }

Stato dello schermo in modalità grafica La funzione seguente carica una struttura contenente le informazioni sulla viewport corrente. void far getviewsettings (struct viewporttype far *viewport); La funzione ritorna informazioni sui valori correnti delle variabili di stato. void far gettextsettings(struct textsettingstype far *texttypeinfo); La funzione restituisce il colore di un particolare pixel. unsigned far getpixel(int x, int y);

Page 184: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 183 di 299

Funzioni grafiche di gestione dello schermo La memoria di alcune schede grafiche permette, solo per alcune modalità video, di memorizzare due o più schermate complete contemporaneamente. La VRAM (Video RAM)) che contiene le informazioni mostrate sullo schermo è chiamata pagina, MS-DOS usa la pagina zero di default. La posizione di quest’area in memoria dipende dal tipo di monitor: monocromatico (B000:0000), colori (B800:0000). In ogni caso è possibile usare ogni pagina video supportata dall’H/W scegliendo fra di esse liberamente. Sebbene possa essere mostrata solo una pagina per volta, può essere utile fare in modo che un processo operante in background costruisca un’immagine in una pagina che in quel momento non è visualizzata, di modo che, quando l’immagine è richiesta, basta semplicemente selezionarla. Per fare questo BorlandC implementa le due funzioni seguenti. setactivepage sets the active page for graphics output // determina la pagina setvisualpage sets the visual graphics page number // visualizza la pagina Le funzioni seguenti copiano/visualizzano una regione della finestra grafica. void far getimage(int left, int top, int right, int bottom,void far *bitmap); void far putimage(int left, int top, void far *bitmap, int op); La funzione seguente determina la dimensione in byte dell’immagine. unsigned far imagesize(int left, int top, int right, int bottom); La funzione seguente cancella la viewport attiva. void far clearviewport(void); La funzione seguente crea una finestra grafica. void far setviewport(int left, int top, int right, int bottom, int clip); I/O diretto sullo schermo 1. ANSI.SYS: lento ma compatibile: ESC[2J. 2. LIBRERIA del compilatore: veloce ma non compatibile: clrscr. 3. ROM (Read Only Memory) BIOS. 4. H/W: programmare “on the metal” problemi con gli adattatori grafici. È possibile bypassare le routine DOS e BIOS di scrittura sullo schermo e, al loro posto, di scrivere l’output direttamente nella VRAM, in questo modo è possibile ottenere operazioni d’I/O video molto veloci. Siccome la VRAM non può essere utilizzata direttamente, il BorlandC mette a disposizione la variabile directvideo, quando è true (1, default) tutto l’output sul video è effettuato tramite accessi alla VRAM, quando è false (0) attiva le routine BIOS. Evoluzione degli ambienti grafici. Line oriented rotolo edlin (MS-DOS). Full screen lavagna edit (MS-DOS), vi (UNIX). Multi window finestre ricorsive edit (BORLANDC). Desktop scrivania (windows più icone) Windows.

Page 185: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 184 di 299

Spazio geometrico Il piano cartesiano è uno spazio continuo, il punto non ha dimensioni.

Monitor: spazio discreto

#include <graphics.h> #include <conio.h> int main(void) { int x,graphdriver=VGA,graphmode=VGAHI; initgraph(&graphdriver,&graphmode,"c:\\borlandc\\bgi"); for (x=0;x<639;x++) putpixel(x,240,15); getch();closegraph(); return (0); } #include <graphics.h> #include <conio.h> int main(void) { int i,j,x,y,graphdriver=VGA,graphmode=VGAHI; initgraph(&graphdriver,&graphmode,"c:\\borlandc\\bgi"); for (i=1;i<=63;i++) { for (j=1;j<=47;j++) { x=10*i;y=10*j;putpixel(x,y,15); } } getch();closegraph(); return (0); } #include <graphics.h> #include <conio.h> int main(void)

Page 186: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 185 di 299

{ int i,x,y,graphdriver=VGA,graphmode=VGAHI; initgraph(&graphdriver,&graphmode,"c:\\borlandc\\bgi"); for (i=1;i<=63;i++) { x=10*i;line(x,0,x,479);} for (i=1;i<=47;i++) { y=10*i;line (0,y,639,y); } getch();closegraph(); return (0); }

Isometria composta da una traslazione di vettore V (0,480) e da una simmetria rispetto alla retta schermo y = 0.

xs = x

ys = 480 - y #include <stdio.h> #include <graphics.h> #include <conio.h> int main(void) { int x,xs,y,ys,graphdriver=VGA,graphmode=VGAHI;clrscr(); printf (" \nASCISSA = ");scanf ("%d",&x); printf (" \nORDINATA = ");scanf ("%d",&y); initgraph(&graphdriver,&graphmode,"c:\\borlandc\\bgi"); line(0,479,639,479);line (0,479,0,0); xs=x;ys=479-y;putpixel(xs,ys,15); getch();closegraph(); return (0); } #include <stdio.h> #include <graphics.h> #include <conio.h> int main(void) { int x,xs,y,ys,graphdriver=VGA,graphmode=VGAHI;clrscr(); initgraph(&graphdriver,&graphmode,"c:\\borlandc\\bgi"); line(0,479,639,479); line (0,479,0,0); for (x=1;x<=639;x++) { y=2*x+10;xs=x;ys=479-y;putpixel(xs,ys,15); } getch();closegraph(); return (0); }

Page 187: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 186 di 299

Isometria composta da una traslazione di vettore V (-X0, Y0) e da una simmetria rispetto alla retta schermo Y = 0.

xs = x + 320 xs = x + x0

ys = 240 - y ys = y0 -y #include <stdio.h> #include <graphics.h> #include <conio.h> int main(void) { int x,xs,y,ys,graphdriver=VGA,graphmode=VGAHI;clrscr(); printf (" \nASCISSA = ");scanf ("%d",&x); printf (" \nORDINATA = ");scanf ("%d",&y); initgraph(&graphdriver,&graphmode,"c:\\borlandc\\bgi"); line(0,240,639,240);line (320,0,320,479); xs=x+320;ys=240-y;putpixel(xs,ys,15); getch();closegraph();return (0); } Fattore di scala Andamento di una curva per 0 < x <1 I pixel hanno coordinate intere, occorre un cambiamento di scala (omotetia), si moltiplica ogni valore per un opportuno fattore di scala. Esempio: 16 unità in orizzontale. xs : 320 = x : 16 xs = (320 * x) / 16 = 20 * x

xs = (k x) xs = (k x + 320)

ys = (480 - k y) ys = (240 - k y) #include <stdio.h> #include <graphics.h> #include <conio.h> int main(void) { int x,xs,y,ys,x0=320,y0=240,graphdriver=VGA,graphmode=VGAHI; float k=640/16;clrscr(); printf (" \nASCISSA = ");scanf ("%d",&x); printf (" \nORDINATA = ");scanf ("%d",&y); initgraph(&graphdriver,&graphmode,"c:\\borlandc\\bgi"); line(0,y0,639,y0);line (x0,0,x0,479); xs=(k*x+320);ys=(y0-k*y);putpixel(xs,ys,15); getch();closegraph();return (0); }

Page 188: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 187 di 299

Deformazioni d’immagini

Quadrato Rettangolo -Cerchio Ellisse Il riferimento sullo schermo non è monometrico. Normalmente un monitor standard ha un rapporto 4:3 (32 * 24 cm), anche il pixel dovrà avere lo stesso rapporto.

CGA: 640*200 16:5

VGA: 640*480 4:3 Per la CGA occorre un fattore di quadratura, in altre parole eseguire un’affinità che raddoppia le ascisse schermo o dimezza le ordinate schermo.

xs = x xs = x + 320

ys = 480 - y/2 ys = 240 - y/2

xs = (k x) xs = (k x + 320)

ys = (480 - k y/2) ys = (240 - k y/2) #include <graphics.h> #include <math.h> #include <conio.h> int main(void) { int xasp,yasp,p,v=100,graphdriver=VGA,graphmode=VGAHI; double q=(16.0/5.0)/(4.0/3.0); p=floor(v+q+0.5); initgraph(&graphdriver,&graphmode,"c:\\borlandc\\bgi"); /*getaspectratio(&xasp,&yasp);*/ rectangle(50,50,150,150); getch(); /*setaspectratio(xasp*2,yasp/2);*/ rectangle(250,250,250+p,250+v); getch(); closegraph(); return (0); } Esempio, disegnare l’Italia. #include <dos.h> #include <stdio.h> #include <conio.h> #include <stdlib.h> #include <graphics.h> int disegno_italia(void),disegno_sicilia(void),disegno_sardegna(void); int bandiera(void),novara(void),colora_italia(void); int driver=9,mode=2; int main (void) { initgraph(&driver,&mode,"c:\\compiler\\borlandc\\bgi"); setbkcolor(7); setcolor(BLUE); disegno_italia(); disegno_sicilia();

Page 189: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 188 di 299

disegno_sardegna(); delay(1000); bandiera(); delay(1000); colora_italia(); novara(); getch();closegraph();return (0); } int disegno_italia() { line(124,168,125,168);line(125,168,127,166);line(127,166,128,168); line(127,168,130,167);line(130,167,133,167);line(133,167,134,166); line(134,166,137,167);line(137,167,139,164);line(139,164,140,164); line(140,164,141,163);line(141,163,140,161);line(140,161,141,161); line(141,161,142,159);line(142,159,143,159);line(146,159,144,158); line(144,158,145,158);line(145,158,146,160);line(146,160,145,161); line(145,161,147,164);line(147,164,150,165);line(150,165,149,166); line(149,166,151,170);line(151,170,152,169);line(152,169,151,166); line(151,166,157,162);line(157,162,155,159);line(155,159,155,158); line(155,158,158,158);line(158,158,157,159);line(157,159,159,161); line(159,161,161,161);line(161,161,161,160);line(161,160,164,160); line(164,160,165,162);line(165,162,164,164);line(164,164,167,163); line(167,163,166,162);line(166,162,167,160);line(167,160,165,159); line(165,159,166,156);line(166,156,167,156);line(167,156,168,157); line(168,157,170,157);line(170,157,171,152);line(171,152,173,151); line(173,151,175,152);line(175,152,178,153);line(178,153,178,151); line(178,151,183,149);line(183,149,187,150);line(187,150,192,147); line(192,147,194,148);line(194,148,192,149);line(192,149,193,151); line(193,151,194,152);line(194,152,193,153);line(193,153,195,155); line(195,155,202,157);line(202,157,205,156);line(205,156,209,158); line(209,158,212,159);line(212,159,212,160);line(212,160,208,163); line(208,163,208,164);line(208,164,210,165);line(210,165,210,166); line(210,166,209,168);line(209,168,210,169);line(210,169,211,168); line(211,168,212,169);line(212,169,210,170);line(210,170,213,172); line(213,172,215,175);line(215,175,214,176);line(214,176,211,176); line(211,176,212,175);line(212,175,210,172);line(210,172,209,173); line(209,173,206,172);line(206,172,204,173);line(204,173,204,174); line(204,174,200,176);line(200,176,195,179);line(195,179,195,178); line(195,178,194,178);line(194,178,192,181);line(192,181,192,183); line(192,183,193,182);line(193,182,194,185);line(194,185,197,187); line(197,187,196,189);line(196,189,193,189);line(193,189,192,191); line(192,191,193,192);line(193,192,193,199);line(193,199,195,202); line(195,202,199,205);line(199,205,201,206);line(201,206,207,212); line(207,212,210,213);line(210,213,212,219);line(212,219,214,222); line(214,222,215,227);line(215,227,218,232);line(218,232,225,238); line(225,238,227,238);line(227,238,227,240);line(227,240,230,241); line(230,241,233,243);line(233,243,237,242);line(237,242,243,242); line(243,242,245,241);line(245,241,247,244);line(247,244,245,246); line(245,246,242,249);line(242,249,254,256);line(254,256,257,256); line(257,256,267,263);line(267,263,273,264);line(273,264,275,267); line(275,267,277,268);line(277,268,280,272);line(280,272,279,279); line(279,279,278,280);line(278,280,273,277);line(273,277,274,275); line(274,275,271,272);line(271,272,267,271);line(267,271,262,269); line(262,269,263,268);line(263,268,260,267);line(260,267,257,269);

Page 190: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 189 di 299

line(257,269,253,276);line(253,276,254,279);line(254,279,252,281); line(252,281,253,284);line(253,284,257,285);line(257,285,263,288); line(263,288,262,292);line(262,292,262,294);line(262,294,263,298); line(263,298,259,296);line(259,296,254,301);line(254,301,255,307); line(255,307,252,308);line(252,308,250,312);line(250,312,248,315); line(248,315,243,317);line(243,317,242,315);line(242,315,242,311); line(242,311,244,309);line(244,309,245,305);line(245,305,244,303); line(244,303,249,301);line(249,301,249,298);line(249,298,246,295); line(246,295,246,290);line(246,290,243,285);line(243,285,242,280); line(242,280,240,277);line(240,277,238,276);line(238,276,237,278); line(237,278,234,277);line(234,277,233,275);line(233,275,231,275); line(231,275,230,274);line(230,274,231,270);line(231,270,228,266); line(228,266,222,268);line(222,268,223,263);line(223,263,221,263); line(221,263,217,264);line(217,264,213,256);line(213,256,211,256); line(211,256,208,255);line(208,255,205,255);line(205,255,203,257); line(203,257,202,253);line(202,253,197,251);line(197,251,192,246); line(192,246,192,244);line(192,244,187,242);line(187,242,182,234); line(182,234,179,234);line(179,234,178,235);line(178,235,176,234); line(176,234,178,233);line(178,233,175,228);line(175,228,172,227); line(172,227,173,224);line(173,224,169,224);line(169,224,170,218); line(170,218,167,213);line(167,213,166,206);line(166,206,164,204); line(164,204,162,204);line(162,204,161,202);line(161,202,160,203); line(160,203,158,202);line(158,202,156,199);line(156,199,152,197); line(152,197,150,197);line(150,197,146,196);line(146,196,143,198); line(143,198,142,200);line(142,200,141,200);line(141,200,139,203); line(139,203,139,205);line(139,205,136,206);line(136,206,134,206); line(134,206,134,207);line(134,207,133,206);line(133,206,132,207); line(132,207,131,206);line(131,206,130,204);line(130,204,134,201); line(134,201,133,199);line(133,199,132,200);line(132,200,128,200); line(128,200,127,199);line(127,199,126,199);line(126,199,125,197); line(125,197,124,197);line(124,197,124,196);line(124,196,123,196); line(123,196,123,194);line(123,194,124,194);line(124,194,123,192); line(123,192,124,189);line(124,189,126,189);line(126,189,125,188); line(125,188,126,186);line(126,186,122,186);line(122,186,120,181); line(120,181,125,181);line(125,181,124,180);line(124,180,125,178); line(125,178,127,179);line(127,179,127,178);line(127,178,128,178); line(127,178,127,173);line(127,173,126,173);line(126,173,126,171); line(126,171,124,171);line(124,171,124,168);line(164,226,168,225); line(168,225,168,228);line(168,228,167,227);line(167,227,165,228); line(165,228,164,226);line(229,304,230,304);line(230,304,230,305); line(230,305,229,305);line(229,305,229,304);line(230,306,231,305); line(231,305,232,306);line(232,306,231,307);line(231,307,230,306); line(231,308,232,308);line(232,308,232,309);line(232,309,231,309); line(231,309,231,308);line(232,302,233,302);line(233,302,233,303); line(233,303,232,303);line(232,303,232,302);line(234,300,235,300); line(235,300,235,301);line(235,301,234,301);line(234,301,234,300); line(214,265,216,265);line(216,265,216,266);line(216,266,214,266); line(214,266,214,265);return (0); } int disegno_sicilia() { line(240,310,238,315);line(238,315,236,319);line(236,319,236,321); line(236,321,234,324);line(234,324,234,327);line(234,327,235,328); line(235,328,236,328);line(236,328,237,329);line(237,329,235,330); line(235,330,238,332);line(238,332,234,337);line(234,337,235,339); line(235,339,234,340);line(234,340,232,338);line(232,338,230,339);

Page 191: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 190 di 299

line(230,339,225,337);line(225,337,222,333);line(222,333,218,331); line(218,331,217,332);line(217,332,211,328);line(211,328,209,328); line(209,328,206,326);line(206,326,205,324);line(205,324,202,324); line(202,324,201,323);line(201,323,199,324);line(199,324,197,322); line(197,322,196,322);line(196,322,195,320);line(195,320,196,314); line(196,314,198,314);line(198,314,198,312);line(198,312,199,312); line(199,312,200,314);line(200,314,202,315);line(202,315,204,314); line(204,314,208,312);line(208,312,209,314);line(209,314,210,313); line(210,313,212,316);line(212,316,214,316);line(214,316,216,314); line(216,314,227,313);line(227,313,228,312);line(228,312,230,312); line(230,312,233,313);line(233,313,235,311);line(235,311,237,310); line(237,310,240,310);return (0); } int disegno_sardegna() { line(150,255,153,257);line(153,257,154,256);line(153,256,155,257); line(155,257,154,258);line(154,258,158,269);line(158,269,155,274); line(155,274,157,276);line(157,276,155,285);line(155,285,153,293); line(153,293,151,294);line(151,294,150,292);line(150,292,148,293); line(148,293,147,292);line(147,292,146,293);line(146,293,146,296); line(146,296,143,298);line(143,298,141,297);line(141,297,140,298); line(140,298,137,292);line(137,292,138,290);line(138,290,137,289); line(137,289,138,281);line(138,281,139,282);line(139,282,139,279); line(139,279,137,279);line(137,279,137,276);line(137,276,138,276); line(138,276,138,272);line(138,272,137,271);line(137,271,137,268); line(137,268,136,266);line(136,266,135,266);line(135,266,135,264); line(135,264,136,259);line(136,259,137,261);line(137,261,140,262); line(140,262,149,256);line(149,259,150,255);line(136,257,137,257); line(137,257,137,258);line(137,258,136,258);line(136,258,136,257); line(134,292,135,292);line(135,292,135,293);line(135,293,134,293); line(134,293,134,292);line(135,294,136,294);line(136,294,137,296); line(137,296,136,296);line(136,296,135,294);return (0); } int bandiera() { rectangle(350,50,500,150);line(400,50,400,150);line(450,50,450,150); setfillstyle(1,GREEN);floodfill(360,60,BLUE);setfillstyle(1,RED); floodfill(460,60,BLUE);setfillstyle(1,WHITE); floodfill(410,60,BLUE);return (0); } int novara() { setcolor(MAGENTA);rectangle(142,177,144,179);setfillstyle(1,MAGENTA); floodfill(143,178,MAGENTA);setcolor(DARKGRAY);line(145,177,150,140); setcolor(YELLOW);settextstyle(TRIPLEX_FONT,HORIZ_DIR,2); outtextxy(140,120,"LP..Noi siamo qui");return (0); } int colora_italia() { setcolor(BLUE);line(170,222,193,192);line(220,263,239,242); setfillstyle(1,GREEN);floodfill(160,180,BLUE);setfillstyle(1,WHITE); floodfill(150,270,BLUE);setfillstyle(1,WHITE);floodfill(167,227,BLUE); setfillstyle(1,WHITE);floodfill(210,230,BLUE);setfillstyle(1,RED); floodfill(250,270,BLUE);setfillstyle(1,RED); floodfill(220,320,BLUE);return (0); }

Page 192: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 191 di 299

Conversione di driver e font Consente di convertire i driver grafici ed i set di caratteri, file di font, in file oggetto OBJ, in modo che si possa compiere il link con l’applicazione, incorporandolo nell’EXE: questa è un’aggiunta rispetto al caricamento dinamico del pacchetto grafico, in cui i driver ed i font sono caricati da disco durante l’esecuzione. Il link diretto nell’applicazione è più vantaggioso perché il file eseguibile contiene tutti i driver ed i font di cui necessita, tuttavia incrementa la dimensione dell’EXE. C:\Compiler\BORLANDC\BGI>bgiobj filename <CR> Dove. filename è il file di driver o font da convertire in file oggetto. Il file oggetto creato ha lo stesso nome del file sorgente ma con l’estensione OBJ; ad esempio, EGAVGA.BGI si ottiene EGAVGA.OBJ; SANS.CHR genera SANS.OBJ. Aggiunta di nuovi file .OBJ in GRAPHICS.LIB I moduli oggetto dei driver e dei font possono essere aggiunti a GRAPHICS.LIB, in modo che il linker possa localizzarli nel link delle routine grafiche, occorre richiamare TLIB con la seguente riga di comando. c:\>tlib graphics + nome_file_oggetto [+ nome_file_oggetto ...] <CR> Dove. nome_file_oggetto è il nome del file oggetto creato da BGIOBJ.EXE, come CGA, EGAVGA e GOTH. Aggiunta di nuovi file OBJ in PRJ Si dovrà specificarli nell’elenco dei file nel file di progetto; dopo l’aggiunta dei moduli oggetto di driver e font in GRAPHICS.LIB, devono essere registrati chiamando la funzione registerbgidriver e registerbgifont nell’applicazione prima di chiamare initgraph. Quest’operazione consente d’informare il sistema grafico della presenza di questi file ed assicura che ne sia compiuto il link nel file EXE creato dal linker. Le routine di registrazione richiedono un parametro, in altre parole il nome simbolico definito in GRAPHICS.H e restituiscono un valore non negativo se il driver o il font sono registrati con successo.

Page 193: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 192 di 299

Si supponga di voler convertire i file del driver grafico di CGA, del font gotico e del font triplex in moduli oggetto, quindi di collegarli nell’applicazione. 1. Si convertano i file binari in file oggetto tramite BGIOBJ.EXE c:\>bgiobj cga c:\>bgiobj trip c:\>bgiobj goth, In questo modo si creano tre file: CGA.OBJ, TRIP.OBJ e GOTH.OBJ. 2. Si possono aggiungere questi file oggetto a GRAPHICS.LIB. c:\>tlib graphics + cga + trip + goth <CR> 3. Se non si aggiungono i file oggetto a GRAPHICS.LIB, si devono aggiungere i nomi dei file oggetto CGA.OBJ, TRIP.OBJ e GOTH.OBJ all’elenco del progetto se si utilizza l’ambiente IDE, o alla riga di comando di BCC, nel modo seguente. c:\>bcc filename graphics.lib cga.obj trip.obj goth.obj <CR> Questi file sono registrati nel C nel modo seguente. #include <graphics.h> /*registra e controlla gli errori */ if (registerbgidriver(CGA_driver) < 0) exit(1); if (registerbgifont(triplex_font) < 0) exit(1); if (registerbgifont(gothic_font) < 0) exit(1); initgraph(....); L’opzione (/F): quando il linker presenta l’errore segment exceeds 64K. Per default, i file creati da BGIOBJ.EXE usano tutti lo stesso segmento _TEXT; questo può causare dei problemi se l’applicazione include diversi driver e/o font, oppure se si sta utilizzando il modello di memoria small o compact. Per risolvere questo problema è possibile convertire uno o più driver o font con l’opzione (/F): questa istruisce BGIOBJ ad utilizzare un nome di un segmento nella forma NOMEFILE_TEXT, in modo che il segmento di default non sia riempito da tutti i driver e i font e, nel modello small e compact, da tutto il codice dell’applicazione; ad esempio, le due linee di comando di BGIOBJ indicano di utilizzare i nomi dei segmenti nella forma EGAVGA_TEXT e SANS_TEXT. c:\>bgiobj /F egavga c:\>bgiobj /F sans Quando si seleziona (/F), BGIOBJ aggiunge la lettera (F) al nome del file di destinazione: EGAVGAF.OBJ, SANSF.OBJ e _far al nome che sarà utilizzato con registerfarbgidriver e registerfarbgifont, ad esempio EGAVGA_driver diventa EGAVGA_driver_far. Con i file creati con (/F), si devono utilizzare le routine di registrazione far, anziché quelle normali; esempio. if (registerfarbgidriver(EGAVGA_driver_far) < 0) exit(1); if (registerfarbgifont(sansserif_font_far) < 0) exit(1); Sintassi. C:\Compiler\BORLANDC\BGI>BGIOBJ BGI to OBJ Converter Version 4.0 Copyright (c) 1987, 1992 Borland International Syntax: BGIOBJ [/F] <source> <destination[.OBJ]> <public name> <segment-name> <segment-class> The <source> parameter is required, the rest is optional.

Page 194: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 193 di 299

/F selects ‘far’ version (please read the documentation before using /F). Examples: BGIOBJ GOTH BGIOBJ /F CGA BGIOBJ HERC.BGI HERCDRV _HERC_fdriver HERC_TEXT Dove. /F o -F: istruisce BGIOBJ.EXE ad utilizzare un nome di segmento diverso da _TEXT (default) e di variare il nome public e il nome del file di destinazione. source: file di driver o font da convertire se il file non è uno dei file di driver o font allegato a BorlandC, si deve specificare l’intero nome del file compresa l’estensione. destination: nome del file oggetto che dev’essere creato per default, è sorgente.OBJ, o sorgenteF.OBJ se si usa l’opzione (/F). public name: nome che sarà usato nell’applicazione nella chiamata a registerbgidriver o a registerbgifont o le rispettive versioni far per il link del modulo oggetto. segment-name: nome di segmento opzionale, per default è _TEXT o NOMEFILE_TEXT se (/F) è stata specificata. segment-class: classe di segmento opzionale; per default è CODE. Tutti i parametri, tranne sorgente, sono opzionali; tuttavia, se occorre specificarne uno di quelli opzionali anche tutti i parametri che lo precedono devono essere inclusi. Se si sceglie di utilizzare i nomi propri public, occorre aggiungere la dichiarazione nell’applicazione, utilizzando una delle seguenti forme. void public_name(void); senza /F o con nome di default extern int far public_name[]; con /F o con nome non _TEXT In queste dichiarazioni, nome_public corrisponde al nome utilizzato nella conversione con BGIOBJ. Il file header GRAPHICS.H contiene le dichiarazioni dei nomi public di driver e di font di default; se si usano questi nomi public non standard, si deve dichiararli come descritto. Dopo queste dichiarazioni, si devono registrare tutti i driver e font nell’applicazione. Se non si utilizza l’opzione (/F) e non si cambia il nome del segmento di default, è possibile farlo tramite registerbgidriver e registerbgifont; altrimenti si utilizzi registerfarbgidriver e registerfarbgifont. Esempio, caricare un file di font in memoria. #include <graphics.h> #include <alloc.h> #include <io.h> #include <fcntl.h> #include <stdio.h> #include <conio.h> #include <process.h> int main(void) { void *gothic_f; /* punta al buffer in memoria del font*/ int handle; /* handle del file utilizzato per l’I/O */ unsigned fsize; /* dimensione del file (e del buffer)*/ int graphdriver,graphmode,error,x,y;int i; /*apre il file di font*/ handle=open ("c:\\borlandc\\bgi\\GOTH.CHR",O_RDONLY|O_BINARY); if (handle==-1) { printf (" Impossibile aprire il font\n");exit (1); } fsize=filelength(handle); /* ricava la dimensione del file */ gothic_f=malloc(fsize); /* alloca il buffer */

Page 195: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 194 di 299

if (gothic_f==NULL) {printf ("Impossibile allocare la memoria\n");exit (1); } /*legge il font scrivendolo in memoria*/ if (read(handle,gothic_f,fsize) !=fsize) { printf("Impossibile leggere il font\n");exit (1);} close (handle); /*chiude il file di font*/ if (registerfarbgifont(gothic_f) != GOTHIC_FONT) { /*registra il font*/ printf ("Impossibile registrare il font\n");exit (1);} /*inizializza la grafica*/ graphdriver=DETECT;initgraph(&graphdriver,&graphmode,"c:\\borlandc\\bgi"); if ((error=graphresult()) !=0) { printf ("errore grafico: %s\n",grapherrormsg(error));exit(1); } settextjustify (CENTER_TEXT,CENTER_TEXT);setbkcolor(4);setcolor(14); settextstyle(GOTHIC_FONT,HORIZ_DIR,4); outtextxy(getmaxx()/2,getmaxy()/2, " LABORATORIO DI SISTEMI "); getch();closegraph();return (0); } #include<conio.h> #include<graphics.h> #include <process.h> int main(void) { int driver=DETECT, mode; if (registerbgidriver(EGAVGA_driver)<0) exit(1); initgraph(&driver,&mode,""); circle(100,100,20);getch();closegraph();return(0); } #include <stdio.h> #include <stdlib.h> #include <graphics.h> #include <bios.h> /* definisco delle costanti per la funzione bioskey(0) */ #define ZERO 2864 #define UNO 561 #define DUE 818 #define TRE 1075 #define QUATTRO 1332 #define CINQUE 1589 #define SEI 1846 #define SETTE 2103 #define OTTO 2360 #define NOVE 2617 #define a 7777 #define b 12386 #define c 11875 #define d 8292 #define e 4709 #define f 8550 #define g 8807 #define h 9064 #define i 5993 #define j 9322 #define k 9579 #define l 9836 #define m 12909 #define n 12654 #define o 6255

Page 196: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 195 di 299

#define p 6512 #define q 4209 #define r 4978 #define s 8051 #define t 5236 #define u 5749 #define v 12150 #define w 4471 #define x 11640 #define y 5497 #define z 11386 #define MENO 3117 #define PUNTO 13358 #define SPAZIO 14624 #define DOLL 1316 #define BARR 13615 #define PIU 3371 #define PERC 1573 #define ASTER 2346 #define DEL 3592 #define INVIO 7181 #define CAR_MAX 10 /* Definisco il numero massimo di caratteri che posso inserire */ int lung; /* Dichiaro la variabile per la memorizzazione del caratteri*/ char codice[CAR_MAX]; // Dichiaro l’array per la memorizzazione della stringa void stmcar(char[],char), cancella(void); int main(void) { int let_car=0; int drive=DETECT,mode=0; initgraph(&drive,&mode,"c:\\borlandc\\bgi"); rectangle(190,90,310,120); moveto(200,100); /* Sposta il cursore nel punto di coordinate 200,100 */ setcolor(2); /* Setta il colore in verde */ lung = 0; /* Azzera la variabile per la memorizzazzione dei caratteri digitati */ /* Ripete il ciclo while finchè non si preme il tasto INVIO o il numero di caratteri inseriti supera la quantità prefissata(CAR_MAX) */ while(let_car!=INVIO && lung<CAR_MAX) { let_car=bioskey(0); /* Con la funzione bioskey(0) leggo il tasto premuto */ /* Con switch stabilisco che tasto è premuto quindi lo stampo a video con la funzione stmcar(cahr[],char) */ switch(let_car) { case ZERO : stmcar("0",’0’);break; case UNO : stmcar("1",’1’);break; case DUE : stmcar("2",’2’);break; case TRE : stmcar("3",’3’);break; case QUATTRO : stmcar("4",’4’);break; case CINQUE : stmcar("5",’5’);break; case SEI : stmcar("6",’6’);break; case SETTE : stmcar("7",’7’);break; case OTTO : stmcar("8",’8’);break; case NOVE : stmcar("9",’9’);break; case a : stmcar("a",’a’);break; case b : stmcar("b",’b’);break; case c : stmcar("c",’c’);break; case d : stmcar("d",’d’);break; case e : stmcar("e",’e’);break; case f : stmcar("f",’f’);break; case g : stmcar("g",’g’);break;

Page 197: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 196 di 299

case h : stmcar("h",’h’);break; case i : stmcar("i",’i’);break; case j : stmcar("j",’j’);break; case k : stmcar("k",’k’);break; case l : stmcar("l",’l’);break; case m : stmcar("m",’m’);break; case n : stmcar("n",’n’);break; case o : stmcar("o",’o’);break; case p : stmcar("p",’p’);break; case q : stmcar("q",’q’);break; case r : stmcar("r",’r’);break; case s : stmcar("s",’s’);break; case t : stmcar("t",’t’);break; case u : stmcar("u",’u’);break; case v : stmcar("v",’v’);break; case w : stmcar("w",’w’);break; case x : stmcar("x",’x’);break; case y : stmcar("y",’y’);break; case z : stmcar("z",’z’);break; case MENO : stmcar("-",’-’);break; case PUNTO : stmcar(".",’.’);break; case SPAZIO : stmcar(" ",’ ‘);break; case DOLL : stmcar("$",’$’);break; case BARR : stmcar("/",’/’);break; case PIU : stmcar("+",’+’);break; case PERC : stmcar("%",’%’);break; case ASTER : stmcar("*",’*’);break; case DEL : cancella(); default : break; } } setcolor(0);closegraph();return 0; } void stmcar(char car[],char car1) { outtext(car); /* Stampa a video il carattere letto*/ codice[lung]=car1; /*Memorizza il carattere letto nell’array codice*/ lung++; /*Incrementa la variabile per la memorizzazione dei caratteri*/ } void cancella(void) { int xx, yy; /* Dichiara due variabili per il posizionamento del cursore*/ /*Con la seguente funzione noi cancelliamo l’ultimo carattere inserito sia a video che in memoria. Per cancellare il carattere dalla memoria basta mettere uno spazio nella posizione del carattere da cancellare e decrementare la variabile (lung) per la memorizzazione dei caratteri. Per cancellarlo a video bisogna prima cancellare tutta la stringa all’interno del rettangolo e di seguito riscriverla senza il carattere cancellato */ setfillstyle(1,0); floodfill(191,91,15); /* Cancella tutto il contenuto del rettangolo*/ moveto(200,100); /* Sposta il cursore CP nel punto di coordinate 200,100*/ lung--; /* Decrementa la variabile per la memorizzazione dei caratteri*/ codice[lung]=‘ ‘; /* Cancella il carattere*/ outtext(codice); /* Riscrive la stringa corretta*/ /*I seguenti tre comandi servono per posizionare il cursore CP nella posizione esatta per ricominciare a scrivere */ yy=100; xx=200+(8*lung); moveto(xx,yy); }

Page 198: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 197 di 299

FUNZIONI SONORE

Introduzione Il PC speaker è controllato a INT 61H con i bit 0 e 1. Il bit 0 è sempre a 0, mentre il bit 1 può assumere i seguenti valori. 1. Bit 1 = 0 contrarre l’altoparlante, inspirare. 2. Bit 1 = 1 espandere l’altoparlante, espirare. I due stati corrispondono alle due fasi di un ciclo di un’onda sonora, pulsazioni. La ripetizione nel tempo del ciclo completo è la frequenza della nota, si usa la funzione seguente. sound(f) Dove. f = 1E+9 / (7.140 * Hz). La durata della nota è la durata della singola pulsazione, si usa la funzione seguente. delay(ms) Gli effetti sonori si ottengono specificando l’altezza e la durata dei suoni, il tempo e l’eventuale sospensione dell’elaborazione dell’applicazione durante l’esecuzione sonora. Il PC può eseguire 84 delle 88 note della scala temperata disponibili sulla tastiera di un pianoforte, per un’estensione di 7 ottave.

Le note s’individuano nel modo seguente. Nome della nota

Notazione anglosassone Notazione latina

A LA

B SI

C DO

D RE

E MI

F FA

G SOL

Page 199: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 198 di 299

Ottava La lettera O seguita da un intero da 0 a 6, dal grave all’acuto, individua l’ottava per tutte le note seguenti, fino al prossimo cambio di ottava; se non s’indica alcuna ottava, vale per default la quarta. Per salire o scendere di una sola ottava o anche per impostarla inizialmente, si può usare < o >. È possibile utilizzarne anche più di uno per volta, ad esempio per salire di due o più ottave. Alterazioni Le note di DIESIS sono prodotte aggiungendo (+) oppure (#) dopo le note normali. Le note di BEMOLLE sono prodotte aggiungendo (-) dopo le note normali. Durata Le note si distinguono nel modo seguente. Interi (1). Mezzi (2), la nota è riprodotta con durata dimezzata. Quarti (4): valore di default. Ottavi (8). Sedicesimi (16). Trentaduesimi (32). Sessantaquattresimi (64). La durata è specificata da un numero che segue la nota alla quale applicarla. Tempi musicali

Tempo Quarti al minuto

Largo 40-60 BPM

Larghetto 60-66 BPM

Adagio 66-76 BPM

Andante 76-108 BPM

Moderato 108-120 BPM

Allegro 120-168 BPM

Presto 168-200 BPM

Prestissimo 200-208 BPM

Con la lettera T seguita da un intero n compreso tra 32 e 255 si ottiene l’esecuzione al tempo di n quarti al minuto, l’assegnazione vale fino a diversa indicazione e se non è data vale il tempo di 120 quarti al minuto.

Page 200: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 199 di 299

Le note possono essere individuate anche con un numero progressivo, secondo la tabella seguente.

Valori delle note del pianoforte.

Tabella di frequenze Nella scala cromatica temperata il rapporto fra le frequenze che producono due note

contigue è dato da 212 che può considerarsi circa uguale a 1.059463094. Partendo dalla nota fondamentale fornita dal diapason, in pratica dal LA che corrisponde a 440 Hz, è possibile costruire la seguente tabella di frequenze in Hertz, valori arrotondati alla cifra decimale, delle note della gamma su più ottave, utilizzabili con la funzione sound. Le frequenze udibili sono comprese fra 20 Hz e 20.000 Hz, quindi i valori che escono da questi limiti non compaiono in tabella. DO DO# RE RE# MI FA FA# SOL SOL# LA LA# SI ... ... ... 19.4 20.6 21.8 23.1 24.5 25.9 27.5 29.1 30.9 32.7 34.6 36.7 38.9 41.2 43.6 46.2 48.9 51.9 55 58.3 61.7 65.4 69.3 73.4 77.8 82.4 87.3 92.5 97.9 103.8 110 116.5 123.5 130.8 138.6 146.8 155.6 164.8 174.6 184.9 195.9 207.6 220 233.1 246.9 261.6 277.2 293.6 311.1 329.6 349.2 369.9 391.9 415.3 440 466.2 493.8 523.2 554.4 587.3 622.2 659.2 698.5 739.9 783.9 830.6 880 932.3 987.8 1046.5 1108.7 1174.6 1244.5 1318.5 1396.9 1479.9 1567.9 1661.2 1760 1864.6 1975.5 2093 2217.5 2349.3 2489 2637.0 2793.8 2959.9 3135.9 3322.4 3520 3729.3 3951.1 4186 4434.9 4698.6 4978 5274 5587.6 5919.9 6271.9 6644.9 7040 7458.6 7902.1 8372 8869.8 9397.2 9956.1 10548 11175.3 11839.8 12543.8 13289.7 14080 14917 15804 16774 17739.7 18794.7 19912.1 21096.2 ... ... ... ... ... ... ...

Page 201: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 200 di 299

#include <dos.h> const int re=587,mi=659,fa=699,sol=784,la=880; inline void suona (int nota,int durata) { sound(nota); delay(durata); nosound();} void motivo(void); int main(void) { motivo(); return (0); } void motivo(void) { suona(sol,500);suona(la,500);suona(sol,500);suona(fa,500); suona(mi,500);suona(fa,500);suona(re,500); suona(re,500);suona(re,500);suona(fa,500);suona(fa,500); suona(sol,500);suona(la,500);suona(sol,500);suona(mi,500); }

Page 202: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 201 di 299

BORLANDC AVANZATO

COME SCRIVERE LUNGHI PROGRAMMI Troppi dati I dati occupano più di 64 KB.

Troppo codice La libreria run-time occupa 18 KB ed è posizionata automaticamente all’inizio dell’applicazione: 64 KB – 18 KB = 46 KB.

Page 203: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 202 di 299

PROGRAMMAZIONE MODULARE 1. Ambiente a linea di comando Serve per automatizzare il processo di compilazione dei progetti; è guidato da un makefile che contiene un elenco di file obiettivo, file del progetto e comandi. C:\COMPILER\BORLANDC\BIN>make /? MAKE Version 3.6 Copyright (c) 1992 Borland International Syntax: MAKE [options ...] target[s] -B Builds all targets regardless of dependency dates -Dsymbol Defines symbol -Dsymbol=string Defines symbol to string -Idirectory Names an include directory -K Keeps (does not erase) temporary files created by MAKE -N Increases MAKE’s compatibility with NMAKE -W Writes all non-string options back to the .EXE file -Usymbol Undefine symbol -ffilename Uses filename as the MAKEFILE -a Performs auto-dependency checks for include files -e Ignores redefinition of environment variable macros -i Ignores errors returned by commands -m Displays the date and time stamp of each file -n Prints commands but does not do them -p Displays all macro definitions and implicit rules -q Returns zero if target is up-to-date and nonzero if it is not (for use in batch files) -r Ignores rules and macros defined in BUILTINS.MAK -s Silent, does not print commands before doing them -? or -h Prints this message Options marked with ‘+’ are on by default. To turn off a default option follow it by a ‘-’, for example: -a- Il make file usa la seguente sintassi. Il nome del file obiettivo deve iniziare in prima colonna e dev’essere seguito da due punti

(:) e da una lista di file dipendenti.

Page 204: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 203 di 299

La sequenza di comandi associata ad ogni obiettivo dev’essere preceduta da almeno uno spazio o un TAB.

I commenti sono preceduti da (#). Ogni specificazione del file obiettivo dev’essere separata dalla successiva da almeno

una riga vuota. Se non è specificato alcun nome, MAKE esegue il file chiamato MAKEFILE. La forma generale è la seguente. file_obiettivo1: lista_file_dipendenti sequenza_comandi file_obiettivo2: lista_file_dipendenti sequenza_comandi … file_obiettivoN: lista_file_dipendenti sequenza_comandi Esempio, progetto suddiviso in due file per ordinare numeri interi. File TRI.C void tri(int *tablo, int nelem) { int m, n, temp; while (--nelem > 0) { for (n = 0; n < nelem; ++n) { if (tablo[n] > tablo[n+1]) { temp = tablo[n]; tablo[n] = tablo[n+1]; tablo[n+1] = temp; } } } } File TRIPRINC.C #include <stdio.h> #include <conio.h> extern void tri(int *tablo, int nelem); /* dichiarazione di prototipo esterna */ int main(void) { int val[100], nelem, n; clrscr();printf("\nInserisci il numero di elementi: "); scanf("%d", &nelem); printf("\n"); for (n = 0; n < nelem; ++n) { printf("Inserisci il valore %d: ", n+1); scanf("%d", &val[n]); } tri(val, nelem); printf("\nValori ordinati :\n\n"); for (n = 0; n < nelem; ++n) printf("Valore %d: %d\n", n+1, val[n]); getch();return(0); } Makefile # File Make per tri.exe

Page 205: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 204 di 299

bcc tri.exe: bcc -c -IC:\compiler\borlandc\include tri.c bcc -c -IC:\compiler\borlandc\include triprinc.c tlink c:\compiler\borlandc\lib\c0s.obj tri.obj triprinc.obj,,,c:\compiler\borlandc\lib\cs.lib

Esempio, progettare un’applicazione che genera in modo automatico il MAKEFILE. #include <stdio.h> #include <stdlib.h> #include <conio.h> int main(void) { char fichier[50][20], *ptr, fexe[50]; int nmax = 0, n; FILE *fp; clrscr(); printf("\nGeneratore di MAKEFILE\n\n"); printf("Nome del file .EXE: "); scanf(" %s", fexe); ptr = fexe; while (*ptr != ‘.’ && *ptr != ‘\0’) ptr++; if (*ptr == ‘.’) *ptr = ‘\0’; printf("\n\Nomi dei file del progetto (terminare con ‘#’):\n"); do { printf("File Nø %d: ", nmax+1); scanf(" %s", fichier[nmax]); ptr = fichier[nmax]; while (*ptr != ‘.’ && *ptr != ‘\0’) ptr++; if (*ptr == ‘.’) *ptr = ‘\0’; } while (fichier[nmax++][0] != ‘#’); --nmax; fp = fopen("MAKEFILE","w"); fprintf(fp,"# File Make per %s.exe\n\n", fexe); /* chiamata del compilatore da modiifcare in base al compilatore usato */

Page 206: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 205 di 299

fprintf(fp,".c.obj:\n\tbcc -AS -c $*.c\n\n"); /* bcc -c $*.c */ for (n = 0; n < nmax; ++n) fprintf(fp,"%s.obj : %s.c\n\n", fichier[n], fichier[n]); fprintf(fp,"\n%s.exe :\\\n", fexe); if (nmax > 0) fprintf(fp," %s.obj", fichier[0]); for (n = 1; n < nmax; ++n) fprintf(fp,"\\\n %s.obj", fichier[n]); fprintf(fp,"\n"); fprintf(fp,"\tdel %s.LNK\n", fexe); for (n = 0; n < nmax; ++n) /* crea un nuovo file .LNK */ { fprintf(fp,"\techo %s.obj", fichier[n]); if (n+1 < nmax) fprintf(fp,"+"); fprintf(fp," >> %s.LNK\n", fexe); } fprintf(fp,"\techo %s.exe >> %s.LNK\n", fexe, fexe); fprintf(fp,"\techo NUL >> %s.LNK\n", fexe); /* chiamata del linker da modiifcare in base al linker usato */ fprintf(fp,"\tlink @%s.LNK /NOI;\n", fexe); /* link @%s.LNK /NOI */ fclose(fp);return(0); }

# File Make per tri.exe .c.obj: bcc -c $*.c tri.obj : tri.c triprinc.obj : triprinc.c tri.exe :\ tri.obj\ triprinc.obj del tri.LNK echo tri.obj+ >> tri.LNK echo triprinc.obj >> tri.LNK echo tri.exe >> tri.LNK echo NUL >> tri.LNK tlink @tri.LNK /NOI; 2. Ambiente IDE (Integrated Development Environment) P/O, P/A, C/M (F9), R (CTRL + F9) Ad ogni progetto è assegnato un file progetto che determina quali sono i file che fanno parte del progetto. L’opzione Project del menu principale permette di specificare un file progetto, la lista dei moduli del progetto è scritta in questo file con estensione PRJ. È permesso il pathname: F/C sui file.

Page 207: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 206 di 299

Le dipendenze sono le seguenti. Implicite: date dal C e dall’OBJ. Esplicite: un C dipende da altri file H, OBJ e LIB. File TRI.PRJ

Compile/Make F9 è compilato automaticamente ogni file contenuto nel file progetto, con quest’opzione sono compilati solo i file che non sono stati compilati in una fase predente, il BorlandC controlla data e ora associata ad ogni file sorgente con quella del file OBJ.

Questa utility converte un file .PRJ in .MAK. C:\COMPILER\BORLANDC\BIN>PRJ2MAK PRJ2MAK 2.0 Copyright (c) 1991, 1992 Borland International Syntax: PRJ2MAK <project-file>[.PRJ] [<makefile>[.MAK] [<config>[.CFG]]] Convert a .PRJ file to a .MAK file for use with the MAKE utility. The default name for the new MAKE file is the base file name of the .PRJ file with the extension .MAK. The default name for the new .CFG file is the base file name of the .MAK file with the extension .CFG. C:\COMPILER\BORLANDC\BIN>PRJ2MAK TRI.PRJ PRJ2MAK 2.0 Copyright (c) 1991, 1992 Borland International

Page 208: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 207 di 299

File TRI.MAK .AUTODEPEND .PATH.obj = C:\COMPILER\BORLANDC\OUTPUT # *Translator Definitions* CC = bcc +TRI.CFG TASM = TASM TLIB = tlib TLINK = tlink LIBPATH = C:\COMPILER\BORLANDC\LIB INCLUDEPATH = C:\COMPILER\BORLANDC\INCLUDE # *Implicit Rules* .c.obj: $(CC) -c {$< } .cpp.obj: $(CC) -c {$< } # *List Macros* EXE_dependencies = \ tri.obj \ triprinc.obj # *Explicit Rules* tri.cfg $(EXE_dependencies) $(TLINK) /v/x/c/P-/L$(LIBPATH) @&&| c0s.obj+ c:\compiler\borlandc\output\tri.obj+ c:\compiler\borlandc\output\triprinc.obj c:\compiler\borlandc\output\tri # no map file graphics.lib+ fp87.lib+ maths.lib+ cs.lib # *Individual File Dependencies* tri.obj: tri.cfg tri.c triprinc.obj: tri.cfg triprinc.c # *Compiler Configuration File* tri.cfg: tri.mak copy &&| -3 -a -f287 -v -G -O -Og -Oe -Om -Ov -Ol -Ob -Op -Oi -Z -k- -vi-

Page 209: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 208 di 299

-wpro -weas -wpre -nC:\COMPILER\BORLANDC\OUTPUT -I$(INCLUDEPATH) -L$(LIBPATH) | tri.cfg Esempio. File TEST1.C #include <stdio.h> #include <conio.h> void count void; int main (void)

clrscr(); printf (“Questo e’ test1 \n”); max=10;count(); getch(); return(0);

File TEST2.C #include “test.h” void count (void);

int i; for (i=0;i<max;i++) printf (“%d\n”,i); File TEST.H extern int max;

LIBRERIE 1. Ambiente a linea di comando Per costruire una libreria di sottoprogrammi è sufficiente raggruppare le funzioni stesse in un unico file sorgente, una volta completata la scrittura del codice delle funzioni, basta convertire il codice sorgente nel corrispondente codice oggetto. TLIB.EXE è un’utility di gestione delle librerie, permette di creare librerie contenenti le funzioni sviluppate dai programmatori, utile per i seguenti motivi. Creare una nuova libreria da un gruppo di OBJ. Aggiungere OBJ o altri LIB ad una libreria esistente. Rimuovere, sostituire, elencare ed estrarre OBJ da una libreria esistente. La forma generale del comando è la seguente. C:\Compiler\BORLANDC\BIN>tlib TLIB 3.02 Copyright (c) 1992 Borland International Syntax: TLIB libname [/C] [/E] [/P] [/0] commands, listfile libname library file pathname commands sequence of operations to be performed (optional) listfile file name for listing file (optional) A command is of the form: <symbol>modulename, where <symbol> is: + add modulename to the library - remove modulename from the library * extract modulename without removing it -+ or +- replace modulename in library -* or *- extract modulename and remove it

Page 210: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 209 di 299

/C case-sensitive library /E create extended dictionary /PSIZE set the library page size to SIZE /0 purge comment records Use @filepath to continue from file "filepath". Use ‘&’ at end of a line to continue onto the next line. Esempi. C:\>BCC -C TEST; per creare il modulo oggetto TEST.OBJ C:\>TLIB PROVALIB +TEST per creare la libreria di nome PROVALIB.LIB; C:\>TLIB PROVALIB, PROVALIB.LST per ottenere un listato della libreria

PROVALIB.LIB; C:\>TILIB -+TEST sostituisce TEST.OBJ con una nuova copia. 2. Ambiente IDE L’opzione Project del menu principale permette di specificare un file progetto, la lista dei moduli del progetto è scritta in questo file con estensione PRJ. Esempio. File TEST.C #include <stdio.h> void func1 (void) { printf(“funzione 1\n”); } void func2 (void) { printf(“funzione 2\n”); } File PROVA.C #include <stdio.h> #include <conio.h> #include "prova.h" int main (void) { clrscr(); func1(); func2(); getch(); return(0); } File PROVA.H void func1(void); void func2(void); 1. Ambiente a linea di comando C:\>TLIB PROVALIB +TEST <INVIO> C:\> BCC PROVA PROVALIB <NVIO>

Page 211: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 210 di 299

2. Ambiente IDE

TSR (Terminate and Stay Resident) È un programma che, quando termina, non consente al DOS di liberare la memoria da esso occupata: al contrario, vi rimane residente; l’interprete dei comandi, COMMAND.COM, riprende il controllo e ricompare a video il prompt. Il TSR, nel frattempo, effettua un monitoraggio costante della situazione attraverso la gestione di una o più routine d’interrupt e, al verificarsi delle condizioni prestabilite, interrompe l’attività svolta dal DOS o dall’applicazione in corso di esecuzione per tornare ad agire in foreground. I TSR sono stati progettati perché il DOS è un SO single user e single tasking; esso è in grado di eseguire una sola applicazione alla volta, sono un parziale rimedio a questa limitazione, proprio perché essi sono in grado di nascondersi dietro le quinte ed apparire quando necessario. I TSR possono essere classificati in due categorie. 1. Passivo assume il controllo del sistema solo quando vi è un’esplicita richiesta da parte

di un’altra applicazione eseguita in foreground, ad esempio attraverso una chiamata ad un interrupt S/W gestito dal TSR stesso.

2. Attivo è “risvegliato” da un evento esterno all’applicazione in foreground: ad esempio la pressione di una certa combinazione di tasti o, più in generale, dal verificarsi di un predefinito interrupt H/W.

La struttura del TSR si suddivide in due segmenti. 1. Il primo ha il compito di caricare in memoria l’applicazione, provvedere a tutte le

operazioni necessarie all’installazione del TSR e restituire il controllo al DOS, questa porzione di codice non ha più alcuna utilità a partire dal momento in cui l’applicazione è residente e pertanto la memoria da essa occupata può essere liberata a vantaggio delle applicazioni che saranno utilizzate in seguito, per questo motivo essa è detta parte transiente.

2. Il secondo costituisce, al contrario, la parte di codice destinata a rimanere attiva in background, è questa la parte residente che si compone a sua volta di due categorie di sottoprogrammi: quelle dedicate al monitoraggio del sistema che devono “intercettare” il segnale di attivazione del TSR e quelle che svolgono le attività proprie del TSR medesimo, le quali possono essere le più svariate.

L’installazione è l’operazione necessaria per terminare l’esecuzione del TSR e renderlo

Page 212: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 211 di 299

permanente in memoria, è effettuata dalla funzione seguente. keep(errlevel,resparas); Dove. errlevel: di tipo unsigned char contiene il valore che è restituito dall’applicazione al DOS. resparas: di tipo unsigned int contiene il numero di paragrafi che sono riservati dal DOS all’applicazione per la sua permanenza in memoria. Esempio, molti vettori d’interrupt sono inutilizzati, in pratica significa che è possibile scrivere delle routine di gestione degli interrupt: utile per i TSR. Per scrivere una routine di gestione degli interrupt, la funzione dev’essere definita di tipo interrupt dove sono passati come parametri tutti i registri, quindi è possibile utilizzarli e modificarli nel codice senza utilizzare le pseudo variabili. Una funzione di questo tipo salva automaticamente SI, DI, BP, AX, DX, ES, DS e li ripristina all’uscita della routine e ritorna al chiamante con IRET. /* emette un beep ogni volta che è chiamata */ #include <dos.h> void interrupt beep (unsigned BP,unsigned DI,unsigned SI,unsigned DS, unsigned AX,unsigned CS, unsigned DX,unsigned CX,unsigned BX); void install (void interrupt (*faddr)(),int n); void testbeep (unsigned char e,int n); int main (void) { install (beep,10); testbeep(3,10); return (0); } void interrupt beep (unsigned BP,unsigned DI,unsigned SI,unsigned DS, unsigned AX,unsigned CS, unsigned DX,unsigned CX,unsigned BX) { int i,j;char b;unsigned int bc; b=inportb(0x61); bc=AX>>8; for (i=0;i<bc;i++) { outportb (0x61,b&0xfc); /* disattiva PC_SPEAKER */ for (j=0;j<=100;j++); /* delay */ outportb(0x61,b|2); /* attiva PC-SPEAKER */ for (j=0;j<=100;j++);} outportb(0x61,0); } /* installa la routine di gestione dell’interrupt, parametri: indirizzo funzione e numero d’interrupt, ha tre compiti: disattiva l’interrupt, memorizza l’indirizzo della funzione, attiva l’interrupt */ void install (void interrupt (*faddr)(),int n) { setvect (n,faddr); } void testbeep (unsigned char e,int n) { _AH=e; geninterrupt(n);} Esempio. #include <stdio.h> #include <conio.h> #include <dos.h> void interrupt inc(); void interrupt (*inter)(); int loop=0; int main(void) { clrscr(); inter = getvect(0x1C); setvect(0x1C,inc); while (loop<=10){printf("%3d Ciao",loop); } setvect(0x1C,inter); puts("\nFatto"); getch();return (0);

Page 213: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 212 di 299

} void interrupt inc() { loop++; }

File CLOCK_1.C Interrupt H/W: mostra un orologio sullo schermo. L’interrupt dev’essere trasparente: cioè l’applicazione non deve accorgersi di essere interrotta. La scrittura d’interrupt handler è difficile, la CPU salva i flag e l’indirizzo di ritorno, handler deve salvare i registri e ripristinarli. Debugging difficile: non si può fermare il run-time, installata la routine questa sarà eseguita che funzioni o non. Ad ogni battito di orologio, mostra un orologio ASCII nell’angolo in alto a destra dello schermo. Mantiene anche nota dell’intervallo delta, il tempo passato dal momento dell’inizio del run-time che è mostrato nella linea successiva. Appena lo schermo scorre verso l’alto, l’orologio scompare ma INT 8H avviene ogni 18.2065 volte al secondo, l’ora ricompare. Se debug è attivo, installa la routine in un vettore inutilizzato e la richiama dal main. Questo è per permettere che i breakpoint siano posti facilmente nelle routine d’interrupt. Se debug è 0 o non è definito, la installa normalmente. #include <stdio.h> #include <dos.h> #include <stdlib.h> #include <process.h> #include <conio.h> #define mono (int far *)0xb0000000 /* per i video mono...*/ #define cga (int far *)0xb8000000 /*... per EGA e CGA */ void interrupt clock (void); void display (int number,int y,int x); void out (char *buffer,int y,int x); void init (void); int restore (void); union {

Page 214: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 213 di 299

long ltime; int stime [2]; } p; union REGS regs; int far *screen;int prevtime, time, minute, hour; char buffer [10]; void interrupt (*fn)(); #if DEBUG #define vect 0x48 struct REGS reg2; #else #define vect 0x1c #endif /*Main - compie le precedenti routine. Se debug e’ ad 1, ‘vect’ e’ diretto verso un vettore inutilizzato (48). Questo permette di fare facilmente il debug usando il debugger preferito. Una volta verificata, debug=0 ed il vettore e’ rediretto all’interrupt di TimerTick (1C).*/ int main (void) { int i, j, k;clrscr(); init ();fn = getvect (vect);setvect (vect, clock);ctrlbrk (restore); /* entra nel ciclo per almeno 2 minuti cosi’da farvi vedere aumentare l’orologio per assicurarvi che sta funzionando */ for (i = 0; i < 150; i++) { for (j = 0; j < 30000; j++) k = j * 5; printf (" questo e’ solo un output fittizio --" " guarda l’orologio\n"); #if DEBUG /* richiama l’interrupt per poter eseguire il debug */ int86 (vect, &reg2, &reg2); #endif }; /* ripristina l’interrupt al suo precedente valore */ setvect (vect, fn); return (0); } /* Init - pone l’indirizzo dello schermo e pulisce lo schermo */ void init (void) { int mode;prevtime = -1;regs.h.ah = 0x0f;int86 (0x10, &regs, &regs); mode = regs.h.al;if (regs.h.ah != 80) abort (); if (mode == 7) screen = mono; else if (mode == 3 || mode == 2) screen = cga; else abort (); } /*Restore ripristina vecchio indirizzo di interrupt nel caso di un Ctrl-C*/ int restore (void) { /* ripristina l’interrupt al suo valore precedente */ setvect (vect, fn); /* indica all’operatore quello che ha fatto*/ printf ("\nBreak!\n"); /* esce dal programma al DOS */ return 0; } /* Clock - afferra l’interrupt e fornisce la funzione */ void interrupt clock (void) { regs.h.ah = 0;int86 (0x1a, &regs, &regs); p.stime [0] = regs.x.dx; p.stime [1] = regs.x.cx; time = (int)(p.ltime / (long)1092); display (time, 0, 75);if (prevtime == -1) prevtime = time;

Page 215: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 214 di 299

if ((time -= prevtime) < 0) time += (24 * 60); display (time, 1, 75); } /*Display - pone l’ora sullo schermo alla posizione indicata Ricordate che non possiamo fare alcuna chiamata DOS.*/ void display (int number,int y,int x) { hour = number / 60;minute = _DX; sprintf (buffer, "%2.2u:%2.2u", hour, minute); buffer [5] = ‘\0’;out (buffer, y, x); } /*Out - mostra una stringa senza usare le chiamate di sistema*/ void out (char *buffer,int y,int x) { int far *scrptr; #if DEBUG /* stampa i dati per assicurarsi che siano corretti */ printf ("%s", buffer); #endif scrptr = screen + (y * 80 + x); while (*buffer) *scrptr++ = 0x1700 + *buffer++; }

File CLOCK_2.C Mostra un orologio sullo schermo: interrupt stealers, ladro, continua a fornire l’ora sullo schermo, anche se il run-time è terminato dovrà essere compilato col modello di memoria TINY e potrà essere convertito in un file COM. Il processo termina ma la memoria occupata non è restituita: non si è più costretti a reinstallare il vecchio vettore d’interrupt. Non eseguirlo dall’IDE perché IDE è posto in memoria tra DOS e l’applicazione in run-time quindi la memoria non può essere riservata in modo adeguato. Questa versione non può andare d’accordo con le altre routine d’interrupt. #include <stdio.h> #include <dos.h> #include <conio.h>

Page 216: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 215 di 299

#include <stdlib.h> #include <process.h> #define mono (int far *)0xb0000000 /* per i video mono...*/ #define cga (int far *)0xb8000000 /*...per la ega e la cga*/ #ifndef __TINY__ #error Occorre usare il modello di memoria TINY #endif #define vect 0x1c void interrupt clock (void), display (int number,int y,int x); void out (char *buffer,int y,int x), init (void); union { long ltime; int stime [2]; } p; union REGS regs; int prevtime, time, minute, hour;char buffer [6];int far *screen; char digits [] = {"0123456789"}; /*Main NON ATTENDE PIU’ PER RIMETTERE A POSTO IL VECCHIO VETTORE */ int main (void) { clrscr(); init (); setvect (vect, clock); printf ("Orologio installato nell’interrupt %2x\n", vect); keep (0, 0x1000); return (0); } /* Init - pone l’indirizzo dello schermo e pulisce lo schermo */ void init (void) { int mode;prevtime = -1;regs.h.ah = 0x0f;int86 (0x10, &regs, &regs); mode = regs.h.al; if (regs.h.ah != 80) abort (); if (mode == 7) screen = mono; else if (mode == 3 || mode == 2) screen = cga;else abort (); } /* Clock - afferra l’interrupt e fornisce la funzione */ void interrupt clock (void) { regs.h.ah = 0;int86 (0x1a, &regs, &regs);p.stime [0] = regs.x.dx; p.stime [1] = regs.x.cx;time = (int)(p.ltime / (long)1092); display (time, 0, 75);if (prevtime == -1) prevtime = time; if ((time -= prevtime) < 0) time += (24 * 60); display (time, 1, 75); } /* Display - mostra l’ora nella posizione indicata. Ricordate che non possiamo compiere chiamate al DOS.*/ void display (int number,int y,int x) { hour = number / 60; minute = _DX; /* lo inserisce in un buffer ascii per l’output */ buffer [0] = digits [hour / 10]; buffer [1] = digits [hour % 10]; buffer [2] = ‘:’; buffer [3] = digits [minute / 10];buffer [4] = digits [minute % 10]; buffer [5] = ‘\0’; out (buffer, y, x); } /*Out - mostra una stringa senza usare le chiamate di sistema*/ void out (char *buffer,int y,int x) { int far *scrptr; scrptr = screen + (y * 80 + x);

Page 217: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 216 di 299

while (*buffer) *scrptr++ = 0x1700 + *buffer++; }

File CLOCK_3.C Mostra un orologio sullo schermo: interrupt borrowers, prestito, normalmente il vettore d’interrupt 1CH punta ad un IRET, questo permette alle applicazioni di segnare il passaggio del tempo come il DOS. Ma in CLOCK_1 e CLOCK_2 per come sono stati scritti una sola applicazione alla volta può avvalersi di questa capacità: sono antisociali. Un’applicazione, invece, deve usare interrupt in modo invisibile affinché le altre applicazioni innestate sullo stesso interrupt non si accorgano della sua presenza: in questo modo si possono costruire catene di TSR. Quest’applicazione deve’essere compilata con il modello di memoria TINY. #include <stdio.h> #include <dos.h> #include <stdlib.h> #include <conio.h> #include <process.h> #define mono (int far *)0xb0000000 /* per i video ...*/ #define cga (int far *)0xb8000000 /*... per ega e cga */ #ifndef __TINY__ #error Occorre compilare col modello di memoria TINY #endif #define vect 0x1c void interrupt clock (void), display (int number, int y, int x); void out (char *buffer,int y,int x), init (void); union { long ltime; int stime [2]; } p; union REGS regs; void interrupt (*old)(void); int prevtime, time, minute, hour;char buffer [6]; int far *screen;char digits [] = {"0123456789"}; int main (void) { clrscr(); init ();old = getvect (vect); setvect (vect, clock); printf ("Orologio installato nel vettore %2x\n", vect); keep (0, 0x1000);return (0); } /* Init - pone l’indirizzo dello schermo e pulisce lo schermo */ void init (void) { int mode;prevtime = -1;regs.h.ah = 0x0f;int86 (0x10, &regs, &regs); mode = regs.h.al;if (regs.h.ah != 80) abort (); if (mode == 7) screen = mono; else if (mode == 3 || mode == 2) screen = cga; else abort (); } /* Clock - prende l’interrupt e fornisce la funzione */ void interrupt clock (void)

Page 218: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 217 di 299

{ regs.h.ah = 0;int86 (0x1a, &regs, &regs); p.stime [0] = regs.x.dx; p.stime [1] = regs.x.cx; time = (int)(p.ltime / (long)1092); display (time, 0, 75); if (prevtime == -1) prevtime = time; if ((time -= prevtime) < 0) time += (24 * 60); display (time, 1, 75); /* passa il controllo alla successiva routine di interrupt */ (*old)(); } /* Display - mostra l’ora nella posizione indicata. Ricordate che non possiamo fare chiamate di sistema*/ void display (int number,int y,int x) { hour = number / 60; minute = _DX; /* lo inserisce in un buffer ascii per l’output */ buffer [0] = digits [hour / 10]; buffer [1] = digits [hour % 10]; buffer [2] = ‘:’; buffer [3] = digits [minute / 10];buffer [4] = digits [minute % 10]; buffer [5] = ‘\0’; out (buffer, y, x); } /*Out mostra una stringa sullo schermo senza usare le chiamate di sistema */ void out (char *buffer,int y,int x) { int far *scrptr; scrptr = screen + (y * 80 + x); while (*buffer) *scrptr++ = 0x1700 + *buffer++; } File CLOCK_4.C Mostra un orologio sullo schermo con il controllo della tastiera. Memorizza l’indirizzo dell’applicazione in un interrupt inutilizzato, DOS stesso usa questa tecnica. Problema: un TSR che usi lo stesso interrupt manda in crash il sistema. Quest’applicazione dev’essere compilata con il modello di memoria TINY. Questa versione aggiunge la capacità che delta può essere nascosto inserendo (~) e azzerato inserendo (`). Questo è inteso come esempio di applicazione TSR reale. I TSR commerciali sono simili, oltre i caratteri di output si possono definire caratteri d’input, INT 16H, quando questi sono premuti, HOT KEY per esempio CRTL+SHIFT, si apre una windows. #include <stdio.h> #include <dos.h> #include <stdlib.h> #include <process.h> #include <conio.h> #define mono (int far *)0xb0000000 /* per i video mono...*/ #define cga (int far *)0xb8000000 /*...per la ega e la cga*/ #ifndef __TINY__ #error Occorre usare il modello di memoria TINY #endif void interrupt clock (void), init (void), display (int, int, int); void out (char *buffer,int y,int x); void interrupt screenout (unsigned bp,unsigned di,unsigned si,unsigned ds, unsigned es,unsigned dx,unsigned cx,unsigned bx,unsigned ax); union { long ltime; int stime [2];

Page 219: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 218 di 299

} p; union REGS regs; void interrupt (*oldclock)(void); void interrupt (*oldscreen)(void); int prevtime, time, minute, hour, dflag, temp;char buffer [6];int far *screen; char digits [] = {"0123456789"}; /* alloca spazio per la nostra area stack */ int savess, savesp, sflag;char stack [0x1000]; int main (void) { clrscr();init ();oldclock = getvect (0x1c);oldscreen = getvect (0x10); setvect (0x1c,clock);setvect(0x10,screenout);keep(0,0x1000);return (0); } /* Init - pone l’indirizzo di schermo e pulisce lo schermo */ void init (void) { int mode; prevtime = -1; dflag = 1; regs.h.ah = 0x0f; int86 (0x10, &regs, &regs); mode = regs.h.al; if (regs.h.ah != 80) abort ();if (mode == 7) screen = mono; else if (mode == 3 || mode == 2) screen = cga;else abort (); } /* Clock - prende l’interrupt e fornisce la funzione */ void interrupt clock (void) { /* mette nel nostro stack */ if (!sflag) { savesp = _SP; /* salva SP */ savess = _SS;_CX = (int)&stack[sizeof(stack)]; _SS = _DS; _SP = _CX; /* dirigono SP verso un grande blocco */ } sflag++; /* l’ora corrente (formato lungo) usando la chiamata BIOS 1a. Da dividere nel numero di minuti (formato corto). */ regs.h.ah = 0;int86 (0x1a, &regs, &regs); p.stime [0] = regs.x.dx; p.stime [1] = regs.x.cx;time = (int)(p.ltime / (long)1092); /* mostra l’ora corrente in formato 24 ore */ display (time, 0, 75); /* mostra delta*/ if (prevtime == -1) prevtime = time; if ((time -= prevtime) < 0) time += (24 * 60); if (dflag) display (time, 1, 75); /* passa il controllo alla successiva routine di interrupt */ (*oldclock)(); /* ripristina lo stack del chiamante */ if (!--sflag) { _SS = savess; _SP = savesp; } } /* Display - mostra l’ora sullo schermo alla posizione indicata. Ricordate che non possiamo eseguire chiamate DOS.*/ void display (int number,int y,int x) { hour = number / 60; minute = _DX; /* lo inserisce in un buffer ascii per l’output */ buffer [0] = digits [hour / 10]; buffer [1] = digits [hour % 10];buffer [2] = ‘:’; buffer [3] = digits [minute / 10];buffer [4] = digits [minute % 10]; buffer [5] = ‘\0’;out (buffer, y, x); } /* Out - mostra una stringa sullo schermo senza usare le chiamate di sistema */ void out (char *buffer,int y,int x)

Page 220: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 219 di 299

{ int far *scrptr; scrptr = screen + (y * 80 + x); while (*buffer) *scrptr++ = 0x1700 + *buffer++; } /* Screenout - esamina i caratteri da visualizzare usando il BIOS int 0x10. Quando incontra un ‘`’,azzera l’orologio;se trova un ‘~’,attiva e disattiva la visualizzazione di delta. */ void interrupt screenout (unsigned bp,unsigned di,unsigned si,unsigned ds, unsigned es,unsigned dx,unsigned cx,unsigned bx,unsigned ax) { if ((temp = (ax & 0xff00)) == 0x0900 || temp == 0x0e00) { if ((temp = (ax & 0x00ff)) == ‘`’) prevtime = -1; if (temp == ‘~’) dflag = !dflag; } /* passa il controllo alla routine BIOS per ottenere il carattere */ _AX = ax; _BX = bx; _CX = cx; _DX = dx; (*oldscreen)(); ax = _AX; bx = _BX; cx = _CX; dx = _DX; }

GESTIONE MOUSE Prima di usare il mouse, è necessario installare il suo driver. Ogni volta che è mosso il mouse o premuto un pulsante, è generato un INT 33H: contiene 35 funzioni selezionate da AX, i parametri, massimo tre, sono specificati in BX, CX, DX rispettivamente, l’informazione generata dalla funzione è restituita in AX, BX, CX e DX. Il driver elabora l’interrupt. Il puntatore del mouse è rappresentato in due modi. 1. Blocchetto se è attivato il modo testo. 2. Freccia se è attivato il modo grafico. La distanza di spostamento del mouse è misurata in MICKEY = 1/200 di pollice. AX = 11 restituisce il contatore MICKEY orizzontale e verticale relativo all’ultima chiamata alla funzione 11. Reinizializza a 0 i propri contatori. Il conteggio orizzontale è restituito in CX e quello verticale in DX. In pratica significa che se il mouse non è stato mosso dall’ultima chiamata entrambi i valori sono uguali a 0. Un valore positivo verticale significa che il mouse è stato mosso verso il basso, uno negativo è stato mosso verso l’alto. Un valore positivo orizzontale significa che il mouse è stato mosso verso destra, uno negativo è stato mosso verso sinistra. #include <dos.h> #include <stdio.h> #include <stdlib.h> #include <conio.h> #define NOT_MOVED 0

Page 221: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 220 di 299

#define RIGHT 1 #define LEFT 2 #define UP 3 #define DOWN 4 void mouse(int *ax,int *bx,int *cx,int *dx),set_mouse_position(int x,int y); void cursor_off(void), cursor_on(void),mouse_position(int *x, int *y); void mouse_motion(char *deltax,char *deltay),mouse_reset(void),read_mouse(void); int leftb_pressed(void), rightb_pressed(void); void goto_xy(int x, int y),mode(int mode_code); int main(int argc, char *argv[]) { char deltax, deltay; int x, y; clrscr(); if(argc!=2) { printf("\n\n\nDigitare: moustest <modo video>\n" "dove modovideo e’ il numero della modalita’ che si intende usare\n" "\nPREMENDO ENTRAMBI I PULSANTI DEL MOUSE SI TERMINA IL PROGRAMMA"); exit(1);} mode(atoi(argv[1])); mouse_reset(); /* Inizializza il mouse. */ cursor_on(); /* Attiva il cursore. */ do { goto_xy(0, 0); if(leftb_pressed()) printf("Pulsante sinistro "); if(rightb_pressed()) printf("Pulsante destro "); /* Mostra la posizione del mouse. */ mouse_position(&x, &y); goto_xy(0, 2); printf(" %d - %d ", x, y); goto_xy(0, 0); /* Controlla eventuali cambiamenti di posizione del mouse. */ mouse_motion(&deltax, &deltay); if(deltax || deltay) { printf("Direzione X: "); switch(deltax) { case NOT_MOVED: printf(" ");break; case RIGHT: printf("destra ");break; case LEFT: printf("sinistra ");break; } printf("Direzione Y: "); switch(deltay) { case NOT_MOVED: printf(" ");break; case UP: printf("su ");break; case DOWN: printf("giu’ ");break;} } /* Cicla fino a quando non sono contemporaneamente premuti i pulsanti sinistro e destro del mouse. */ } while(!(leftb_pressed() && rightb_pressed())); mode(3);return (0); } /* Imposta il modo video. */ void mode(int mode_code) { union REGS r; r.h.al = mode_code;r.h.ah = 0;int86(0x10, &r, &r);} /* Posiziona il cursore alle coordinate X,Y specificate. */ void goto_xy(int x, int y) { union REGS r;

Page 222: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 221 di 299

r.h.ah = 2; /* Funzione di indirizzamento cursore. */ r.h.dl = x; /* Coordinata colonna. */ r.h.dh = y; /* Coordinate riga. */ r.h.bh = 0; /* Pagina video. */ int86(0x10, &r, &r); } /************************************************/ /* Funzioni di interfacciamento mouse. */ /************************************************/ /* Disattiva il cursore del mouse AX=2 non vi e’ alcun valore di ritorno */ void cursor_off(void) { union REGS r;r.x.ax = 2;int86(0x33, &r, &r);} /* Attiva il cursore del mouse AX=1 non vi e’ alcun valore di ritorno */ void cursor_on(void) { union REGS r;r.x.ax = 1;int86(0x33, &r, &r);} /*Ritorna vero se e’ stato premuto il pulsante destro,altrimenti falso. AX=3 restituisce lo stato dei pulsanti in BX, la posizione orizzontale del cursore in CX e quella verticale in DX. Lo stato dei pulsanti e’ codificato in BX : quando e’ attivato il bit 0 e’ stato premuto il pulsante sinistro " " " " " 1 " " " " " destro, quando un bit e’ disattivato il pulsante associato non e’ stato premuto */ rightb_pressed(void) { union REGS r;r.x.ax = 3;int86(0x33, &r, &r);return r.x.bx & 2;} /*Ritorna vero se e’ stato premuto il pulsante sinistro,altrimenti falso. */ leftb_pressed(void) { union REGS r;r.x.ax = 3;int86(0x33, &r, &r);return r.x.bx & 1;} /* Imposta le coordinate per il cursore del mouse AX=4 il valore di CX=posizione orizzontale DX=posizione vertivale */ void set_mouse_position(int x, int y) { union REGS r;r.x.ax = 4;r.x.cx = x;r.x.dx = y;int86(0x33, &r, &r);} /* Restituisce le coordinate del cursore del mouse. */ void mouse_position(int *x, int *y) { union REGS r; r.x.ax = 3; /* Prende la posizione e lo stato del pulsante. */ int86(0x33, &r, &r); *x = r.x.cx;*y = r.x.dx; } void mouse_motion(char *deltax, char *deltay) { int sx, sy;union REGS r; r.x.ax = 11; /* funzione 11: prende la direzione di movimento. */ int86(0x33, &r, &r); sx = r.x.cx;sy = r.x.dx; if(sx > 0) *deltax = RIGHT;else if(sx < 0) *deltax = LEFT; else *deltax = NOT_MOVED; if(sy > 0) *deltay = DOWN;else if(sy < 0) *deltay = UP; else *deltay = NOT_MOVED; } /* Inizializza il mouse: AX=0 posiziona il mouse al centro delle schermo con il cursore disattivato, ritorna in BX il numero di pulsanti presenti e in AX=-1 (AX=0 mouse non installato) */ void mouse_reset(void) { union REGS r;r.x.ax = 0;int86(0x33, &r, &r); if((int) r.x.ax != -1) { printf("Mouse non installato (hardware o software).");exit(1); }

Page 223: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 222 di 299

if(r.x.bx != 2) { printf("E’ necessario un mouse a due pulsanti.");exit(1);} }

File MPAINT.CPP Applicazione grafica che consente la tracciatura di linee, rettangoli e cerchi. È possibile definire un oggetto e ruotarlo in entrambe le direzioni: oraria e antioraria, si possono copiare oppure spostare delle regioni di schermo. Inoltre, le immagini realizzate possono essere salvate su disco e caricate in un secondo tempo. Tramite mouse selezione: rettangoli, cerchi, linee e riempimenti, il pulsante destro è usato per definire i punti finali di linee, rettangoli e cerchi, per default il mouse non scrive. In un’applicazione pittorica l’utente deve poter mirare il punto corrente, ciò è ottenuto con xhairs. Muovendo il cursore o il mouse si effettua la tracciatura con il colore definito (2). Premendo i tasti 0, 1, 2 o 3 si può scegliere il colore della tavolozza corrente, il pennello può essere attivato o disattivato mediante il tasto p. I tasti PGUP e PGDN e END muovono il cursore a croce diagonale di 45°. F2 permette di muoversi di 5 pixel per volta. F1 di 1 pixel per volta. Per tracciare linee, cerchi e rettangoli devono essere definite le due coordinate, esempio per rettangoli i vertici opposti; per selezionare tale processo bisogna premere CR quando il cursore è nel punto desiderato. Una volta definite le coordinate si sceglie la funzione desiderata. R rettangolo. E rettangolo pieno. L traccia una linea. C cerchio. H cerchio pieno. Per spostare o copiare una regione dello schermo bisogna definire il vertice in alto a sinistra ed in basso a destra, poi posizionare il cursore in alto a sinistra e premere. M spostamento. I copy. Per ruotare un oggetto si deve definire l’oggetto stesso premendo il tasto D, poi definire ciascun segmento costituente l’oggetto stesso, premere il tasto F1 per indicare la fine del processo di definizione; quindi. U+A rotazione antioraria. U+O rotazione oraria. Per terminare il programma premere X. #include <dos.h> #include <iostream.h> #include <stdio.h> #include <math.h> #include <conio.h> #include <stdlib.h> #include <ctype.h> #include <string.h>

Page 224: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 223 di 299

#define NUM_SIDES 20 /* Numero di lati che un oggetto puo’ avere. Aumentatelo secondo le vostre esigenze. */ #define NOT_MOVED 0 #define RIGHT 1 #define LEFT 2 #define UP 3 #define DOWN 4 #define LEFTB 1 #define RIGHTB 2 #define MENU_MAX 20 /* Numero di elementi di menu del mouse. */ #define XMAX 319 #define YMAX 199 void line(int startx, int starty, int endx, int endy,int color); void box(int startx, int starty, int endx, int endy,int color); void fill_box(int startx, int starty, int endx, int endy,int color); void circle(int x_center, int y_center, int radius, int color); void fill_circle(int x_center, int y_center, int radius,int color); void plot_circle(int x, int y, int x_center, int y_center,int color); void mempoint(int x, int y, int color),palette(int pnum); void mode(int mode_code); void rotate_point(double theta, double *x, double *y,int x_org, int y_org); void display_object(double ob[][5], int lines); void rotate_object(double ob[][5], double theta,int x, int y, int lines); void move(int startx, int starty, int endx, int endy,int x, int y); void copy(int startx, int starty, int endx, int endy,int x, int y); void xhairs(int x, int y), goto_xy(int x, int y); void save_pic(void), load_pic(void); int define_object(double ob[][5], int x, int y),readkey(void); void set_mouse_position(int x, int y); void cursor_off(void), cursor_on(void); void mouse_position(int *x, int *y); void mouse_motion(char *deltax, char *deltay); void mouse_reset(void), wait_on(int button); int mouse_menu(int count, char item[][20], int x, int y); int menu(void), leftb_pressed(void), rightb_pressed(void); void read_mouse(void); int read_kb(void); unsigned char read_point(); /* Questa matrice conterra’ le coordinate di un oggetto definito dinamicamente. */ double object[NUM_SIDES][5]; double asp_ratio; /* Contiene il valore di aspetto per i cerchi. */ int x = 10, y = 10; /* Posizione corrente dello schermo. */ int cc = 2; /* Colore corrente. */ int on_flag = 1, mouse_on_flag = 0; /* Stato della penna scrive/non scrive. */ int pal_num = 1; /* Numero della tavolozza. */ /* Punto finale di una figura: linea, cerchio, o rettangolo. */ int startx=0, starty=0, endx=0, endy=0, first_point=1; int inc = 1; /* Incremento del movimento. */ int sides = 0; /* Numero di lati dell’oggetto definito. */ char deltax, deltay; /* Indicatori di posizione del mouse. */ int main(void) { char done = 0; mode(4); /* Abilita il modo video grafico 4 (CGA/EGA). */

Page 225: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 224 di 299

palette(0); /* Tavolozza 0. */ mouse_reset(); /* Inizializza il mouse. */ xhairs(x, y); /* Mostra il cursore a croce. */ set_mouse_position(x*2, y); /* Imposta la posizione iniziale del mouse. */ do { /* Controlla eventuali cambiamenti di posizione del mouse. */ mouse_motion(&deltax, &deltay);if(deltax || deltay) read_mouse(); /* Controlla l’eventuale pressione di un pulsante. */ if(leftb_pressed() || rightb_pressed()) read_mouse(); if(kbhit()) { done = read_kb(); /* Riposiziona il mouse in modo da farlo coincidere con il cursore. */ set_mouse_position(x*2, y); } } while (!done); mode(3); return (0); } /* Legge e processa i segnali del mouse. */ void read_mouse(void) { int oldx, oldy;int choice;oldx = x; oldy = y; xhairs(x, y); /* Cancella dalla posizione corrente. */ /* Se entrambi i pulsanti del mouse risultano premuti, viene richiesta la visualizzazione del menu del mouse. */ if(rightb_pressed() && leftb_pressed()) { choice = menu(); /* Prende la selezione del menu del mouse. */ switch(choice) { case 0: box(startx, starty, endx, endy, cc);break; case 1: if(starty < endy) circle(startx, starty, endy-starty, cc);else circle(startx, starty, starty-endy, cc);break; case 2: line(startx, starty, endx, endy, cc);break; case 3: fill_box(startx, starty, endx, endy, cc);break; case 4:if(starty < endy) fill_circle(startx, starty, endy-starty, cc); else fill_circle(startx, starty, starty-endy, cc);break; } } /* Il pulsante destro definisce il punto finale per le figure. */ else if(rightb_pressed()) { if(first_point) {startx = x; starty = y; } else {endx = x; endy = y;} first_point = !first_point; wait_on(RIGHTB); /* Attende il rilascio del pulsante. */ } if(deltax || deltay) { mouse_position(&x, &y); x = x / 2; /* Adegua le coordinate di schermo. */ /* Il pulsante sinistro chiama la funzione di disegno. */ if(leftb_pressed()) mouse_on_flag = 1; else mouse_on_flag = 0; if(mouse_on_flag) line(oldx, oldy, x, y, cc); } xhairs(x, y); /* Rivisualizza il cursore a croce. */ } /* Legge e processa un comando inserito da tastiera. */ read_kb(void) { union k{ char c[2]; int i; } key; key.i = readkey(); xhairs(x, y); /* Cancella il cursore a croce. */

Page 226: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 225 di 299

if(!key.c[0]) switch(key.c[1]) { case 75: /* Sinistra. */ if(on_flag) line(x, y, x-inc, y, cc);x -= inc; break; case 77: /* Destra. */ if(on_flag) line(x, y, x+inc, y, cc);x += inc; break; case 72: /* Su. */ if(on_flag) line(x, y, x, y-inc, cc); y -= inc;break; case 80: /* Giu’. */ if(on_flag) line(x, y, x, y+inc, cc); y += inc; break; case 71: /* Su a sinistra. */ if(on_flag) line(x, y, x-inc, y-inc, cc); x -= inc; y -= inc; break; case 73: /* Su a destra. */ if(on_flag) line(x, y, x+inc, y-inc, cc); x += inc; y -= inc; break; case 79: /* Giu’ a sinistra. */ if(on_flag) line(x, y, x-inc, y+inc, cc); x -= inc; y += inc; break; case 81: /* Giu’ a destra. */ if(on_flag) line(x, y, x+inc, y+inc, cc); x += inc; y += inc; break; case 59: inc = 1; /* F1 - lento. */ break; case 60: inc = 5; /* F2 - veloce. */ break; } else switch(tolower(key.c[0])) { case ‘p’: on_flag = !on_flag; /* Selezione pennello. */ break; case ‘1’: cc = 1; /* Colore 1. */ break; case ‘2’: cc = 2; /* Colore 2. */ break; case ‘3’: cc = 3; /* Colore 3. */ break; case ‘0’: cc = 0; /* Colore 0. */ break; case ‘r’: box(startx, starty, endx, endy, cc); break; case ‘e’: fill_box(startx, starty, endx, endy, cc); break; case ‘l’: line(startx, starty, endx, endy, cc); break; case ‘c’: if(starty < endy) circle(startx, starty, endy-starty, cc); else circle(startx, starty, starty-endy, cc); break; case ‘h’: if(starty < endy) fill_circle(startx, starty, endy-starty, cc); else fill_circle(startx, starty, starty-endy, cc); break; case ‘s’: save_pic();break; case ‘a’: load_pic();break; case ‘m’: /* Muove una regione di schermo. */ move(startx, starty, endx, endy, x, y); break;

Page 227: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 226 di 299

case ‘i’: /* Copia una regione di schermo. */ copy(startx, starty, endx, endy, x, y);break; case ‘d’: /* Definisce un oggetto da ruotare. */ sides = define_object(object, x, y);break; case ‘u’: /* Ruota un oggetto. */ rotate_object(object, 0.05, x, y, sides);break; case ‘\r’: /* Definisce la fine di una linea, cerchio, o rettangolo. */ if(first_point) { startx = x; starty = y; } else {endx = x; endy = y; } first_point = !first_point;break; case ‘t’: pal_num = pal_num==1 ? 2:1; palette(pal_num); } xhairs(x, y); /* Rivisualizza il cursore a croce. */ if(tolower(key.c[0])==‘x’) return 1; return 0; } /* Imposta la tavolozza. */ void palette(int pnum) { union REGS r; r.h.bh = 1; /* Codice per la scelta della tavolozza. */ r.h.bl = pnum; r.h.ah = 11; /* Imposta la funzione di tavolozza. */ int86(0x10, &r, &r); } void mode(int mode_code) { union REGS r;r.h.al = mode_code;r.h.ah = 0;int86(0x10, &r, &r);} /* Traccia un rettangolo. */ void box(int startx, int starty, /* Angolo in alto a sinistra. */ int endx, int endy, /* Angolo in basso a destra. */ int color) /* Colore usato. */ { line(startx, starty, endx, starty, color); line(startx, starty, startx, endy, color); line(startx, endy, endx, endy, color); line(endx, starty, endx, endy, color); } /* Traccia una linea con uno specifico colore mediante l’algoritmo di Bresenham basato sui valori interi.*/ void line(int startx, int starty, /* Angolo in alto a sinistra. */ int endx, int endy, /* Angolo in basso a destra. */ int color) /* Colore usato. */ { register int t, distance;int x=0, y=0, delta_x, delta_y;int incx, incy; /* Calcola la direzione dell’incremento. Un incremento 0 significa una linea verticale o orizzontale. */ delta_x = endx-startx;delta_y = endy-starty; /* Calcola la direzione dell’incremento. Un incremento 0 significa una linea verticale o orizzontale. */ if(delta_x>0) incx = 1; else if(delta_x==0) incx = 0;else incx = -1; if(delta_y>0) incy = 1; else if(delta_y==0) incy = 0;else incy = -1; /* Determina la distanza piu’ grande. */ delta_x = abs(delta_x);delta_y = abs(delta_y); if(delta_x>delta_y) distance = delta_x;else distance = delta_y; /* Traccia la linea. */ for(t=0; t<=distance+1; t++) { mempoint(startx, starty, color);x += delta_x;y += delta_y; if(x>distance) { x -= distance;startx += incx;}

Page 228: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 227 di 299

if(y>distance) { y -= distance;starty += incy; } } } /* Traccia il rettangolo pieno con il colore specificato.*/ void fill_box(int startx, int starty, /* Angolo in alto a sinistra. */ int endx, int endy, /* Angolo in basso a destra. */ int color) /* Colore usato. */ { register int i, begin, end; begin = startx<endx ? startx : endx; end = startx>endx ? startx : endx; for(i=begin; i<=end;i++) line(i, starty, i, endy, color); } /* Traccia un cerchio mediante l’algoritmo di Bresenham basato sui valori interi. */ void circle(int x_center, int y_center, /* Centro. */ int radius, /* Raggio. */ int color) /* Colore usato. */ { register int x, y, delta; asp_ratio = 1.0; /* Per usare un differente valore di aspetto, modificate questo numero. */ y = radius; delta = 3 - 2 * radius; for(x=0; x<y; ) { plot_circle(x, y, x_center, y_center, color); if (delta < 0) delta += 4*x+6; else { delta += 4*(x-y)+10; y--; } x++; } x = y; if(y) plot_circle(x, y, x_center, y_center, color); } /* Plot_circle attualmente traccia i punti che definiscono il cerchio.*/ void plot_circle(int x, int y, /* Punto da tracciare. */ int x_center, int y_center, /* Centro. */ int color) /* Colore usato. */ { int startx, endx, x1, starty, endy, y1; starty = y*asp_ratio;endy = (y+1)*asp_ratio; startx = x*asp_ratio;endx = (x+1)*asp_ratio; for (x1=startx; x1<endx; ++x1) { mempoint(x1+x_center, y+y_center, color); mempoint(x1+x_center, y_center-y, color); mempoint(x_center-x1, y_center-y, color); mempoint(x_center-x1, y+y_center, color); } for (y1=starty; y1<endy; ++y1) { mempoint(y1+x_center, x+y_center, color); mempoint(y1+x_center, y_center-x, color); mempoint(x_center-y1, y_center-x, color); mempoint(x_center-y1, x+y_center, color); } } /* Traccia un cerchio pieno chiamando ripetutamente la funzione di tracciatura di un cerchio, alla quale passa valori di raggio sempre piu’ piccoli.*/ void fill_circle(int x_center, int y_center, int radius, int color) { while(radius) { circle(x_center, y_center, radius, color); radius--; } } /* Scrive un punto direttamente alla RAM grafica del modo video 4.*/

Page 229: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 228 di 299

void mempoint(int x, int y, /* Coordinate del punto. */ int color) /* Colore usato. */ { union mask { char c[2]; int i; } bit_mask; int index, bit_position;unsigned char t; char xor; /* Effettua lo xor del colore o la sovrascrittura. */ /* Puntatore alla RAM grafica del modo video 4. */ char far *ptr = (char far *) 0xB8000000; bit_mask.i = 0xFF3F; /* 11111111 00111111 in binario. */ /* Controlla i limiti per il modo grafico 4. */ if(x<0 || x>XMAX || y<0 || y>YMAX) return; xor = color & 128; /* Controlla se e’ impostato il modo xor. */ color = color & 127; /* Azzera il bit piu’ alto. */ /* Imposta il bit_mask e i bit di colore nella giusta locazione. */ bit_position = x%4;color <<= 2*(3-bit_position); bit_mask.i >>= 2*bit_position; /* Cerca il byte corretto nella memoria di schermo. */ index = y*40 + (x >> 2); if(y % 2) index += 8152; /* Se e’ dispari usa il secondo banco. */ /* Scrive il colore. */ if(!xor) { /* Modo sovrascrittura. */ t = *(ptr+index) & bit_mask.c[0]; *(ptr+index) = t | color; } else { /* Modo xor. */ t = *(ptr+index) | (char) 0; *(ptr+index) = t ^ color; } } /* Visualizza il cursore a croce. Il bit 7 del byte di colore viene impostato a 1 tramite l’OR con 128, in modo da predisporre mempoint() ad effettuare lo XOR del colore presente sullo schermo e non la sua sovrascrittura. */ void xhairs(int x, int y) {line(x-4, y, x+3, y, 1 | 128);line(x, y+4, x, y-3, 1 | 128);} /* Restituisce il codice di scansione a 16 bit della tastiera. */ readkey(void) { union REGS r;r.h.ah = 0;return int86(0x16, &r, &r);} /*Legge il byte direttamente dalla VRAM del modo 4. Lavora come mempoint() ma invece di scrivere un punto restituisce il valore corrente. */ unsigned char read_point(int x, int y) { union mask { char c[2]; int i; } bit_mask; int index, bit_position; unsigned char t; /* Puntatore alla RAM del modo video grafico 4. */ char far *ptr = (char far *) 0xB8000000; bit_mask.i = 3; /* 11111111 00111111 in binario. */ /* Controlla i limiti per il modo grafico 4. */ if(x<0 || x>XMAX || y<0 || y>YMAX) return 0; /* Imposta il bit_mask e i bit di colore nella giusta locazione. */ bit_position = x%4; bit_mask.i <<= 2*(3-bit_position); /* Cerca il byte corretto nella memoria di schermo. */ index = y*40 +(x >> 2); if(y % 2) index += 8152; /* Se e’ dispari usa il secondo banco. */ /* Legge il colore. */

Page 230: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 229 di 299

t = *(ptr+index) & bit_mask.c[0]; t >>= 2*(3-bit_position); return t; } /*Salva su disco o carica da disco un’immagine grafica: semplice dato che quanto visualizzato sullo schermo e’ presente in VRAM. Pero’ bisogna permettere all’utente di inserire il nome del file quindi visualizzare messaggi sul video senza che il disegno venga modificato. Salva il video grafico corrente, subito le prime 14 linee del disegno cancellando tale area c’e’ spazio per la richiesta del nome del file. */ void save_pic(void) { char fname[80];FILE *fp;register int i, j;int e=0; /* Puntatore alla RAM del modo grafico 4. */ char far *ptr = (char far *) 0xB8000000;char far *temp; unsigned char buf[14][80]; /* Contiene i dati dello schermo. */ temp = ptr; /* Salva la parte alta dello schermo corrente. */ for(i=0; i<14; i++) for(j=0; j<80; j+=2) { buf[i][j] = *temp; /* Byte pari. */ buf[i][j+1] = *(temp+8152); /* Byte dispari. */ *temp = 0; *(temp+8152) = 0; /* Cancella la parte alta dello schermo. */ temp++; } goto_xy(0, 0); cout<<"Nome del file: "; gets(fname); if(!(fp=fopen(fname, "wb"))) { goto_xy(0, 0); cout<<"Non posso aprire il file. Premi un tasto. "; getch(); e = 1; /* Flag di errore. */ } temp = ptr; /* Ripristina la parte alta dello schermo corrente. */ for(i=0; i<14; i++) for(j=0; j<80; j+=2) { *temp = buf[i][j];*(temp+8152) = buf[i][j+1]; temp++; } if(e) return; /* Esce nel caso in cui il file non possa essere aperto. */ /* Salva l’immagine grafica nel file. */ for(i=0; i<8152; i++) { putc(*ptr, fp); /* Byte pari. */ putc(*(ptr+8152), fp); /* Byte dispari. */ ptr++; } fclose(fp); } /* Carica il video grafico da un file. */ void load_pic(void) { char fname[80];FILE *fp;register int i, j; /* Puntatore alla RAM del modo grafico 4. */ char far *ptr = (char far *) 0xB8000000; char far *temp; unsigned char buf[14][80]; /* Contiene i dati dello schermo. */ temp = ptr; /* Salva la parte alta dello schermo corrente. */ for(i=0; i<14; i++) for(j=0; j<80; j+=2) { buf[i][j] = *temp;buf[i][j+1] = *(temp+8152); *temp = 0; *(temp+8152) = 0; /* Cancella la parte alta dello schermo. */ temp++; } goto_xy(0, 0);cout<<"Nome del file: ";gets(fname); if(!(fp=fopen(fname, "rb"))) {

Page 231: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 230 di 299

goto_xy(0, 0);cout<<"Il file non puo’ essere aperto.\n";temp = ptr; /* Ripristina la parte alta dello schermo. */ for(i=0; i<14; i++) for(j=0; j<80; j+=2) { *temp = buf[i][j];*(temp+8152) = buf[i][j+1]; temp++; } return; } /* Carica l’immagine grafica dal file. */ for(i=0; i<8152; i++) { *ptr = getc(fp); /* Byte pari. */ *(ptr+8152) = getc(fp); /* Byte dispari. */ ptr++; } fclose(fp); } void goto_xy(int x, int y) { union REGS r; r.h.ah = 2; /* Funzione di indirizzamento del cursore. */ r.h.dl = x; /* Coordinata colonna. */ r.h.dh = y; /* Coordinata riga. */ r.h.bh = 0; /* Pagina video. */ int86(0x10, &r, &r); } /* Sposta una regione in un’altra posizione. */ void move(int startx, int starty, /* Coordinate in alto a sinistra. */ int endx, int endy, /* Coordinate in basso a destra. */ int x, int y) /* Coordinate in alto a sinistra della regione nella quale si desidera spostare l’immagine. */ { int i, j; unsigned char c; for(; startx<=endx; startx++, x++) for(i=starty, j=y; i<=endy; i++, j++) { c = read_point(startx, i); /* Legge un punto. */ mempoint(startx, i, 0); /* Cancella la vecchia immagine. */ mempoint(x, j, c); /* La scrive nella nuova posizione. */ } } /* Copia una regione in un’altra posizione. */ void copy(int startx, int starty, /* Coordinate in alto a sinistra. */ int endx, int endy, /* Coordinate in alto a destra. */ int x, int y) /* Coordinate in alto a sinistra della regione nella quale si vuole copiare l’immagine. */ { int i, j; unsigned char c; for(; startx<=endx; startx++, x++) for(i=starty, j=y; i<=endy; i++, j++) { c = read_point(startx, i); /* Legge un punto. */ mempoint(x, j, c); /* Lo scrive nella nuova locazione. */ } } /* Ruota un punto attorno all’origine, specificata dalle variabili x_org e y_org, dell’angolo teta. */ void rotate_point(double theta, /* Angolo di rotazione. */ double *x, double *y, /* Punto da ruotare. */ int x_org, int y_org) /* Origine. */ { double tx, ty;

Page 232: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 231 di 299

/* Ricalcola x e y. */ tx = *x - x_org;ty = *y - y_org; /* Ruota. */ *x=tx*cos(theta)-ty*sin(theta);*y=tx*sin(theta) + ty * cos(theta); /* Restituisce in PC i valori delle coordinate. */ *x += x_org;*y += y_org; } /* Ruota l’oggetto specificato. */ void rotate_object(double ob[][5], /* Definizione dell’oggetto. */ double theta, /* Angolo di rotazione in radianti. */ int x, int y, /* Locazione di origine. */ int lines) /* Numero di linee nell’immagine. */ { register int j; char ch; for(;;) { ch = getch(); /* Stabilisce in quale direzione ruotare. */ switch(tolower(ch)) { case ‘o’: /* Oraria. */ theta = theta < 0 ? -theta : theta;break; case ‘a’: /* Antioraria. */ theta = theta > 0 ? -theta : theta;break; default: return; } for(j=0; j<lines; j++) { /* Cancella la vecchia linea. */ line((int) ob[j][0], (int) ob[j][1],(int) ob[j][2], (int) ob[j][3], 0); rotate_point(theta, &ob[j][0],&ob[j][1], x, y); rotate_point(theta, &ob[j][2],&ob[j][3], x, y); line((int) ob[j][0], (int) ob[j][1], (int) ob[j][2], (int) ob[j][3], (int) ob[j][4]); } } } /* Visualizza un oggetto. */ void display_object(double ob[][5], int lines) { register int i; for(i=0; i<lines; i++) line((int) ob[i][0], (int) ob[i][1], (int) ob[i][2], (int) ob[i][3], (int) ob[i][4]); } /* Definisce un oggetto specificandone i punti terminali, mediante l’uso del mouse o della tastiera. */ define_object(double ob[][5], int x, int y) { union k { char c[2]; int i; } key; register int i, j; /* Puntatore alla RAM video del modo grafico 4. */ char far *ptr = (char far *) 0xB8000000;char far *temp; unsigned char buf[14][80]; /* Contiene i dati dello schermo. */ int sides = 0;char deltax, deltay;int oldx, oldy;temp = ptr; /* Salva la parte alta dello schermo. */ for(i=0; i<14; i++) for(j=0; j<80; j+=2) { buf[i][j] = *temp;buf[i][j+1] = *(temp+8152); *temp = 0; *(temp+8152) = 0; /* Cancella la parte alta dello schermo. */ temp++; }

Page 233: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 232 di 299

i = 0; key.i = 0;xhairs(x, y); do { goto_xy(0, 0);cout<<"Lato %d,", sides+1; if(i==0) cout<<" inserisci il primo punto "; else cout<<" inserisci il secondo punto "; do { /* aggiunta dei controlli per il mouse ****************/ /* Controlla se il mouse e’ stato mosso.*/ mouse_motion(&deltax, &deltay); /* Usa il pulsante sinistro per definire un punto. */ if(leftb_pressed()) { xhairs(x, y); /* Cancella il cursore a croce. */ /* Memorizza le coordinate del punto. */ ob[sides][i++] = (double) x;ob[sides][i++] = (double) y; if(i==4) { ob[sides][4] = read_point(x, y); /* Prende il colore. */ i = 0; sides++;} break; } } while(!kbhit() && !deltax && !deltay); if(leftb_pressed()) wait_on(LEFTB); if(deltax || deltay) { /* Se e’ stato mosso il mouse, viene aggiornata la posizione. */ oldx = x; oldy = y; mouse_position(&x, &y); x = x / 2; /* Adegua le coordinate virtuali dello schermo. */ xhairs(oldx, oldy); /* Cancella il cursore a croce. */ } else if(kbhit()) { key.i = readkey(); xhairs(x, y); /* Disegna il cursore a croce. */ if(key.c[0]==‘\r’) { /* Usa INVIO per definire un punto. */ ob[sides][i++] = (double) x;ob[sides][i++] = (double) y; if(i==4) { ob[sides][4] = read_point(x, y); /* Prende il colore. */ i = 0; sides++;} } /* Se e’ un tasto cursore allora muove il cursore a croce. */ if(!key.c[0]) switch(key.c[1]) { case 75: /* Sinistra. */ x -= 1; break; case 77: /* Destra. */ x += 1; break; case 72: /* Su. */ y -= 1; break; case 80: /* Giu’. */ y += 1; break; case 71: /* Su a sinistra. */ x -= 1; y -= 1; break; case 73: /* Su a destra. */ y -= 1; x += 1; break; case 79: /* Giu’ a sinistra. */ y += 1; x -= 1; break; case 81: /* Giu’ a destra. */ y += 1; x += 1; break; } } if(key.c[1]!=59) xhairs(x, y); } while(key.c[1] != 59); /* F1 per terminare. */

Page 234: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 233 di 299

temp = ptr; /* Ripristina la parte alta dello schermo. */ for(i=0; i<14; i++) for(j=0; j<80; j+=2) { *temp = buf[i][j];*(temp+8152) = buf[i][j+1];temp++;} return sides; } /* Visualizza un menu. */ menu(void) { register int i, j; /* Puntatore alla RAM del modo video grafico 4. */ char far *ptr = (char far *) 0xB8000000; char far *temp; unsigned char buf[14][80]; /* Contiene i dati dello schermo. */ int choice; char items[][20] = {"RET.","CERCHIO","LINEA", "R.PIENO", "C.PIENO" }; temp = ptr; /* Salva la parte alta dello schermo corrente. */ for(i=0; i<14; i++) for(j=0; j<80; j+=2) { buf[i][j] = *temp; /* Byte pari. */ buf[i][j+1] = *(temp+8152); /* Byte dispari. */ *temp = 0; *(temp+8152) = 0; /* Cancella la parte alta dello schermo. */ temp++; } goto_xy(0, 0); /* Attende finche’ l’ultimo pulsante premuto venga rilasciato. */ while(rightb_pressed() || leftb_pressed()) ; cursor_on(); choice = mouse_menu(5, items, 0, 0); cursor_off(); temp = ptr; /* Ripristina la parte alta dello schermo corrente. */ for(i=0; i<14; i++) for(j=0; j<80; j+=2) {*temp=buf[i][j];*(temp+8152)=buf[i][j+1];temp++;} return choice; } void cursor_off(void) { union REGS r;r.x.ax = 2;int86(0x33, &r, &r);} void cursor_on(void) { union REGS r;r.x.ax = 1;int86(0x33, &r, &r);} rightb_pressed(void) { union REGS r;r.x.ax = 3;int86(0x33, &r, &r);return r.x.bx & 2;} leftb_pressed(void) { union REGS r;r.x.ax = 3;int86(0x33, &r, &r);return r.x.bx & 1;} void set_mouse_position(int x, int y) { union REGS r;r.x.ax = 4;r.x.cx = x;r.x.dx = y;int86(0x33, &r, &r);} void mouse_position(int *x, int *y) { union REGS r;r.x.ax = 3;int86(0x33, &r, &r);*x = r.x.cx;*y = r.x.dx;} void mouse_motion(char *deltax, char *deltay) { int sx, sy;union REGS r;r.x.ax = 11;int86(0x33, &r, &r); sx = r.x.cx;sy = r.x.dx; if(sx > 0) *deltax = RIGHT;else if(sx < 0) *deltax = LEFT; else *deltax = NOT_MOVED; if(sy > 0) *deltay = DOWN;else if(sy < 0) *deltay = UP; else *deltay = NOT_MOVED; } /* Visualizza una linea di menu del mouse e restituisce la selezione. */ mouse_menu(int count, /* Numero di voci del menu. */ char item[][20], /* Voci di menu. */ int x, int y) /* Posizione di visualizzazione. */

Page 235: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 234 di 299

{ int i, len[MENU_MAX][2], t; int mousex, mousey; goto_xy(x, y); t = 0; for(i=0; i<count; i++) { cout<<item[i];len[i][0] = t; len[i][1] = t + strlen(item[i])*16;t = len[i][1] + 32;} do {if(rightb_pressed() || leftb_pressed()) break;} while(!kbhit()); /* Aspetta finche’ non viene premuto un pulsante. */ while(rightb_pressed()||leftb_pressed());mouse_position(&mousex,&mousey); /* Controlla se il cursore del mouse si trova su una voce di menu. */ if(mousey >= 0 && mousey < 8) for(i=0; i<count; i++) { if(mousex > len[i][0] && mousex < len[i][1]) return i;} return (-1); /* Non viene fatta alcuna selezione. */ } /* Restituisce 1 quando il pulsante specificato viene rilasciato. Premendo un pulsante si determina la generazione continua di interrupt fino al rilascio del pulsante stesso, wait_on() evita cio’ e fa in modo che la pressione di un pulsante generi un solo interrupt. */ void wait_on(int button) { if(button==LEFTB) while(leftb_pressed()); else while(rightb_pressed()) ; } void mouse_reset(void) { union REGS r;r.x.ax = 0; int86(0x33, &r, &r); if((int) r.x.ax != -1) { cout<<"Il mouse non e’ installato (hardware o software)."; exit(1);} if(r.x.bx != 2) { cout<<"E’ richiesto un mouse a due pulsanti.";exit(1);} }

GESTIONE TASTIERA La battitura dei caratteri può avvenire “in anticipo”, in pratica si può digitare un comando mentre è ancora in esecuzione il comando precedente. Il buffer di tastiera è un’area di memoria in cui sono accumulati i caratteri battuti dall’utente in attesa di essere letti dall’applicazione. Il microprocessore INTEL (INTegrated ELectronics) 8048 può memorizzare fino a 20 tasti ma le routine per la gestione della tastiera del BIOS (Basic Input Output System) riservano solo un buffer da 15 caratteri che si trova all’indirizzo (041E)H. Lo stato del buffer è importante per riconoscere i caratteri speciali del codice esteso. Per la gestione del buffer sono usati due puntatori agli indirizzi (041A)H e (041C)H che indicano rispettivamente l’inizio e la fine del buffer e variano in funzione del numero di caratteri che esso contiene. Quando i due puntatori sono uguali, non ci sono caratteri in attesa. Per la gestione della tastiera sono usati i seguenti interrupt. Int 16H, 00H Servizi Interfaccia Tastiera Descrizione Attende fino a che non è premuto un tasto. Categoria Servizi tastiera. Input AH = 00, AL = codice ASCII del tasto premuto. Int 21,0CH.

Page 236: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 235 di 299

Agli indirizzi (0417)H e (0418)H si trovano due byte che rappresentano lo stato dei tasti chiamati “toggle”, funzionano come un interruttore, in pratica controllano il passaggio da uno stato all’altro: INS, NUMLOCK, CAPSLOCK, …. Indirizzo (0417)H Bit 7 = 1 modo INSERT attivo. Bit 6 = 1 modo CAPSLOCK attivo. Bit 5 = 1 modo NUMLOCK attivo. Bit 4 = 1 modo SCROLLLOCK attivo. Bit 3 = 1 combinazione ALT+SHIFT attiva. Bit 2 = 1 combinazione CTRL+SHIFT attiva. Bit 1 = 1 tasto SHIFT di sinistra attivo. Bit 0 = 1 tasto SHIFT di destra attivo. Indirizzo (0418)H Bit 7 = 1 tasto INS abbassato. Bit 6 = 1 tasto CAPSLOCK abbassato. Bit 5 = 1 tasto NUMLOCK abbassato. Bit 4 = 1 tasto SCROLLLOCK abbassato. Bit 3 = 1 non usato. Bit 2 = 1 non usato. Bit 1 = 1 non usato. Bit 0 = 1 non usato. Tutti caratteri disponibili sulla tastiera sono rappresentati da un codice numerico che assume un valore da 0 a 255. Per rappresentare i tasti speciali si usa un codice esteso costituito da un intero il cui byte meno significativo è nullo, mentre il byte più significativo rappresenta il codice vero e proprio. Le funzioni dello standard ANSI non permettono le seguenti operazioni. Non si può leggere un singolo carattere, senza aspettare l’invio che termina la linea. Non si possono leggere i tasti funzione, i tasti cursore e i tasti speciali. Non si può sapere se ci sono caratteri in attesa nel buffer di tastiera. Allora bisogna scrivere applicazioni non portatili e scegliere le strade seguenti. Usare le funzioni di libreria allegate al compilatore. Chiamare le funzioni del DOS. Chiamare la funzione del BIOS bioskey: offre la possibilità di conoscere lo stato dei tasti

modificatori, in altre parole quelli che non producono caratteri ma modificano i caratteri prodotti dagli altri tasti: SHIFT (Maiuscole), CTRL (Control), ALT (Alternate), NUMLOCK, CAPSLOCK e SCROLLLOCK.

Accedere direttamente all’H/W è molto complesso e quindi sconsigliabile. È molto utile sapere se ci sono caratteri in attesa nel buffer, in pratica battuti ma non ancora letti dall’applicazione, la funzione kbhit ritorna vero se c’è almeno un carattere in attesa, permette di costruire un ciclo che l’utente può interrompere premendo un tasto. while (! kbhit()) { putch(‘.’); } /* questo ciclo continua a stampare punti finché non è premuto un tasto*/ Un altro uso molto frequente è la cancellazione del buffer di tastiera, per evitare che eventuali tasti premuti in precedenza rimangano in attesa. while (kbhit()) { getch(); } Il ciclo continua a leggere tasti finché non ha vuotato completamente il buffer, il carattere

Page 237: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 236 di 299

ritornato dalla getch è semplicemente ignorato. Tasti speciali Nel caso dei tasti normali, ad esempio la lettera (a), la getch ritorna un codice numerico compreso tra 0 e 255, è il codice ASCII del carattere corrispondente al tasto, invece ritorna 0 per segnalare che si tratta di un tasto speciale e va chiamata una seconda volta, alla seconda chiamata, ritorna un numero, il codice di scansione del tasto premuto.

Tasti funzione Tasti cursore

59..68 F1..F10 75 Sinistra

84..93 SHIFT-F1..SHIFT-F10 77 Destra

94..103 CTRL-F1..CTRL-F10 72 Alto

104..113 ALT-F1..ALT-F10 80 Basso

133..134 F11..F12 115 CTRL-Sinistra

135..136 SHIFT-F11..SHIFT-F12 116 CTRL-Destra

137..138 CTRL-F11..CTRL-F12 71 HOME

139..140 ALT-F11..ALT-F12 72 END (Fine)

82 INS 119 CTRL-HOME

83 DEL (CANC) 117 CTRL-END

15 SHIFT-TAB 73 PGUP (PGSU)

81 PGDN (PGGIÙ)

132 CTRL-PGUP

118 CTRL-PGDN

Esempio, interrupt per memorizzare i byte generati da dieci battute sulla tastiera. #include <stdio.h> #include <conio.h> #include <dos.h> #define IRQ9 9 /*Vettore interrupt hardware IRQ1*/ int i;int bcoun=0; /*Contatore dei caratteri memorizzati nel buffer*/ char maschera; /*Registro di maschera PIC*/ char buffer [10]; /*Memorizza gli scan code letti*/ void interrupt (*oldirq9)(void); /*Memorizza IP originale dell’interrupt 9; Interrupt personale: ad ogni IRQ1 salva il byte letto dalla porta 60H; Riempito il buffer setta un flag*/ void interrupt get_scancode(void); int main (void) { clrscr(); printf("Inserisci dieci caratteri "); for (i=0;i<10;i++) buffer[i]=0; /*Inizializza il buffer*/ oldirq9=getvect(IRQ9); /*Salva IP originale IRQ9*/ /*getvect restituisce l’IP dell’interrupt il cui numero e’ passato come parametro*/ setvect (IRQ9,get_scancode); /*Installa IRQ9 personalizzato*/ /*setvect registra nella posizione della tabella degli interrupt dichiarata come parametro l’IP dell’interrupt personalizzato*/

Page 238: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 237 di 299

while (bcoun !=10); /*Fine riempimento buffer*/ setvect(IRQ9,oldirq9); /*Ripristina interrupt IRQ9 originale*/ getchar(); /*Istruzione per il debug*/ asm { push ax in al,21h /*legge la maschera del PIC*/ or al,00000001b /*modifica il bit 0*/ out 21h,al /*scrive la nuova maschera*/ pop ax } delay(5000); asm { push ax in al,21h and al,11111110b out 21h,al pop ax } return (0); } void interrupt get_scancode(void) { maschera=inportb(0x21); wait: asm { in al,64h and al,1 jz wait } asm { in al,60h mov si,bcoun cmp si,9 jg skip lea bx,buffer mov [bx+si],al inc si mov bcoun,si } skip: asm { mov al,20h out 20h,al } /*Essendo un interrupt hardware prima del termine occorre mandare i codici di reset dell’interrupt al PIC (EOI)*/ } 1. Compilare il programma. 2. Eseguirlo con TD.EXE, è il debugger della Borland. 3. Inserire un breakpoints all’istruzione getchar Breakpoints/Toggle F2. 4. Run.

Page 239: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 238 di 299

5. Digitare la sequenza seguente: abcdefg. 6. Raggiunto il brekpoints Data/Inspect... variabile buffer. 7. Esempio a genera lo scan code 1E. File CTRL.C Interrupt handler del CTRL-BREAK O CTRL-C. Chiude tutti i file dati, termina l’applicazione e torna al DOS. Non è sempre desiderabile che l’applicazione interrotta torni al DOS ma ad esempio ad un menu principale: è l’applicazione stessa che deve gestire il CTRL+C. DOS esamina la tastiera per I/O quando intercetta un CTRL+C esegue INT 23H, è una locazione inizializzata dal DOS al momento del passaggio del controllo ad un COM o un EXE, grazie a questo indirizzo l’utente ha il prompt (C>) quando preme CTRL+C. IL programmatore è libero di ridefinire questo indirizzo, un’applicazione può avere più break handler ma solo uno può essere attivo. Break = on|off in config.sys Non è elegante. Allora può essere usata la routine ctrlbrk del C per stabilire il proprio handler. #include <stdio.h> #include <dos.h> #include <conio.h> #include <setjmp.h> int break1 (void); int break2 (void); jmp_buf save; int main (void) { int value;clrscr();value = setjmp (save); switch (value) { case 0: ctrlbrk (break1); printf ("Entro nel primo ciclo\n"); for (;;) printf (" Ciclo infinito #1\n"); case 1: ctrlbrk (break2); printf ("Entro nel secondo ciclo\n"); for (;;) printf (" Ciclo infinito #2\n"); default:printf ("Questo e’ tutto\n"); } return (0); } /* Break1 - intercetta il primo control-break */ int break1 (void) { printf ("Ecco il primo break !\n");longjmp (save, 1);} /* Break2 - intercetta il secondo control-break */ int break2 (void) {printf ("Ecco il secondo break !\n");longjmp (save, 2);}

GESTIONE FINESTRE È una porzione di schermo che è utilizzata per un impiego specifico. Prima che la finestra sia visualizzata, ciò che risulta presente sullo schermo è memorizzato, dopo l’impiego della finestra bisogna ripristinare il contenuto originale. È possibile avere numerose finestre contemporaneamente presenti sullo schermo. Si deve consentire la modifica interattiva della dimensione e della posizione. Due tipi di problemi. 1. Non si può consentire che la scrittura raggiunga i limiti della finestra. 2. Si deve prevenire la sovrascrittura.

Page 240: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 239 di 299

Nessuna delle routine d’I/O di console può essere usata: si devono sviluppare funzioni di I/O specifico per le finestre. Ogni task possiede una propria finestra di lavoro: quando è in esecuzione è attivata la sua finestra, quando termina è eliminata. L’interruzione di un task, sospensione delle sue attività, non comporta la rimozione della finestra. Però il task che ha causato l’interruzione attiverà la propria finestra disponendola in primo piano: in altre parole, è considerato come una piccola pausa temporanea. File WINDOWS.C Routine per la gestione di finestre inserite in un’applicazione che simula un editor. Tre speciali utilità a finestre pop-up illustrano la potenza consentita dalla gestione di finestre. Le routine di finestre pop-up usate sono le seguenti. Il calcolatore a quattro funzioni: RPN (Reverse Polish Notation). Il convertitore da formato decimale ad esadecimale e un semplice blocco appunti. F1 - Dimostra la funzione windows_xy. F2 - Dimostra il dimensionamento e il posizionamento di una finestra. F3 - Richiama il calcolatore. F4 - Richiama il convertitore da decimale a esadecimale. F5 - Richiama il blocco note. #include <stdio.h> #include <dos.h> #include <bios.h> #include <stdlib.h> #include <ctype.h> #include <string.h> #define BORDER 1 #define ESC 27 #define MAX_FRAME 10 #define REV_VID 0x70 #define NORM_VID 7 #define MAX 100 #define MAX_NOTE 10 void save_video(int num),restore_video(int num),goto_xy(int x,int y); void write_string(int x, int y, char *p, int attrib),cls(void); void write_char(int x, int y, char ch, int attrib),draw_border(int num); void display_header(int num),window_gets(int num, char *s), size(int num); void move(int num),window_cls(int num),window_cleol(int num),window(int num); void dectohex(void), notepad(void), calc(void),deactivate(int num); void window_bksp(int num); int window_upline(int num), window_downline(int num); int make_window(int num,char *header,int startx,int starty,int endx,int endy, int border); int window_getche(int num),window_putchar(int num, char ch); int window_xy(int num, int x, int y),video_mode(void), get_special(void); int push(int i), pop(void),readkey(void); char far *vid_mem; /*Tutti i parametri necessari per la struttura delle finestre devono essere resi disponibili a tutte le finestre funzionanti in contemporanea. */ struct window_frame { int startx, endx, starty, endy; /* Posizione e ingombro della finestra: coordinate angolo sinistro e destro. */ int curx, cury; /* Posizione corrente del cursore nella finestra: dev’essere manipolato dalle routine di gestione. */

Page 241: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 240 di 299

unsigned char *p; /* Puntatore al buffer di memoria schermo. */ char *header; /* Messaggio di testa: titolo della finestra */ int border; /* Stato del bordo: disegnato/non disegnato. */ int active; /* Attivita’ della finestra: presente/non presente. */ } frame[MAX_FRAME]; int *p; /* Puntatore nello stack. */ int *tos; /* Punta alla cima dello stack. */ int *bos; /* Punta alla coda dello stack. */ char notes[MAX_NOTE][80]; int main(void) { union inkey { char ch[2]; int i; } c; int i;char ch;cls();goto_xy(0,0); /* Per prima cosa crea il corpo di ciascuna finestra. */ make_window(0, " Editor [Esc per uscire] ", 0, 0, 78, 24, BORDER); make_window(1, " Da decimale a esadecimale ", 40, 7, 70, 10, BORDER); make_window(2, " Calcolatore ", 20, 8, 60, 12, BORDER); make_window(3, " Blocco appunti [F1 per uscire] ", 20, 5, 60, 16, BORDER); /* Utilizza window() per attivare la finestra specificata. */ window(0); do { c.i = window_getche(0); ch = c.i; /* Usa solo il byte basso. */ if(ch==‘\r’) /* Dev’essere per forza CR + LF. */ window_putchar(0, ‘\n’); switch(c.ch[1]) { /* Controlla se e’ un cursore o un tasto funzione. */ case 59: /* F1 dimostra la funzione window_xy(). */ window(1); for(i=0; i<10; i++) if(window_xy(1, i, i)) window_putchar(1,’X’); window_getche(1);deactivate(1);break; case 60: /* F2 dimostra dimensionamento e spostamento della finestra. */ size(1);move(1);break; case 61: /* F3 richiama il calcolatore. */ calc();break; case 62: /* F4 richiama il convertitore decimale/esadecimale. */ dectohex();break; case 63: /* F5 richiama il blocco appunti. */ notepad();break; case 72: /* Su. */ window_upline(0);break; case 80: /* Giu’. */ window_downline(0);break;} } while (ch!=ESC); deactivate(0); /* Elimina la finestra di Editor. */ return 0; } /*********************************************************/ /* Funzioni di gestione della finestra. */ /*********************************************************/ /* Visualizza una finestra pull-down. */ void window(int num) /* Numero della finestra che si desidera usare. */ { int vmode;int x, y;vmode = video_mode(); if((vmode!=2) && (vmode!=3) && (vmode!=7)) {

Page 242: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 241 di 299

printf("Il modo video dev’essere testo a 80 colonne.");exit(1);} /* Determina gli indirizzi appropriati della RAM video. */ if(vmode==7) vid_mem = (char far *) 0xB0000000; else vid_mem = (char far *) 0xB8000000; /* Prende la finestra attiva. */ if(!frame[num].active) { /* Non correntemente in uso. */ save_video(num); /* Memorizza lo schermo corrente. */ frame[num].active=1;} /* Definisce il flag come attivo. */ if(frame[num].border) draw_border(num); display_header(num); /* Visualizza la finestra. */ x = frame[num].startx + frame[num].curx + 1; y = frame[num].starty + frame[num].cury + 1; goto_xy(x, y); } /* Costruisce la struttura di dati per una finestra pull-down. Se viene restituito 1 significa che la struttura puo’ essere costruita, altrimenti viene restituito 0. */ make_window( int num, /* Numero della finestra. */ char *header, /* Titolo da visualizzare in testa alla finestra. */ int startx, int starty, /* Coordinate X,Y dell’angolo in alto a sinistra. */ int endx, int endy, /* Coordinate X,Y dell’angolo in basso a destra. */ int border) /* Se vale 0 non viene disegnato il bordo. */ { unsigned char *p; if(num>MAX_FRAME) {printf("Troppe finestre.\n");return 0;} if((starty>24) || (starty<0) || (startx>78) || (startx<0)) { printf("Errore dimensionale.");return 0;} if((endy>24) || (endx>79)) {printf("La finestra non ci sta.");return 0;} /* Alloca memoria sufficiente per tenere la struttura di una finestra. */ p = (unsigned char *) malloc(2*(endy-starty+1)*(endx-startx+1)); if(!p) exit(1); /* Inserite qui le vostre routine di gestione errori. */ /* Costruisce la struttura di dati. */ frame[num].startx = startx; frame[num].endx = endx; frame[num].starty = starty; frame[num].endy = endy; frame[num].p = p;frame[num].header = header; frame[num].border = border;frame[num].active = 0; frame[num].curx = 0; frame[num].cury = 0;return 1; } /* Disattiva una finestra e la rimuove dallo schermo. */ void deactivate(int num) { /* Riporta la posizione del cursore nell’angolo in alto a sinistra. */ frame[num].curx = 0;frame[num].cury = 0; frame[num].active = 0;restore_video(num); } /* Cambia interattivamente le dimensioni della finestra. Con entrambe le function size() e move() dopo aver terminato la modifica della finestra dev’essere premuto F1, premendo F2 si interrompono le function e la finestra ritornera’ alla sua forma o posizione iniziale. */ void size(int num) { char ch; int x, y, startx, starty; /* La attiva, se necessario. */ if(!frame[num].active) window(num); startx = x = frame[num].startx;starty = y = frame[num].starty; window_xy(num, 0, 0); do { ch = get_special();

Page 243: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 242 di 299

switch(ch) { case 75: /* Sinistra. */ startx--;break; case 77: /* Destra. */ startx++;break; case 72: /* Su. */ starty--;break; case 80: /* Giu’. */ starty++;break; case 71: /* Su a sinistra. */ starty--; startx--;break; case 73: /* Su a destra. */ starty--; startx++;break; case 79: /* In basso a sinistra. */ starty++; startx--;break; case 81: /* In basso a destra. */ starty++; startx++;break; case 60: /* F2: annulla ed usa la dimensione originale. */ startx = x;starty = y;ch = 59;} /* Controlla se e’ fuori portata video. */ if(startx<0) startx++; if(startx>=frame[num].endx) startx--; if(starty<0) starty++; if(starty>=frame[num].endy) starty--; deactivate(num);frame[num].startx = startx; frame[num].starty = starty;window(num); } while(ch!=59); /* F1 per terminare l’operazione. */ deactivate(num); } /* Muove interattivamente una finestra. */ void move(int num) { char ch; int x, y, ex, ey, startx, starty, endx, endy; /* La attiva, se necessario. */ if(!frame[num].active) window(num); startx = x = frame[num].startx;starty = y = frame[num].starty; endx = ex = frame[num].endx;endy = ey = frame[num].endy; window_xy(num, 0, 0); do { ch = get_special(); switch(ch) { case 75: /* Sinistra. */ startx--;endx--;break; case 77: /* Destra. */ startx++;endx++;break; case 72: /* Su. */ starty--;endy--;break; case 80: /* Giu. */ starty++;endy++;break; case 71: /* Su a sinistra. */ starty--; startx--;endy--; endx--;break; case 73: /* Su a destra. */ starty--; startx++;endy--; endx++;break; case 79: /* In basso a sinistra. */ starty++; startx--;endy++; endx--;break;

Page 244: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 243 di 299

case 81: /* In basso a destra. */ starty++; startx++;endy++; endx++;break; case 60: /* F2: annulla ed usa la dimensione originale. */ startx = x;starty = y;endx = ex;endy = ey;ch = 59;} /* Verifica che non sia fuori portata video. */ if(startx<0) {startx++;endx++;} if(endx>=79) {startx--;endx--;} if(starty<0) {starty++;endy++;} if(endy>=25) {starty--;endy--;} deactivate(num); frame[num].startx = startx;frame[num].starty = starty; frame[num].endx = endx;frame[num].endy = endy; window(num); } while(ch!=59); /* F1 per terminare l’operazione. */ deactivate(num); } /* Visualizza il titolo della finestra nella locazione appropriata. Se il messaggio e’ piu’ largo della dimensione orizzontale della finestra, non viene visualizzato. Se si desidera che il messaggio venga visualizzato in negativo si deve usare REV_VID al posto di NORM_VID. */ void display_header(int num) { register int x, len;x = frame[num].startx; /* Calcola la posizione di partenza corretta per centrare il titolo della finestra. Se il valore e’ negativo il titolo non puo’ starci. */ len = strlen(frame[num].header);len = (frame[num].endx - x - len) / 2; if(len<0) return; /* Non visualizzarlo. */ x+=len;write_string(x, frame[num].starty,frame[num].header, NORM_VID); } /* Disegna il bordo di una finestra. */ void draw_border(int num) { register int i; char far *v, far *t; v = vid_mem; t = v; for(i=frame[num].starty+1; i<frame[num].endy; i++) { v += (i*160) + frame[num].startx*2; *v++ = 179; /* Disegna le linee verticali. */ *v = NORM_VID;v = t;v += (i*160) + frame[num].endx*2; *v++ = 179;*v = NORM_VID;v = t; } for(i=frame[num].startx+1; i<frame[num].endx; i++) { v += (frame[num].starty*160) + i*2; *v++ = 196; /* Disegna le linee orizzontali. */ *v = NORM_VID;v = t;v += (frame[num].endy*160) + i*2; *v++ = 196;*v = NORM_VID;v = t;} /* Disegna gli angoli. */ write_char(frame[num].startx, frame[num].starty,(char) 218, NORM_VID); write_char(frame[num].startx, frame[num].endy,(char) 192, NORM_VID); write_char(frame[num].endx, frame[num].starty,(char) 191, NORM_VID); write_char(frame[num].endx, frame[num].endy,(char) 217, NORM_VID); } /*************************************************************/ /* Funzioni di input/output delle finestre. */ /*************************************************************/ /*Scrive una stringa alla posizione corrente del cursore nella finestra specificata. Restituisce 0 se la finestra non e’ attiva, altrimenti restituisce 1. */ window_puts(int num, char *str)

Page 245: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 244 di 299

{ /* Si assicura che la finestra sia attiva. */ if(!frame[num].active) return 0; for( ; *str; str++) window_putchar(num, *str);return 1; } /*Scrive un carattere alla posizione corrente del cursore nella finestra specificata. Restituisce 0 se la finestra non e’ attiva, altrimenti restituisce 1. Il carattere non deve sovrascrivere il bordo della finestra. Non considera errore la condizione per la quale il carattere non puo’ essere scritto al fine di evitare che sovrascriva il bordo. Cio’ e’ dovuto al fatto che la dimensione della finestra puo’ essere modificata dinamicamente, per cui un messaggio che prima entrava nella finestra potrebbe non entrare piu’ a seguito del ridimensionamento. La function si comporta non visualizzando quanto si verrebbe a trovare fuori dalla finestra. */ window_putchar(int num, char ch) { register int x, y;char far *v; /* Si assicura che la finestra sia attiva. */ if(!frame[num].active) return 0; x = frame[num].curx + frame[num].startx + 1; y = frame[num].cury + frame[num].starty + 1; v = vid_mem; v += (y*160) + x*2; /* Calcola l’indirizzo. */ if (y>=frame[num].endy) { return 1;} if(x>=frame[num].endx) {return 1;} if(ch==‘\n’) { /* Carattere di Invio. */ y++; x = frame[num].startx+1;v = vid_mem; v += (y*160) + x*2; /* Calcola l’indirizzo. */ frame[num].curx = 0; /* Definisce a 0 il valore di X. */ frame[num].cury++; /* Incrementa il valore di Y. */ } else { frame[num].curx++; *v++ = ch; /* Scrive il carattere. */ *v++ = NORM_VID;} /* Attributo video normale. */ window_xy(num, frame[num].curx, frame[num].cury); return 1; } /*Posiziona il cursore in una finestra alla locazione specificata. Restituisce 0 se e’ fuori dai limiti, altrimenti restituisce un valore differente da 0. Sono coordinate relative alla finestra, a prescindere dalla posizione della finestra rispetto allo schermo. */ window_xy(int num, int x, int y) { if(x<0 || x+frame[num].startx>=frame[num].endx-1) return 0; if(y<0 || y+frame[num].starty>=frame[num].endy-1) return 0; frame[num].curx = x;frame[num].cury = y; goto_xy(frame[num].startx+x+1, frame[num].starty+y+1);return 1; } /* Legge una stringa da una finestra. */ void window_gets(int num, char *s) { char ch, *temp; temp = s; for(;;) { ch = window_getche(num); switch(ch) { case ‘\r’: /* E’ stato premuto Invio. */ *s=‘\0’;return; case ‘\b’: /* Tasto cancellazione. */ if(s>temp) {

Page 246: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 245 di 299

s--;frame[num].curx--; if(frame[num].curx<0) frame[num].curx = 0; window_xy(num, frame[num].curx, frame[num].cury); write_char(frame[num].startx+frame[num].curx+1, frame[num].starty+frame[num].cury+1, ‘ ‘, NORM_VID);} break; default: *s = ch; s++; } } } /*Effettua una richiesta di inserimento tasto dall’interno di una finestra. Restituisce il codice di scansione a 16 bit inserito. Per ignorare tale codice basta assegnare un carattere al valore di ritorno. Il cursore e’ situato nella posizione corrente nella finestra, ed un tasto viene letto: se si tratta di un tasto normale allora viene incrementata la X ed il dato viene visualizzato; se la X si trova sul bordo della finestra, il valore di X viene decrementato per prevenire la sovrascrittura del bordo.*/ window_getche(int num) { union inkey { char ch[2]; int i; } c; if(!frame[num].active) return 0; /* Finestra non attiva. */ window_xy(num, frame[num].curx, frame[num].cury); c.i = readkey(); /* Legge il tasto. */ if(c.ch[0]) { switch(c.ch[0]) { case ‘\r’: /* Il tasto Invio e’ stato premuto. */ break; case ‘\b’: /* Tasto di cancellazione. */ break; default: if(frame[num].cury+frame[num].starty < frame[num].endy-1) { write_char(frame[num].startx+ frame[num].curx+1, frame[num].starty+frame[num].cury+1, c.ch[0], NORM_VID); frame[num].curx++;} } if(frame[num].curx < 0) frame[num].curx = 0; if(frame[num].curx+frame[num].startx>frame[num].endx-2)frame[num].curx--; window_xy(num, frame[num].curx, frame[num].cury);} return c.i; } /* Cancella una finestra. */ void window_cls(int num) { register int i,j; char far *v, far *t; v = vid_mem; t = v; for(i=frame[num].starty+1; i<frame[num].endy; i++) for(j=frame[num].startx+1; j<frame[num].endx; j++) { v = t;v += (i*160) + j*2; *v++ = ‘ ‘; /* Scrive uno spazio. */ *v = NORM_VID;} /* Attributo normale. */ frame[num].curx = 0;frame[num].cury = 0; } /* Cancella fino al termine della linea corrente. */ void window_cleol(int num) { register int i, x, y; x = frame[num].curx; y = frame[num].cury; window_xy(num, frame[num].curx, frame[num].cury); for(i=frame[num].curx; i<frame[num].endx-1; i++) window_putchar(num,’ ‘); window_xy(num, x, y);

Page 247: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 246 di 299

} /*Muove il cursore nella riga soprastante. Restituisce un valore diverso da 0 se l’operazione ha avuto successo, altrimenti restituisce 0. */ window_upline(int num) { if(frame[num].cury > 0) { frame[num].cury--;window_xy(num,frame[num].curx,frame[num].cury); return 1;} return 0; } /*Muove il cursore nella riga sottostante. Restituisce un valore diverso da 0 se l’operazione ha avuto successo, altrimenti restituisce 0. */ window_downline(int num) { if(frame[num].cury < frame[num].endy-frame[num].starty-1) { frame[num].cury++; window_xy(num, frame[num].curx, frame[num].cury);return 1;} return 1; } /* Torna indietro di un carattere. */ void window_bksp(int num) { if(frame[num].curx>0) { frame[num].curx--;window_xy(num, frame[num].curx, frame[num].cury); window_putchar(num, ‘ ‘);frame[num].curx--; window_xy(num, frame[num].curx, frame[num].cury); } } /*****************************************************/ /* Funzioni di vario genere. */ /*****************************************************/ /* Visualizza una stringa con l’attributo specifico. */ void write_string(int x, int y, char *p, int attrib) { char far *v;v = vid_mem; v += (y*160) + x*2; /* Calcola l’indirizzo. */ while(*p) { *v++ = *p++; /* Scrive il carattere. */ *v++ = attrib;} /* Scrive l’attributo. */ } /* Scrive il carattere con l’attributo specificato. */ void write_char(int x, int y, char ch, int attrib) { char far *v;v = vid_mem;v += (y*160) + x*2; *v++ = ch; /* Scrive il carattere. */ *v = attrib; /* Scrive l’attributo. */ } /* Memorizza una porzione dello schermo. */ void save_video(int num) { register int i,j; char *buf_ptr; char far *v, far *t; buf_ptr = frame[num].p;v = vid_mem; for(i=frame[num].startx; i<frame[num].endx+1; i++) for(j=frame[num].starty; j<frame[num].endy+1; j++) { t = (v + (j*160) + i*2); *buf_ptr++ = *t++;*buf_ptr++ = *t; *(t-1) = ‘ ‘;} /* Cancella la finestra. */ } /* Ripristina una porzione di schermo. */ void restore_video(int num) { register int i,j; char far *v, far *t; char *buf_ptr;

Page 248: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 247 di 299

buf_ptr = frame[num].p;v = vid_mem; t = v; for(i=frame[num].startx; i<frame[num].endx+1; i++) for(j=frame[num].starty; j<frame[num].endy+1; j++) { v = t; v += (j*160) + i*2; *v++ = *buf_ptr++; /* Scrive il carattere. */ *v = *buf_ptr++;} /* Scrive l’attributo. */ frame[num].active = 0; /* Ripristina lo schermo con la restore_video(). */ } void cls(void) { union REGS r; r.h.ah = 6;r.h.al = 0;r.h.ch = 0;r.h.cl = 0; r.h.dh = 24;r.h.dl = 79;r.h.bh = 7;int86(0x10, &r, &r); } void goto_xy(int x, int y) {union REGS r;r.h.ah=2;r.h.dl=x;r.h.dh=y;r.h.bh=0;int86(0x10, &r, &r);} /* Restituisce il codice di posizione dei tasti cursore e funzione. */ get_special(void) { union inkey { char ch[2]; int i; } c; c.i = readkey(); /* Legge il tasto. */ return c.ch[1]; } video_mode(void) {union REGS r;r.h.ah=15;return int86(0x10, &r, &r) & 255;} /* Restituisce il codice di scansione a 16 bit ottenuto dalla tastiera. */ int readkey(void) {union REGS r;r.h.ah = 0;return int86(0x16, &r, &r);} /***********************************************************************/ /*Calcolatore a quattro funzioni basato sullo stack di parametri, ovvero funzionante secondo il metodo della notazione polacca inversa (RPN REVERSE POLISH NOTATION usato dalla Hewlett Packard). I parametri sono inseriti subito nello stack e poi gli operatori. Ogni volta che viene incontrato un operatore, i primi due operandi presenti sullo stack verranno rimossi e la operazione verra’ applicata su di essi. Il risultato viene messo nello stack e visualizzato. */ void calc(void) { char in[80],out[80];int answer,stack[MAX];int a,b;p=stack;tos=p; bos = p+MAX-1;window(2); do { window_xy(2, 0, 0);window_cleol(2); window_puts(2, ": "); /* Prompt della calcolatrice. */ window_gets(2, in);window_puts(2, "\n "); window_cleol(2); switch(*in) { case ‘+’: /* Operatore di somma. */ a = pop();b = pop();answer = a+b;push(a+b);break; case ‘-’: /* Operatore di sottrazione. */ a = pop();b = pop();answer = b-a;push(b-a);break; case ‘*’: /* Operatore di moltiplicazione. */ a = pop();b = pop();answer = b*a;push(b*a);break; case ‘/’: /* Operatore di divisione. */ a = pop();b=pop(); if(a==0) { window_puts(2, "Divisione per 0.\n");break;} answer = b/a;push(b/a);break;

Page 249: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 248 di 299

default:push(atoi(in));continue;} sprintf(out, "%d", answer);window_puts(2, out); } while(*in);deactivate(2); } /*Mette un numero nello stack. Restituisce 1 se tutto e’ andato per il meglio altrimenti restituisce 0, ovvero nel caso di stack pieno. */ int push(int i) {if(p>bos) return 0;*p=i;p++;return 1;} /*Riprende l’elemento dalla cima dello stack. Restituisce 0 se lo stack non dispone di alcun elemento. */ int pop(void) { p--;if(p<tos) {p++;return 0;} return *p;} /* Convertitore dal formato decimale al formato esadecimale: attiva la propria finestra e poi entra in un ciclo, nel quale e’ prevista l’attesa del valore inserito dall’utente e la visualizzazione del valore convertito. Il ciclo viene interrotto premendo CR, in presenza del prompt che richiede l’inserimento del numero decimale, la finestra viene disattivata. */ void dectohex(void) { char in[80], out[80]; int n; window(1); do { window_xy(1, 0, 0); /* Va alla prima linea. */ window_cleol(1); /* Cancella la linea. */ window_puts(1, "dec: "); /* Prompt del convertitore. */ window_gets(1, in); /* Legge il numero. */ window_putchar(1, ‘\n’); /* Va alla linea successiva. */ window_cleol(1); /* La cancella. */ sscanf(in,"%d", &n); /* Converte nel formato interno. */ sprintf(out, "%s%X", "hex: ",n); /* Converte in esadecimale. */ window_puts(1, out); /* Visualizza il valore esadecimale. */ } while(*in); deactivate(1); } /*Blocco appunti pop-up: consente l’inserimento di un testo fino a 10 linee. F1 esce dal notepad, F2 cancella l’intero blocco. */ void notepad(void) { static first=1; register int i, j, k; union inkey { char ch[2]; int i; } c; char ch; /* Inizializza la matrice degli appunti, se necessario. */ if(first) {for(i=0; i<MAX_NOTE; i++) *notes[i] = ‘\0’;first = !first;} window(3); /* Visualizza gli appunti esistenti. */ for(i=0; i<MAX_NOTE; i++) { if(*notes[i]) window_puts(3, notes[i]);window_putchar(3, ‘\n’);} i=0; window_xy(3, 0, 0); for(;;) { c.i = readkey(); /* Legge il tasto. */ if(tolower(c.ch[1])==59) { /* F1 per uscire. */ deactivate(3); break; } /* Se si tratta di un tasto normale. */ if(isprint(c.ch[0]) || c.ch[0]==‘\b’) { window_cleol(3);notes[i][0]=c.ch[0];j=1;window_putchar(3, notes[i][0]); do {ch = window_getche(3); if(ch==‘\b’) { if(j>0) {j--;window_bksp(3);}

Page 250: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 249 di 299

} else {notes[i][j] = ch;j++;} } while(notes[i][j-1]!=‘\r’); notes[i][j-1] = ‘\0’;if(i<MAX_NOTE-1) i++;window_putchar(3, ‘\n’); } else { /* Se si tratta di un tasto speciale. */ switch(c.ch[1]) { case 72: /* Freccia in su. */ if(i>0) {i--;window_upline(3);} break; case 80: /* Freccia in giu’. */ if(i<MAX_NOTE-1) {i++;window_downline(3);} break; case 60: /* Cancella il blocco di appunti. */ window_cls(3);for(k=0; k<MAX_NOTE; k++) *notes[k] = ‘\0’; window_xy(3, 0, 0); } } } }

GESTIONE MENU La differenza sostanziale che caratterizza il funzionamento di un menu standard generico, rispetto ai menu pop-up o pull-down è che la sua attivazione comporta l’arresto temporaneo dell’applicazione. Un menu pop-up o pull-down, invece, appare sospendendo solo temporaneamente l’attività dell’applicazione. Dal punto di vista dell’utente, un menu standard può causare una netta interruzione della concentrazione, mentre un menu pop-up o pull-down non la distoglie. Pop-up Sullo schermo ne può apparire uno solo per volta, è utilizzato quando il menu richiede un solo livello di profondità, in altre parole quando la selezione di una voce di menu non comporta ulteriori selezioni; il menu pop-up è un menu pull-down che non possiede sottomenu. Le routine di gestione dei menu pop-up operanti in modo testo hanno un vantaggio, con l’impiego delle routine video di servizio del BIOS si ha la piena compatibilità su tutti i sistemi anche se funzionanti con un diverso H/W per la gestione dello schermo. Lo svantaggio è che la procedura di attivazione risulta lenta, l’unico modo per risolvere tale difetto è di leggere e scrivere i dati dello schermo accedendo direttamente alla VRAM (Video RAM), tramite l’uso di puntatori far.

Page 251: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 250 di 299

File POPUP_B.CPP #include <iostream.h> #include <dos.h> #include <stdio.h> #include <stdlib.h> #include <bios.h> #include <string.h> #include <ctype.h> const int BORDER=1; const int ESC=27; const int REV_VID=0x70; const int NORM_VID=7; int popup(char *menu[], char *keys, int count,int x, int y, int border); void save_video(int startx, int endx, int starty, int endy, unsigned int *buf_ptr); void restore_video(int startx, int endx, int starty, int endy, unsigned char *buf_ptr); void goto_xy(int x, int y), cls(void); void write_video(int x, int y, char *p, int attrib); void display_menu(char *menu[], int x, int y, int count); void draw_border(int startx, int starty, int endx, int endy); int get_resp(int x, int y, int count, char *menu[], char *keys); int readkey(void); char *fruit[]= {"Mela","Arancia","Pera","Uva","Banana","Ciliegia"}; char *color[]= {"Gialla","Rossa","Arancione","Verde",}; char *apple_type[]= {"Renetta","Golden","Cotogna","Val di Non"}; int main(void) { int i;cls();goto_xy(0, 0); for(i=0; i<25; i++) cout<<"Questo e’ un test delle routine di finestra pop-up.\n"; popup(fruit, "mapubc", 6, 3, 1, BORDER); popup(color, "grav", 4, 10, 5, BORDER); popup(apple_type, "rgcv", 4, 18, 10, BORDER);return (0); } /*Le informazioni che devono essere passate ad una funzione capace di creare un menu pop-up sono: - l’elenco delle voci di menu, ovvero delle stringhe che devono essere visualizzate (il miglior modo e’ di inserirle in una matrice in modo tale da passarne solo il pointer); - i tasti di selezione mediante una stringa contenente i caratteri chiave con lo stesso ordine delle voci di menu; - il numero delle voci di menu; - le coordinate dello schermo che dovranno corrispondere all’angolo in alto a sinistra del menu pop-up; - visualizzare un bordo intorno al menu. popup() visualizza un menu e restituisce la selezione: utilizza le routine di allocazione dinamica per la memorizzazione temporanea di dati presenti sullo schermo. Questa funzione restituisce: - (-2) se il menu non puo’ essere disegnato, eccede lo spazio disponibile; - (-1) se l’utente preme il tasto ESC; - 0..count-1 indica il numero della voce selezionata, con 0 corrispondente alla prima voce (quella piu’ in alto). La popup() deve effettuare le seguenti operazioni:

Page 252: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 251 di 299

- memorizzare in un buffer la porzione di schermo usata dal menu; - visualizzare il bordo, se richiesto; - visualizzare il menu; - attendere il responso dell’utente; - ripristinare i contenuti dello schermo alle condizioni originali. */ int popup( char *menu[], /* Testo del menu. */ char *keys, /* Tasti chiave. */ int count, /* Numero delle voci di menu. */ int x, int y, /* Coordinate X,Y dell’angolo in alto a sinistra. */ int border /* Quando vale 0 non viene disegnato il bordo. */ ) { register int i, len;int endx, endy, choice;unsigned int *p; if ((y>24) || (y<0) || (x>79) || (x<0)) { cout<<"Errore dimensionale.";return -2;} /* Si assicura che il menu stia nello schermo. */ len = 0; for (i=0; i<count; i++) if(strlen(menu[i]) > len) len = strlen(menu[i]); endx = len + 2 + x;endy = count + 1 + y; if ((endy+1>24) || (endx+1>79)) { cout<<"Il menu non ci sta.";return -2;} /* Alloca memoria sufficiente affinche’ si possa memorizzare il contenuto dello schermo. */ p = (unsigned int *) malloc((endx-x+1) * (endy-y+1)); if(!p) exit(1); /* Inserite qui la vostra gestione errori. */ /* Memorizza quanto appare sullo schermo. */ save_video(x, endx+1, y, endy+1, p); if(border) draw_border(x, y, endx, endy); /* Visualizza il menu. */ display_menu(menu, x+1, y+1, count); /* Attende il responso dell’utente. */ choice = get_resp(x, y, count, menu, keys); /* Ripristina lo schermo iniziale. */ restore_video(x, endx+1, y, endy+1, (char *) p); free(p); return choice; } /*Il dato principale di popup() che permette la visualizzazione dei menu, e’ rappresentato dal pointer alla matrice di pointer a stringhe. Per mostrare ciascuna stringa, si deve indicizzare il pointer come una matrice: ogni dato della matrice punta alla stringa corrispondente alla voce di menu. La display_menu() visualizza il menu nell’apposita locazione. */ void display_menu(char *menu[], int x, int y, int count) { register int i; for(i=0; i<count; i++, y++) {goto_xy(x, y);cout<<menu[i];} } /* Disegna un bordo intorno al menu dalle coordinate relative all’angolo in alto a sinistra fino all’angolo in basso a destra. */ void draw_border(int startx, int starty, int endx, int endy) { register int i; /* Disegna le linee verticali. */ for (i=starty+1; i<endy; i++) { goto_xy(startx, i);putchar(179); goto_xy(endx, i);putchar(179);}

Page 253: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 252 di 299

/* Disegna le linee orizzontali. */ for (i=startx+1; i<endx; i++) { goto_xy(i, starty);putchar(196); goto_xy(i, endy);putchar(196);} /* Disegna gli angoli. */ goto_xy(startx, starty); putchar(218); goto_xy(startx, endy); putchar(192); goto_xy(endx, starty); putchar(191); goto_xy(endx, endy); putchar(217); } /* Inserimento della selezione dell’utente in due modi: - evidenziazione della voce desiderata con i tasti freccia ed invio, anche la barra spaziatrice puo’ essere utilizzata; - mediante la pressione del tasto chiave associato alla voce desiderata. Dopo aver evidenziato la prima voce di menu, la funzione entra in un ciclo che attende il responso dell’utente ed usa readkey() per leggere il tasto inserito. */ get_resp(int x, int y, int count, char *menu[], char *keys) { union inkey { char ch[2]; int i; } c; int arrow_choice=0;char *key_choice; x++; y++; /* Evidenzia la prima voce. */ goto_xy(x, y); write_video(x, y, menu[0], REV_VID); /* Negativo. */ for (;;) { c.i = readkey(); /* Legge il tasto. */ /* Riporta a normale l’attributo del video. */ goto_xy(x, y+arrow_choice); /* Rivisualizza. */ write_video(x, y+arrow_choice,menu[arrow_choice], NORM_VID); if (c.ch[0]) { /* E’ un tasto normale. */ /* Controlla se si tratta di un tasto chiave. */ key_choice = strchr(keys, tolower(c.ch[0])); if(key_choice) return key_choice-keys; /* Controlla se si tratta di INVIO o della barra spaziatrice. */ switch(c.ch[0]) { case ‘\r’: return arrow_choice; case ‘ ‘ : arrow_choice++;break; case ESC : return -1; } } else { /* E’ un tasto speciale. */ switch(c.ch[1]) { case 72: arrow_choice--; /* Tasto cursore freccia su. */ break; case 80: arrow_choice++; /* Tasto cursore freccia giu’. */ break; } } if(arrow_choice==count) arrow_choice = 0; if(arrow_choice<0) arrow_choice = count-1; /* Evidenzia la voce successiva. */ goto_xy(x, y+arrow_choice); write_video(x, y+arrow_choice, menu[arrow_choice], REV_VID); } } /* Utilizzata da get_esp() per scrivere una stringa sullo schermo nella posizione X, Y usando l’attributo specificato. */ void write_video(int x, int y, char *p, int attrib)

Page 254: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 253 di 299

{ union REGS r;register int i; for(i=x; *p; i++) { goto_xy(i, y); r.h.ah = 9; /* Scrive il carattere. */ r.h.bh = 0; /* La pagina video attiva e’ la numero 0. */ r.x.cx = 1; /* Numero di ripetizioni del carattere. */ r.h.al = *p++; /* Carattere. */ r.h.bl = attrib; /* Attributo. */ int86(0x10, &r, &r);} } /* Legge una porzione dello schermo, memorizza le informazioni in un buffer e cancella sullo schermo la stessa porzione appena memorizzata. I primi quattro parametri specificano le coordinate della porzione di schermo, buf_ptr punta alla regione di memoria preposta a contenere i dati correnti dello schermo.*/ void save_video(int startx, int endx, int starty, int endy, unsigned int *buf_ptr) { union REGS r; register int i,j; for(i=startx; i<endx; i++) for(j=starty; j<endy; j++) { goto_xy(i, j); r.h.ah = 8;/*Funzione 8 legge un carattere da una locazione specifica*/ r.h.bh = 0; /* BH=pagina video attiva e’ la numero 0. */ *buf_ptr++ = int86(0x10, &r, &r); /* Memorizza nel buffer, e ritorna /* in AL=carattere e in AH=attributo. */ putchar(‘ ‘);} /* Cancella il carattere sullo schermo. */ } /* Trasferisce i dati dal buffer puntato da buf_ptr direttamente sullo schermo ma solo dopo aver ricevuto le coordinate X,Y. */ void restore_video(int startx, int endx, int starty, int endy, unsigned char *buf_ptr) { union REGS r; register int i, j; for(i=startx; i<endx; i++) for(j=starty; j<endy; j++) { goto_xy(i, j); r.h.ah = 9; /* Funzione 9 scrive il carattere. */ r.h.bh = 0; /* BH=pagina video attiva e’ la numero 0. */ r.x.cx = 1; /* CX=numero di ripetizioni del carattere. */ r.h.al = *buf_ptr++; /* AL=carattere. */ r.h.bl = *buf_ptr++; /* BL=attributo. */ int86(0x10, &r, &r);} } /* Cancella lo schermo. */ void cls(void) { union REGS r; r.h.ah = 6; /* Codice di scorrimento dello schermo. */ r.h.al = 0; /* Cancella il codice dello schermo. */ r.h.ch = 0; /* Riga di partenza. */ r.h.cl = 0; /* Colonna di partenza. */ r.h.dh = 24; /* Riga finale. */ r.h.dl = 79; /* Colonna finale. */ r.h.bh = 7; /* La linea vuota e’ nera. */ int86(0x10, &r, &r); } /* Posiziona il cursore alle coordinate video specificate. */

Page 255: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 254 di 299

void goto_xy(int x, int y) { union REGS r; r.h.ah = 2; /* Funzione 2 di indirizzamento del cursore. */ r.h.dl = x; /* DL=coordinata colonna. */ r.h.dh = y; /* DH=coordinata riga. */ r.h.bh = 0; /* BH=pagina video. */ int86(0x10, &r, &r); } readkey(void) { union REGS r; r.h.ah = 0; /* Funzione 0, restituisce il codice di scansione a 16 bit generato dalla tastiera quando un tasto viene premuto. Se il tasto premuto e’ di tipo carattere, il carattere stesso viene restituito nel byte basso. Se il tasto premuto e’ un tasto speciale, il byte basso e’ posto a 0 e il byte alto contiene il codice di posizione del tasto premuto. */ return int86(0x16, &r, &r); }

File POPUP_V.CPP Esempio di routine di gestione dei menu pop-up operanti in modo testo, utilizzanti l’accesso diretto alla VRAM: si ottengono funzioni estremamente veloci ed efficienti. #include <iostream.h> #include <dos.h> #include <stdlib.h> #include <ctype.h> #include <bios.h> #include <string.h> const int BORDER=1; const int ESC=27; const int REV_VID=0x70; const int NORM_VID=7; int popup(char *menu[], char *keys, int count,int x, int y, int border); void save_video(int startx,int endx,int starty,int endy, unsigned char *buf_ptr); void restore_video(int startx, int endx, int starty, int endy, unsigned char *buf_ptr); void goto_xy(int x, int y), cls(void); void write_video(int x, int y, char *p, int attrib); void display_menu(char *menu[], int x, int y, int count);

Page 256: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 255 di 299

void draw_border(int startx, int starty, int endx, int endy); void write_string(int x, int y, char *p, int attrib); void write_char(int x, int y, char ch, int attrib); int get_resp(int x, int y, int count, char *menu[], char *keys); int video_mode(void),readkey(void); char far *vid_mem; char *fruit[]= {"Mela","Arancia","Pera","Uva","Banana","Ciliegia"}; char *color[]= {"Gialla","Rossa","Arancione","Verde",}; char *apple_type[]= {"Renetta","Golden","Cotogna","Val di Non"}; int main(void) { int i; cls();goto_xy(0, 0); for(i=0; i<25; i++) cout<<"Questo e’ un test delle routine di finestra pop-up.\n"; popup(fruit, "mapubc", 6, 3, 1, BORDER); popup(color, "grav", 4, 10, 5, BORDER); popup(apple_type, "rgcv", 4, 18, 10, BORDER);return 0; } int popup(char *menu[],char *keys,int count,int x, int y,int border) { register int i, len;int endx, endy, choice, vmode;unsigned char *p; if((y>24) || (y<0) || (x>79) || (x<0)) { cout<<"Errore dimensionale."; return -2;} /*Controlla il tipo d’interfaccia per predisporre in modo appropriato le variabili che puntano alla VRAM. */ vmode = video_mode(); if((vmode!=2) && (vmode!=3) && (vmode!=7)) { cout<<"Il modo video dev’essere di tipo testo a 80 colonne.";exit(1);} /* Imposta l’indirizzo appropriato per la RAM video. */ if(vmode==7) vid_mem = (char far *) 0xB0000000; else vid_mem = (char far *) 0xB8000000; /* Si assicura che il menu stia nello schermo. */ len = 0; for(i=0; i<count; i++) if(strlen(menu[i]) > len) len = strlen(menu[i]); endx = len + 2 + x;endy = count + 1 + y; if((endy+1>24) || (endx+1>79)) {cout<<"Il menu non ci sta.";return -2;} p = (unsigned char *) malloc(2 * (endx-x+1) * (endy-y+1)); if(!p) exit(1); /* Inserite qui la vostra gestione degli errori. */ save_video(x, endx+1, y, endy+1, p); if(border) draw_border(x, y, endx, endy); display_menu(menu, x+1, y+1, count); choice = get_resp(x, y, count, menu, keys); restore_video(x, endx+1, y, endy+1, p);free(p);return choice; } void display_menu(char *menu[], int x, int y, int count) { register int i; for(i=0; i<count; i++, y++) write_string(x, y, menu[i], NORM_VID); } void draw_border(int startx, int starty, int endx, int endy) { register int i;char far *v, far *t;v = vid_mem;t = v; for(i=starty+1; i<endy; i++) { v += (i*160) + startx*2;*v++ = 179;*v = NORM_VID;v = t; v += (i*160) + endx*2;*v++ = 179;*v = NORM_VID;v = t;} for(i=startx+1; i<endx; i++) { v += (starty*160) + i*2;*v++ = 196;*v = NORM_VID;v = t;

Page 257: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 256 di 299

v += (endy*160) + i*2;*v++ = 196;*v = NORM_VID;v = t;} write_char(startx, starty, (char) 218, NORM_VID); write_char(startx, endy, (char) 192, NORM_VID); write_char(endx, starty, (char) 191, NORM_VID); write_char(endx, endy, (char) 217, NORM_VID); } get_resp(int x, int y, int count, char *menu[], char *keys) { union inkey { char ch[2]; int i; } c; int arrow_choice=0;char *key_choice; x++; y++; goto_xy(x, y); write_string(x, y, menu[0], REV_VID); for(;;) { c.i = readkey();goto_xy(x, y+arrow_choice); write_string(x, y+arrow_choice, menu[arrow_choice], NORM_VID); /* Rivisualizza. */ if(c.ch[0]) { key_choice = strchr(keys, tolower(c.ch[0])); if(key_choice) return key_choice-keys; switch(c.ch[0]) { case ‘\r’: return arrow_choice; case ‘ ‘ : arrow_choice++; break; case ESC : return -1; } } else { switch(c.ch[1]) { case 72: arrow_choice--;break; case 80: arrow_choice++;break; } } if(arrow_choice==count) arrow_choice=0; if(arrow_choice<0) arrow_choice = count-1; goto_xy(x, y+arrow_choice); write_string(x, y+arrow_choice, menu[arrow_choice], REV_VID); } } /* Visualizza una stringa con l’attributo specificato. */ void write_string(int x, int y, char *p, int attrib) { register int i; char far *v; v = vid_mem; v += (y*160) + x*2; /* Calcola l’indirizzo. */ for(i=y; *p; i++) { *v++ = *p++; /* Scrive il carattere. */ *v++ = attrib;} /* Scrive l’attributo. */ } /* Scrive un carattere con lo specifico attributo direttamente nello schermo alla posizione specificata. */ void write_char(int x, int y, char ch, int attrib) { char far *v; v = vid_mem; v += (y*160) + x*2; /* Calcola l’indirizzo. */ *v++ = ch; /* Scrive il carattere. */ *v = attrib; /* Scrive l’attributo. */ } /*Il byte di carattere precede il byte di attributo: per ogni riga dello schermo sono necessari 160 byte. Per calcolare la posizione specifica di un

Page 258: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 257 di 299

carattere si usa la formula: indirizzo=indirizzo_interfaccia+y*160+x*2 */ void save_video(int startx,int endx,int starty,int endy, unsigned char *buf_ptr) { register int i,j;char far *v, far *t;v = vid_mem; for(i=startx; i<endx; i++) for(j=starty; j<endy; j++) { t = v + (j*160) + i*2; /* Calcola l’indirizzo. */ *buf_ptr++ = *t++; /* Legge il carattere. */ *buf_ptr++ = *t; /* Legge l’attributo. */ *(t-1) = ‘ ‘;} /* Cancella la finestra. */ } /* Ripristina una porzione dello schermo utilizzando l’accesso diretto alla RAM video. */ void restore_video(int startx, int endx, int starty, int endy, unsigned char *buf_ptr) { register int i,j;char far *v, far *t;v = vid_mem;t = v; for(i=startx; i<endx; i++) for(j=starty; j<endy; j++) { v = t; v += (j*160) + i*2; /* Calcola l’indirizzo. */ *v++ = *buf_ptr++; /* Scrive il carattere. */ *v = *buf_ptr++;} /* Scrive l’attributo. */ } void cls(void) { union REGS r;r.h.ah = 6;r.h.al = 0;r.h.ch = 0;r.h.cl = 0; r.h.dh = 24;r.h.dl = 79;r.h.bh = 7;int86(0x10, &r, &r); } void goto_xy(int x, int y) {union REGS r;r.h.ah=2;r.h.dl=x;r.h.dh=y;r.h.bh=0;int86(0x10, &r, &r);} /* Restituisce il modo video correntemente usato. */ video_mode(void) { union REGS r; r.h.ah = 15; /* Funzione 15 prende il modo video. */ return int86(0x10, &r, &r) & 255; } readkey(void) {union REGS r;r.h.ah = 0;return int86(0x16, &r, &r);}

Pull-down Possono apparire contemporaneamente sullo schermo molteplici menu, sono impiegati quando una voce di menu necessita l’apertura di un sottomenu per la selezione di ulteriori opzioni. #include <iostream.h> #include <dos.h> #include <stdlib.h>

Page 259: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 258 di 299

#include <bios.h> #include <ctype.h> #include <string.h> #define BORDER 1 #define ESC 27 #define MAX_FRAME 10 /* Numero dei menu di cui possiamo disporre. */ #define REV_VID 0x70 #define NORM_VID 7 void save_video(int num),restore_video(int num); void goto_xy(int x, int y), cls(void); void write_video(int x, int y, char *p, int attrib),display_menu(int num); void write_string(int x, int y, char *p, int attrib); void write_char(int x, int y, char ch, int attrib),pd_driver(void); int make_menu(int num,char *menu[],char *keys,int count, int x,int y,int border); int get_resp(int num), pulldown(int num),video_mode(void); void display_menu(int num), draw_border(int num); char far *vid_mem;int readkey(void); /*Struttura di dati del menu. Per ciascun menu e’ definita una propria struttura di dati di riferimento. Ciascun menu viene attivato dal numero associato alla propria struttura di dati e l’informazione necessaria viene caricata al momento del bisogno dalle function di supporto ai menu.*/ struct menu_frame { int startx, endx, starty, endy; unsigned char *p; /* Pointer alle informazioni di schermo. */ char **menu; /* " " stringhe del menu. */ char *keys; /* " ai tasti chiave. */ int border; /* Stato del bordo: acceso/spento. */ int count; /* Numero di selezioni. */ int active; /* Indicatore dello stato di attivazione. Segnala quando un menu e’ gia’ presente sullo schermo, in modo tale da prevenire la sovrascrittura. */ } frame[MAX_FRAME], i; char *fruit[]= { "Mela","Arancia","Pera","Uva","Banana","Ciliegia"}; char *color[]= { "Gialla","Rossa","Arancione","Verde",}; char *apple_type[]= { "Renetta","Golden","Cotogna","Val di Non"}; char *grape_type[]= { "Regina","Italia","Americana","Moscato"}; int main(void) { cls(); goto_xy(0, 0); /* Per prima cosa crea i menu. */ make_menu(0, fruit, "mapubc", 6, 20, 5, BORDER); make_menu(1, color, "grav", 4, 28, 9, BORDER); make_menu(2, apple_type, "rgcv", 4, 32, 12, BORDER); make_menu(3, grape_type, "riam", 4, 10, 9, BORDER); cout<<"Scegliere un frutto:"; pd_driver(); /* Attiva il sistema di menu. */ return 0; } /* Dimostra le funzioni di menu pull-down. */ void pd_driver(void) { int choice1, choice2, selection; /* Ora abilita quanto richiesto. */ while((choice1=pulldown(0)) != -1) { switch(choice1) { case 0: /* Vuole una mela. */

Page 260: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 259 di 299

while((choice2=pulldown(1)) != -1) { if(choice2==0) { selection = pulldown(2); /* Mela gialla. */ restore_video(2); }} restore_video(1);break; case 1: case 2: goto_xy(0, 1); cout<<"Selezione non attiva.";break; case 3: /* Vuole l’uva. */ selection = pulldown(3); restore_video(3);break; case 4: case 5: goto_xy(0, 1); cout<<"Selezione non attiva.";break; } } restore_video(0); } /*Compiti: salva lo schermo, visualizza il menu, restituisce la selezione dell’utente. Accede alle informazioni relative ai vari menu solo se gli stessi risulteranno attivati. La porzione di schermo usata dal menu viene memorizzata solo se il flag active=0. Poiche’ un menu pull-down dev’essere in grado di essere richiuso, e’ necessario evitare che lo schermo venga memorizzato molteplici volte. Altrimenti verrebbe memorizzata anche la figura del menu principale, cancellando cosi’ il vero contenuto dello schermo, precedentemente memorizzato. */ int pulldown(int num) { int vmode;vmode = video_mode(); if((vmode!=2) && (vmode!=3) && (vmode!=7)) { cout<<"Il modo video dev’essere di tipo testo a 80 colonne.";exit(1);} /* Imposta l’indirizzo appropriato per la RAM video. */ if(vmode==7) vid_mem = (char far *) 0xB0000000; else vid_mem = (char far *) 0xB8000000; /* Determina la finestra attiva. */ if(!frame[num].active) { /* Non correntemente in uso. */ save_video(num); /* Memorizza lo schermo corrente. */ frame[num].active = 1;} /* Imposta il flag attivo. */ if(frame[num].border) draw_border(num); display_menu(num); /* Visualizza il menu. */ return get_resp(num); /* Restituisce il responso. */ } /* Costruisce la struttura dei dati di un menu pull-down. Restituisce 1 se la struttura pu• essere costruita, altrimenti restituisce 0. */ make_menu( int num, /* Numero del menu. */ char *menu[], /* Testo del menu. */ char *keys, /* Tasti chiave. */ int count, /* Numero delle voci di menu. */ int x, int y, /* Coordinate X,Y dell’angolo in alto a sinistra. */ int border /* Quando vale 0 non viene disegnato il bordo. */ ) { register int i, len;int endx, endy;unsigned char *p; if(num>MAX_FRAME) {cout<<"Troppi menu.\n";return 0;} if((y>24) || (y<0) || (x>79) || (x<0)) { cout<<"Errore dimensionale.";return 0;}

Page 261: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 260 di 299

/* Calcola la dimensione. */ len = 0; for(i=0; i<count; i++) if(strlen(menu[i]) > len) len = strlen(menu[i]); endx = len + 2 + x;endy = count + 1 + y; if((endy+1>24) || (endx+1>79)) {cout<<"Il menu non ci sta.";return 0;} /* Alloca memoria sufficiente affinche’ si possa memorizzare il contenuto corrente dello schermo. */ p = (unsigned char *) malloc(2 * (endx-x+1) * (endy-y+1)); if(!p) exit(1); /* Inserite qui la vostra gestione degli errori. */ /* Costruisce la struttura. */ frame[num].startx = x; frame[num].endx = endx; frame[num].starty = y; frame[num].endy = endy; frame[num].p = p;frame[num].menu = (char **) menu; frame[num].border = border;frame[num].keys = keys; frame[num].count = count;frame[num].active = 0;return 1; } /* Visualizza il menu nell’apposita locazione. */ void display_menu(int num) { register int i, y; char **m; y = frame[num].starty+1;m = frame[num].menu; for(i=0; i<frame[num].count; i++, y++) write_string(frame[num].startx+1, y, m[i], NORM_VID); } /* Disegna un bordo intorno al menu. */ void draw_border(int num) { register int i;char far *v, far *t;v = vid_mem;t = v; /* Disegna le linee verticali. */ for(i=frame[num].starty+1; i<frame[num].endy; i++) { v += (i*160) + frame[num].startx*2;*v++ = 179;*v = NORM_VID; v = t;v += (i*160) + frame[num].endx*2; *v++ = 179;*v = NORM_VID;v = t; } /* Disegna le linee orizzontali. */ for(i=frame[num].startx+1; i<frame[num].endx; i++) { v += (frame[num].starty*160) + i*2; *v++ = 196;*v = NORM_VID;v = t; v += (frame[num].endy*160) + i*2;*v++ = 196;*v = NORM_VID;v = t;} /* Disegna gli angoli. */ write_char(frame[num].startx, frame[num].starty,(char) 218, NORM_VID); write_char(frame[num].startx, frame[num].endy,(char) 192, NORM_VID); write_char(frame[num].endx, frame[num].starty,(char) 191, NORM_VID); write_char(frame[num].endx, frame[num].endy,(char) 217, NORM_VID); } /* Inserimento della selezione dell’utente. */ get_resp(int num) { union inkey { char ch[2]; int i; } c; int arrow_choice=0;char *key_choice;int x, y; x = frame[num].startx+1;y = frame[num].starty+1; /* Evidenzia la prima voce. */ goto_xy(x, y); write_string(x, y, frame[num].menu[0], REV_VID);

Page 262: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 261 di 299

for(;;) { c.i = readkey(); /* Legge il tasto. */ /* Riporta a normale l’attributo del video. */ goto_xy(x, y+arrow_choice); write_string(x, y+arrow_choice, frame[num].menu[arrow_choice], NORM_VID); /* Rivisualizza. */ if(c.ch[0]) { /* E’ un tasto normale. */ /* Controlla se si tratta di un tasto chiave. */ key_choice = strchr(frame[num].keys, tolower(c.ch[0])); if(key_choice) return key_choice-frame[num].keys; /* Controlla se si tratta di INVIO o della barra spaziatrice. */ switch(c.ch[0]) { case ‘\r’: return arrow_choice; case ‘ ‘ : arrow_choice++; break; case ESC : return -1; /* Annulla. */ } } else { /* E’ un tasto speciale. */ switch(c.ch[1]) { case 72: arrow_choice--; /* Tasto cursore freccia su. */ break; case 80: arrow_choice++; /* Tasto cursore freccia giu’. */ break; } } if(arrow_choice==frame[num].count) arrow_choice=0; if(arrow_choice<0) arrow_choice = frame[num].count-1; /* Evidenzia la voce successiva. */ goto_xy(x, y+arrow_choice); write_string(x, y+arrow_choice, frame[num].menu[arrow_choice], REV_VID); } } /* Visualizza una stringa con l’attributo specificato. */ void write_string(int x, int y, char *p, int attrib) { char far *v; v = vid_mem; v += (y*160) + x*2; /* Calcola l’indirizzo. */ while(*p) { *v++ = *p++; /* Scrive il carattere. */ *v++ = attrib;} /* Scrive l’attributo. */ } void write_char(int x, int y, char ch, int attrib) { char far *v;v = vid_mem; v += (y*160) + x*2; /* Calcola l’indirizzo. */ *v++ = ch; /* Scrive il carattere. */ *v = attrib; /* Scrive l’attributo. */ } void save_video(int num) { register int i,j;char *buf_ptr;char far *v, far *t; buf_ptr = frame[num].p;v = vid_mem; for(i=frame[num].startx; i<frame[num].endx+1; i++) for(j=frame[num].starty; j<frame[num].endy+1; j++) { t = (v + (j*160) + i*2);*buf_ptr++ = *t++;*buf_ptr++ = *t; *(t-1) = ‘ ‘;} /* Cancella la finestra. */ } /* La pulldown() non ripristina lo schermo per consentire la presenza contemporanea di molteplici menu: quindi dovremo ripristinare lo schermo in modo esplicito. */ void restore_video(int num) { register int i,j;char far *v, far *t;char *buf_ptr;

Page 263: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 262 di 299

buf_ptr = frame[num].p;v = vid_mem;t = v; for(i=frame[num].startx; i<frame[num].endx+1; i++) for(j=frame[num].starty; j<frame[num].endy+1; j++) { v = t; v += (j*160) + i*2; /* Calcola l’indirizzo. */ *v++ = *buf_ptr++; /* Scrive il carattere. */ *v = *buf_ptr++;} /* Scrive l’attributo. */ frame[num].active = 0; /* Lo disattiva. */ } void cls(void) { union REGS r;r.h.ah = 6;r.h.al = 0;r.h.ch = 0;r.h.cl = 0; r.h.dh = 24;r.h.dl = 79;r.h.bh = 7;int86(0x10, &r, &r); } void goto_xy(int x, int y) { union REGS r;r.h.ah=2;r.h.dl=x;r.h.dh=y;r.h.bh=0;int86(0x10, &r, &r);} video_mode(void) {union REGS r;r.h.ah = 15;return int86(0x10, &r, &r) & 255;} readkey(void) {union REGS r;r.h.ah = 0;return int86(0x16, &r, &r);}

VIDEOGIOCHI Un videogioco combina due tecniche. 1. Un’eccellente grafica animata. 2. Una logica complessa per la sfida, la strategia si basa su AI (Artificial Intelligence). Il videogioco consiste di due componenti principali. 1. L’ambiente, in pratica lo schema di gioco, consiste in un’immagine grafica all’interno

della quale si svolge l’azione di gioco, può essere statico o dinamico. 2. Gli sprite, sono piccoli oggetti animati che si muovono sullo schermo di gioco, la

dimensione dev’essere piccola in modo che possa essere tracciato velocemente. Per animare un oggetto bisogna cancellare la parte di schermo che visualizza correntemente l’oggetto e ridisegnare l’oggetto nella nuova posizione. Questo procedimento dev’essere eseguito velocemente, bisogna accedere alla VRAM e bypassare il BIOS. Il modo più veloce per visualizzare uno sprite è quello di visualizzarlo, cancellarlo e rivisualizzarlo mediante lo XOR di ogni suo punto sullo schermo. Così facendo, la prima volta che l’oggetto è tracciato sullo schermo risulta visualizzato, mentre la seconda volta che è scritto nella stessa locazione, il contenuto precedente dello schermo è ripristinato. In questo modo è possibile creare sprite che si muovono sull’intero schema di gioco senza che distruggano quanto già presente nell’immagine originale. Per organizzare i dati del videogioco, per esempio la posizione di un oggetto che si muove

Page 264: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 263 di 299

dev’essere memorizzata in un gruppo di variabili. Se necessitano informazioni relative allo schema bisogna accedere alla VRAM come se si trattasse di una grande matrice al fine di controllare il suo contenuto. Individuazione dei limiti del videogioco, lo sprite non può essere mosso attraverso oggetti dello schema di gioco. Ci sono due modi per imporre la restrizione dei movimenti di uno sprite. 1. Tenere un gruppo di variabili contenente tutti i punti rappresentanti i limiti e controllare le

coordinate al fine di rilevare i movimenti dell’oggetto non consentiti, questo però implica un notevole rallentamento del gioco.

2. Controllare la VRAM per determinare se la locazione nella quale dev’essere visualizzato l’oggetto contiene già un elemento grafico: vantaggioso.

I colori contano, per esempio il rosso per un limite invalicabile, il verde per lo sprite del giocatore e il giallo per lo sprite avversario: gli oggetti sono identificati dal proprio colore. Questo metodo facilita il codice e rende il gioco più veloce, per esempio, se l’azzurro indica una mina, per determinare se lo sprite del giocatore ha urtato una mina è sufficiente controllare se uno dei pixel in cui sarà posizionato lo sprite è azzurro. Segnapunti oppure sfidante? Se sono previsti due giocatori il PC assume il ruolo di gestore degli eventi e di segnapunti, invece nel caso di un singolo giocatore il PC partecipa in modo attivo: caso più interessante. Lo scopo del gioco è di riuscire a prendere l’avversario rincorrendolo all’interno dello schermo; i giocatori sono rappresentati da sprite raffiguranti un omino stilizzato, l’omino è di colore verde, mentre l’avversario, gestito dal PC, è giallo; muovendosi nello schermo non si possono oltrepassare gli ostacoli visualizzati in colore rosso. Affinché si blocchi il tempo per la rincorsa dell’omino avversario e quindi tocchi all’avversario rincorrerci, è necessario che gli omini si trovino sovrapposti con al massimo un pixel di scarto. All’inizio sarà l’omino del PC a cercare di prendere l’utente. Vince chi non si fa prendere per la maggior parte del tempo. Il punteggio è tenuto dal sistema di visualizzazione del tempo: per ogni secondo trascorso, il punteggio di chi scappa è incrementato di uno, il gioco termina quando un omino raggiunge i 999 secondi. L’applicazione lavora nel seguente modo. I tasti cursore, PGUP, PGDOWN, HOME, END controllano la posizione dello sprite, ogni volta che uno di questi tasti è premuto lo sprite si muove di un pixel nella direzione prescelta. Per qualsiasi evenienza il gioco può essere interrotto con il tasto (X). Il giocatore controlla lo sprite dell’omino mediante i tasti cursore. Lo schema di gioco non è creato dal gioco stesso ma può essere creato con un’applicazione di grafica e caricato all’inizio del gioco, in questo modo il giocatore può creare molti livelli di gioco. File GAME.C #include <dos.h> #include <stdio.h> #include <math.h> #include <time.h> #include <conio.h> #define XMAX 319 #define YMAX 199 #define COMPUTER 0 #define HUMAN 1 #define IDLE 0 #define DOWN 1 #define UP -1 #define LEFT -1

Page 265: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 264 di 299

#define RIGHT 1 void line(int startx, int starty, int endx, int endy,int color); void mempoint(int x, int y, int color),palette(int pnum); void mode(int mode_code),display_object(int ob[][5], int lines); void xhairs(int x, int y), goto_xy(int x, int y); void save_pic(void), load_pic(void); void it_comp_move(int ob1[][5], int ob2[][5], int human[][5],int sides); void not_it_comp_move(int ob1[][5],int ob2[][5],int directx,int directy,int sides); void show_score(int it, long htime, long ctime); void update_object(int ob[][5], int x, int y,register int sides); int readkey(void), kbhit(void),is_legal(int ob[][5],int x,int y,int sides); int tag(int ob1[][5], int ob2[][5]); int human[4][5] = { /* I vostri sprite:parte dall’angolo in alto a sinistra*/ 6, 1, 6, 6, 1, 2, 4, 9, 3, 1, 1, 9, 6, 6, 1, 11, 9, 6, 6, 1}; int human2[4][5] = { 6, 1, 6, 6, 1, 2, 4, 9, 3, 1, 3, 9, 6, 6, 1, 9, 9, 6, 6, 1}; int computer[4][5] = { /*Sprite del PC: parte dall’angolo in basso a sinistra*/ 6,180,6,185,3,2,183, 9, 182, 3, 1, 188, 6, 185, 3, 11, 188, 6, 185, 3}; int computer2[4][5] = { 6,180,6,185, 3, 2, 183, 9, 182, 3, 3, 188, 6, 185, 3, 9, 188, 6, 185, 3}; int directx, directy; /* Direzione del giocatore. */ /*gestisce il gioco: deve aggiornare continuamente il video, prestare attenzione ai tasti premuti, rilevare movimenti non consentiti, visualizzare il punteggio e calcolare le mosse del proprio personaggio. */ int main(void) { union k { /* Contiene il codice di scansione e il tasto. */ char c[2]; int i; } key; int deltax = 0, deltay = 0; /* Direzione del movimento. */ int swaph = 0, swapc = 0; /* Sceglie lo sprite. */ int it = COMPUTER; /* Chi deve iniziare a rincorrere. */ long htime, ctime, starttime, curtime; int count; /* Usato per permettere la separazione dall’avversario dopo che e’ stato preso. */ mode(4); /* Passa al modo video grafico 4. */ palette(0); /* Tavolozza 0. */ load_pic(); /* Carica lo schema di gioco. */ time(&starttime); /* Inizializza il tempo. */ htime = ctime = 0; /* Punti giocatore e punti PC */ display_object(human, 4);display_object(computer, 4);count = 0; /* Ciclo principale del gioco. */ do { /* Aggiorna i contatori del punteggio. */ time(&curtime); if(it==COMPUTER) htime += curtime-starttime; else ctime += curtime-starttime; time(&starttime);show_score(it, htime, ctime); if(kbhit()) { /* Se e’ stato premuto un tasto. */ directx = directy = IDLE; /* Reimposta la direzione ad ogni mossa. */ key.i = readkey(); /* Legge il tasto. */ deltax = 0; deltay = 0; if(!key.c[0]) switch(key.c[1]) { case 75: /* Sinistra. */ deltax = -1;directx = LEFT;break;

Page 266: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 265 di 299

case 77: /* Destra. */ deltax = 1;directx = RIGHT;break; case 72: /* Su. */ deltay = -1;directy = UP; break; case 80: /* Giu’. */ deltay = 1;directy = DOWN;break; case 71: /* Su a sinistra. */ deltax = -1; deltay = -1;directy = UP; directx = LEFT;break; case 73: /* Su a destra. */ deltay = -1; deltax = 1;directy = UP; directx = RIGHT;break; case 79: /* Giu’ a sinistra. */ deltay = 1; deltax = -1;directy = DOWN; directx = LEFT;break; case 81: /* Giu’ a destra. */ deltay = 1; deltax = 1;directy = DOWN; directx = RIGHT;break; } } /* Scambia l’omino del giocatore. */ if(swaph<5) display_object(human, 4);else display_object(human2, 4); /* Controlla se la mossa e’ consentita. */ if(is_legal(human, deltax, deltay, 4)) { update_object(human, deltax, deltay, 4); update_object(human2, deltax, deltay, 4);} /* Controlla se si e’ verificata una sovrapposizione degli omini. */ if(!count && tag(human, computer)) { it = !it; /* Inverte i ruoli degli omini. */ count = 6; /* Non consente la sovrapposizione immediata !!! */ } swaph++; if(swaph==10) swaph = 0; /* Visualizza l’omino del giocatore nella nuova posizione. */ if(swaph<5) display_object(human, 4);else display_object(human2, 4); /* Scambia l’omino del computer. */ if(swapc<5) display_object(computer,4);else display_object(computer2, 4); /* Calcola la mossa del computer. */ if(it==COMPUTER) it_comp_move(computer, computer2, human, 4); else not_it_comp_move(computer, computer2, directx, directy, 4); if(!count && tag(human, computer)) { it = !it; /* Inverte i ruoli degli omini. */ count = 6; /* Non consente la sovrapposizione immediata. */ /* Se e’ il computer a dover scappare, viene spostata la X di 2, cosicche’ non risulti facile la sua cattura. */ if(is_legal(computer, 2, 0, 4)) { update_object(computer, 2, 0, 4);update_object(computer2, 2, 0, 4); } else { update_object(computer,-2,0,4);update_object(computer2, -2, 0, 4);}} swapc++;if(swapc==10) swapc = 0; /* Visualizza l’omino del computer. */ if(swapc<5) display_object(computer,4);else display_object(computer2, 4); if(count) count--; } while (key.c[0]!=‘x’ && htime<999 && ctime<999); mode(3); if(ctime>htime) printf("Evviva, HO VINTO IO!"); else printf("Hai vinto tu!"); return 0; } /* Visualizza i punti. Quando viene scelto TU o IO, sono visualizzati i punteggi relativi. */ void show_score(int it, long htime, long ctime) { goto_xy(6, 24); if(it==COMPUTER) printf("TU:%ld", htime);else printf("TU:%ld", htime); goto_xy(26, 24);

Page 267: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 266 di 299

if(it==HUMAN) printf("IO:%ld", ctime);else printf("IO:%ld", ctime); } /* Imposta la tavolozza. */ void palette(int pnum) { union REGS r; r.h.bh = 1; /* Codice per la tavolozza scelta. */ r.h.bl = pnum; r.h.ah = 11; /* Funzione che imposta la tavolozza. */ int86(0x10, &r, &r); } /* Imposta il modo video. */ void mode(int mode_code) { union REGS r;r.h.al = mode_code;r.h.ah = 0;int86(0x10, &r, &r);} /* Disegna una linea con il colore specificato, mediante l’algoritmo di Bresenham basato sui valori interi. */ void line(int startx, int starty, /* Angolo in alto a sinistra. */ int endx, int endy, /* Angolo in basso a destra. */ int color) /* Colore usato. */ { register int t, distance;int x=0, y=0, delta_x, delta_y;int incx, incy; /* Calcola la distanza in entrambe le direzioni. */ delta_x = endx-startx; delta_y = endy-starty; /* Calcola la direzione dell’incremento. Un incremento pari a 0 indica che la linea e’ orizzontale o verticale. */ if(delta_x>0) incx = 1;else if(delta_x==0) incx = 0;else incx = -1; if(delta_y>0) incy = 1;else if(delta_y==0) incy = 0;else incy = -1; /* Determina la distanza maggiore. */ delta_x = abs(delta_x);delta_y = abs(delta_y); if(delta_x>delta_y) distance = delta_x;else distance = delta_y; /* Traccia la linea. */ for(t=0; t<=distance+1; t++) { mempoint(startx, starty, color); x += delta_x;y += delta_y; if(x>distance) {x -= distance;startx += incx;} if(y>distance) {y -= distance;starty += incy;} } } /* Scrive un punto direttamente nella RAM del modo video grafico 4. */ void mempoint(int x, int y, /* Coordinate del punto. */ int color) /* Colore usato. */ { union mask { char c[2]; int i; } bit_mask; int index, bit_position;unsigned char t; char xor; /* Xor colore o sovrascrittura. */ /* Puntatore alla RAM del modo video grafico 4. */ char far *ptr = (char far *) 0xB8000000; bit_mask.i = 0xFF3F; /* 11111111 00111111 in binario. */ /* Controlla i limiti nel modo video grafico 4. */ if(x<0 || x>XMAX || y<0 || y>YMAX) return; xor = color & 128; /* Controlla se e’ abilitato il modo xor. */ color = color & 127; /* Maschera il bit alto. */ /* Imposta bit_mask e i bit di colore nella giusta locazione. */ bit_position = x%4; color <<= 2*(3-bit_position);bit_mask.i >>= 2*bit_position;

Page 268: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 267 di 299

/* Cerca il byte corretto nella memoria dello schermo. */ index = y*40 + (x >> 2); if(y % 2) index += 8152; /* Se e’ dispari usa il secondo banco. */ /* Scrive il colore. */ if(!xor) { /* Modo sovrascrittura. */ t = *(ptr+index) & bit_mask.c[0];*(ptr+index) = t | color; } else { /* Modo xor. */ t = *(ptr+index) | (char) 0;*(ptr+index) = t ^ color; } } /* Visualizza il cursore a croce. */ void xhairs(int x, int y) {line(x-4, y, x+3, y, 1 | 128);line(x, y+4, x, y-3, 1 | 128);} /* Legge il byte direttamente dalla RAM del modo video grafico 4. */ unsigned char read_point(int x, int y) { union mask { char c[2]; int i; } bit_mask; int index, bit_position; unsigned char t; /* Puntatore alla RAM del modo video grafico 4. */ char far *ptr = (char far *) 0xB8000000; bit_mask.i = 3; /* 11111111 00111111 in binario. */ /* Controllo dei limiti nel modo video grafico 4. */ if(x<0 || x>XMAX || y<0 || y>YMAX) return 0; /* Imposta bit_mask e i bit di colore nella giusta locazione. */ bit_position = x%4; bit_mask.i <<= 2*(3-bit_position); /* Cerca il byte corretto nella memoria dello schermo. */ index = y*40 +(x >> 2); if(y % 2) index += 8152; /* Se e’ dispari usa il secondo banco. */ /* Legge il colore. */ t = *(ptr+index) & bit_mask.c[0];t >>= 2*(3-bit_position);return t; } /* Salva il video grafico. */ void save_pic(void) { char fname[80]; FILE *fp; register int i, j; int e=0; /* Puntatore alla RAM video del modo grafico 4. */ char far *ptr = (char far *) 0xB8000000; char far *temp; unsigned char buf[14][80]; /* Contiene i dati dello schermo. */ temp = ptr; /* Memorizza la parte alta dello schermo corrente. */ for(i=0; i<14; i++) for(j=0; j<80; j+=2) { buf[i][j] = *temp; /* Byte pari. */ buf[i][j+1] = *(temp+8152); /* Byte dispari. */ *temp = 0; *(temp+8152) = 0; /* Cancella la parte alta dello schermo. */ temp++; } goto_xy(0, 0); printf("Schema di gioco: "); gets(fname); if(!(fp=fopen(fname, "wb"))) { goto_xy(0, 0); printf("Non posso aprire il file. - Premi un tasto. ");getch(); e = 1; /* Flag di errore. */ } temp = ptr; /* Ripristina la parte alta dello schermo. */ for(i=0; i<14; i++)

Page 269: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 268 di 299

for(j=0; j<80; j+=2) { *temp = buf[i][j];*(temp+8152) = buf[i][j+1];temp++;} if(e) return; /* Se il file non puo’ essere aperto, allora esce. */ /* Salva l’immagine nel file su disco. */ for(i=0; i<8152; i++) { putc(*ptr, fp); /* Byte pari. */ putc(*(ptr+8152), fp); /* Byte dispari. */ ptr++; } fclose(fp); } /* Carica il video grafico. */ void load_pic(void) { char fname[80];FILE *fp;register int i, j; /* Puntatore alla RAM del modo video grafico 4. */ char far *ptr = (char far *) 0xB8000000; char far *temp; unsigned char buf[14][80]; /* Contiene i dati dello schermo. */ temp = ptr; /* Salva la parte alta dello schermo corrente.*/ for(i=0; i<14; i++) for(j=0; j<80; j+=2) { buf[i][j] = *temp;buf[i][j+1] = *(temp+8152); *temp = 0; *(temp+8152) = 0; /* Cancella la parte alta dello schermo. */ temp++; } goto_xy(0, 0);printf("Nome del file: ");gets(fname); if(!(fp=fopen(fname, "rb"))) { goto_xy(0, 0);printf("Non posso aprire il file.\n");temp = ptr; /* Ripristina la parte alta dello schermo. */ for(i=0; i<14; i++) for(j=0; j<80; j+=2) { *temp = buf[i][j];*(temp+8152) = buf[i][j+1]; temp++; } return;} /* Carica l’immagine dal file. */ for(i=0; i<8152; i++) { *ptr = getc(fp); /* Byte pari. */ *(ptr+8152) = getc(fp); /* Byte dispari. */ ptr++;} fclose(fp); } /* Posiziona il cursore alle coordinate X,Y specificate. */ void goto_xy(int x, int y) { union REGS r; r.h.ah = 2; /* Funzione di indirizzamento del cursore. */ r.h.dl = x; /* Coordinata colonna. */ r.h.dh = y; /* Coordinata riga. */ r.h.bh = 0; /* Pagina video. */ int86(0x10, &r, &r); } /* Visualizza un oggetto. */ void display_object(int ob[][5], int lines) { register int i; for(i=0; i<lines; i++) line(ob[i][0], ob[i][1],ob[i][2], ob[i][3], ob[i][4] | 128); } /* Aggiorna la posizione di un oggetto come specificato in x e y. */ void update_object(int ob[][5], /* Oggetto. */ int x, int y, /* Valore da aggiornare. */ register int sides) /* Numero di lati. */

Page 270: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 269 di 299

{ sides--; for( ; sides>=0; sides--) { ob[sides][0] += x;ob[sides][1] += y; ob[sides][2] += x;ob[sides][3] += y;} } /* Controlla se l’eventuale movimento e’ consentito. Restituisce 1 se e’ corretto, altrimenti restituisce 0. */ is_legal(int ob[][5], /* Oggetto. */ int x, int y, /* Incremento del movimento. */ int sides) /* Numero di lati che costituiscono l’oggetto. */ { if(x==0 && y==0) return 1;sides--; for( ; sides>=0; sides--) { /* Controlla se e’ fuori dai limiti. */ if(ob[sides][0]+x>XMAX || ob[sides][1]+y >YMAX) return 0; if(ob[sides][2]+x<0 || ob[sides][3]+y<0) return 0; /* Controlla la presenza di eventuali ostacoli. */ if(read_point(ob[sides][0]+x, ob[sides][1] + y)==2) return 0; if(read_point(ob[sides][2]+x, ob[sides][3] + y)==2) return 0;} return 1; } /* Calcola la mossa del computer quando deve rincorrere il giocatore. La strategia determina il movimento dello sprite in direzione dell’avversario. In caso di ostacoli li aggira. La function cambia la posizione dello sprite del PC solo per due terzi del tempo, questo e’ necessario per rallentare la velocita’ del PC in modo che il giocatore riesca a gareggiare. */ void it_comp_move(int ob1[][5], int ob2[][5], int human[][5], int sides) { register int x, y; static skip = 0; skip++; if(skip==3) { skip = 0;return;} x = 0; y = 0; /* Muove verso l’omino del giocatore. */ if(human[0][0]<ob1[0][0]) x = -1;else if(human[0][0]>ob1[0][0]) x = 1; if(human[0][1]<ob1[0][1]) y = -1;else if(human[0][1]>ob1[0][1]) y = 1; if(is_legal(ob1, x, y, sides)) { update_object(ob1, x, y, sides);update_object(ob2, x, y, sides); } else { /* Se non e’ corretto, prova a girargli attorno. */ if(x && is_legal(ob1, x, 0, sides)) { update_object(ob1, x, 0, sides);update_object(ob2, x, 0, sides); } else if(is_legal(ob1, 0, y, sides)) { update_object(ob1, 0, y, sides);update_object(ob2, 0, y, sides);} } } /* Calcola la mossa del computer quando deve scappare. La strategia e’ semplice: muove il proprio sprite nella direzione opposta in cui si muove l’avversario. La function e’ rallentata: viene seguita due volte su tre. */ void not_it_comp_move(int ob1[][5], int ob2[][5], int directx, int directy, /* Ultima mossa del giocatore. */ int sides) { register int x, y; static skip = 1; skip++; if(skip==3) { skip = 0;return; } x = 0; y = 0; /* Muove nella direzione opposta al giocatore. */ x = -directx; y = -directy; if(is_legal(ob1, x, y, sides)) { update_object(ob1, x, y, sides);update_object(ob2, x, y, sides); } else { /* Se non e’ corretta, prova a girargli attorno. */ if(x && is_legal(ob1, x, 0, sides)) { update_object(ob1, x, 0, sides);update_object(ob2, x, 0, sides); } else if(is_legal(ob1, 0, y, sides)) { update_object(ob1, 0, y, sides);update_object(ob2, 0, y, sides); } }

Page 271: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 270 di 299

} /* Controlla se e’ avvenuta una sovrapposizione: 1 per vero 0 per falso. */ tag(int ob1[][5], int ob2[][5]) { register int i; /* Per essere catturato, un omino dev’essere sovrapposto all’altro in modo coincidente o con un pixel di scarto. */ for(i=-1; i<2; i++) { if(ob1[0][0]==ob2[0][0]+i && ob1[0][1]==ob2[0][1]+i) { return 1; } } return 0; } /* Restituisce il codice a 16 bit di scansione della tastiera. */ readkey(void) { union REGS r;r.h.ah = 0;return int86(0x16, &r, &r);}

MICROSOFT DIRECTX È una collezione d’interfacce di programmazione API che permette la gestione d’informazioni multimediali e che facilita la programmazioni di giochi. Il nome di ciascuna API specifica, per ogni ambito d’impiego all’interno della libreria, comincia con la parola Direct, per esempio Direct3D, DirectShow, mentre l’aggiunta della lettera X identifica l’insieme di tutte le API. Le novità di DirectX 11, disponibili solo per Windows 7 e Vista, sono tre. 1. Un miglior supporto al multithread attraverso l’utilizzo delle display list. 2. L’introduzione di nuovi stadi nella pipeline dedicati alla tessellation. 3. Il supporto ai compute shader. I programmatori potranno utilizzare più thread in esecuzione sui core del processore per caricare ed elaborare in anticipo alcuni dati senza preoccuparsi della sincronizzazione con la GPU (Graphics Processing Unit). I singoli thread compileranno le display list all’interno di code di comando concorrenti, deferred contex che l’API provvede a sincronizzare autonomamente con quella principale, immediate contex che è interfacciata con il processore grafico. L’unità di tessellation permette di ottenere geometrie o superfici complesse aggiungendo punti di controllo a primitive più semplici attraverso algoritmi parametrici, ad esempio ottenere una sfera partendo da un cubo. Per fare questo le DirectX 11 aggiungono tre nuovi stadi della pipeline. 1. Hull shader. 2. Tessellation. 3. Domain shader. L’hull shader esegue codice scritto dal programmatore e calcola, sulla base dei nodi di controllo delle geometrie, i parametri necessari all’unità di tassellazione per generare i nuovi punti di controllo. I risultati ottenuti dall’unità di tassellazione sono quindi elaborati dal domain shader per generare i dati della nuova geometria. In questa fase il programmatore può istruire il domain shader all’utilizzo di texture di dislocazione, displace texture, per modificare la posizione dei vertici rispetto alla geometria originale. L’utilizzo di questa tecnologia permette di modificare gli oggetti per ottenere un maggiore livello di dettaglio solo dove è necessario. L’efficienza di questo strumento permetterà da un lato di ottenere una migliore qualità dei dettagli nell’immagine e fornirà maggiori risorse H/W a livello dei pixel shader in quanto sarà possibile ridurre l’utilizzo tecniche specifiche nel calcolo del colore dei pixel per simulare visivamente una geometria carente di dettagli.

Page 272: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 271 di 299

CHATLINE #include <bios.h> #include <dos.h> #include <stdio.h> #include <conio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #define PORT 1 #define ALT 0x08 void chatline(void),trasmissione(int porta, char car); int ricezione(int porta),check_stat(int porta); void visualizza_t(int car),visualizza_r(int car); union REGS reg; int main (void) { int c,car,esci = 0,key = 0; reg.h.ah = 0; reg.h.al = 0xE7; reg.x.dx = PORT; int86 (0x14, &reg, &reg); clrscr ();chatline (); do{ if (kbhit ()) { c = bioskey (0); key = bioskey (2); if (key) { if ((c & 255) != 27) { if ((c & 255) >= 32 || (c & 255) == 13 || (c & 255) == 8) { c = c & 255; visualizza_t (c); if ((check_stat(PORT) & 256) == 256) { car = ricezione (PORT); if (car != 0) visualizza_r (car); } else trasmissione (PORT, c); } else { if (key & ALT) { if (c == 5888) printf (""); else { if (c == 8448) printf (""); } } } } else esci = 1; }

Page 273: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 272 di 299

} if ((check_stat(PORT)&256) == 256) { car = ricezione (PORT); if (car != 0x00) visualizza_r (car); } } while (esci != 1); return(0); } void trasmissione (int porta, char car) { reg.h.ah = 0x01; reg.h.al = car; reg.x.dx = porta; int86 (0x14, &reg, &reg); while ((check_stat(porta)&16384)!=16384); } int ricezione (int porta) { reg.x.dx = porta; reg.h.ah = 2; int86 (0x14, &reg, &reg); if (reg.h.ah & 128) return 0; else return reg.h.al; } int check_stat (int porta) { reg.x.dx = porta; reg.h.ah = 3; int86 (0x14, &reg, &reg); return reg.x.ax; } void chatline (void) { int i; gotoxy (1,1); printf ("ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ TRASMISSIONE ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»"); for (i = 0; i < 10; i++) { gotoxy (1,2+i); printf ("º º"); } gotoxy (1,12); printf ("ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ RICEZIONE ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͹"); for (i = 0; i < 10; i++) { gotoxy (1,13+i); printf ("º º"); } gotoxy (1,23); printf ("ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ"); } void visualizza_t (int car) { static int xt = 1,yt = 1; window (2,2,79,11); if (xt == 78) { switch (car) {

Page 274: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 273 di 299

case 13 : xt = 1; if (yt == 10) { clrscr (); yt = 1; } else yt++; break; case 8 : xt--; gotoxy (xt,yt); printf (" "); break; default : gotoxy (xt,yt); printf ("%c",car); xt = 1; if (yt == 10) { clrscr (); yt = 1; } else yt++; break; } } else { switch (car) { case 13 : xt = 1; if (yt == 10) { clrscr (); yt = 1; } else yt++; break; case 8 : if (xt != 0) { xt--; gotoxy (xt,yt); printf (" "); } else if (yt != 1) { yt--; xt = 78; gotoxy (xt,yt); printf (" "); } break; default : gotoxy (xt,yt); printf ("%c",car); xt++; break; } } }

Page 275: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 274 di 299

void visualizza_r (int car) { static int xt = 1,yt = 1; window (2,13,79,22); if (xt == 78) { switch (car) { case 13 : xt = 1; if (yt == 10) { clrscr (); yt = 1; } else yt++; break; case 8 : xt--; gotoxy (xt,yt); printf (" "); break; default : gotoxy (xt,yt); printf ("%c",car); xt = 1; if (yt == 10) { clrscr (); yt = 1; } else yt++; break; } } else { switch (car) { case 13 : xt = 1; if (yt == 10) { clrscr (); yt = 1; } else yt++; break; case 8 : if (xt != 1) { xt--; gotoxy (xt,yt); printf (" "); } else if (yt != 0) { yt--; xt = 78; gotoxy (xt,yt); printf (" "); } break; default : gotoxy (xt,yt); printf ("%c",car);

Page 276: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 275 di 299

xt++; break; } } }

Page 277: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 276 di 299

BORLANDC++

LIBRERIA DI CLASSI Il BorlandC++ mette a disposizione del programmatore una serie di classi precostituite delle quali fornisce il file di definizione H e il sorgente CPP e che possono essere utilizzate all’interno delle applicazioni. Per evitare problemi con la gestione dei progetti si consiglia d’inserire i file d’inclusione di tutta la gerarchia cui appartiene la classe che si vuole utilizzare. Per esempio, per usare la classe STRING.H derivata da sortable a sua volta derivata da object occorre seguire la seguente procedura. Aggiungere nel menu O/D

c:\compiler\borlandc\classlib\include c:\compiler\borlandc\classlib\lib

Inserire nel codice sorgente i file d’inclusione relativi a tutte le classi appartenenti alla gerarchia, rispettando l’ordine. #include <object.h> #include <sortable.h> #include <string.h>

Tutte le classi predefinite sono contenute nella directory classlib.

Per esempio, la classe STRING.H. /*------------------------------------------------------------------------*/ /* */ /* STRNG.H */ /* */ /* Copyright Borland International 1991, 1992 */ /* All Rights Reserved */ /* */ /*------------------------------------------------------------------------*/ #if !defined( _STRNG_H ) #define _STRNG_H #if !defined( __CLSTYPES_H ) #include <ClsTypes.h> #endif // __CLSTYPES_H #if !defined( __SORTABLE_H ) #include <Sortable.h> #endif // __SORTABLE_H #if !defined( __STRING_H ) #include <string.h> #endif // __STRING_H #if !defined( __CHECKS_H ) #include <Checks.h> #endif // __CHECKS_H #pragma option -Vo- #if defined( __BCOPT__ ) && !defined( _ALLOW_po )

Page 278: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 277 di 299

#pragma option -po- #endif _CLASSDEF(ostream) _CLASSDEF(String) class _CLASSTYPE String : public Sortable { public: String( const char _FAR * = "" ); String( const String _FAR & ); virtual ~String() { delete theString; } String& operator = ( const String _FAR & ); operator const char _FAR *() const; virtual int isEqual( const Object _FAR & ) const; virtual int isLessThan( const Object _FAR & ) const;\ virtual classType isA() const { return stringClass; } virtual char _FAR *nameOf() const { return "String"; } virtual hashValueType hashValue() const; virtual void printOn( ostream _FAR & ) const; private: sizeType len; char _FAR *theString; }; inline String::operator const char _FAR *() const { return theString; } #if defined( __BCOPT__ ) && !defined( _ALLOW_po ) #pragma option -po. #endif #pragma option -Vo. #endif

Page 279: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 278 di 299

ASSEMBLY - BORLANDC

COMPILAZIONE CLI (COMMAND LINE INTERFACE) Applicazione console Sono semplici da costruire, non hanno grafica e hanno interfaccia CUI (Character User Interface) che permette all’utente d’interagire con la tastiera e una finestra. /* Nome dell’applicazione: hello.c Programmatore: Descrizione: */ #include <stdio.h> #include <conio.h> int main (void) { /* clrscr(); */ printf ("Ciao, mondo in BorlandC\n\n"); printf ("Premere un tasto qualsiasi per chiudere l'applicazione"); getch();return(0); }

Applicazione assembly

Il compilatore genera il file HELLO.ASM. ifndef ??version ?debug macro endm publicdll macro name public name endm $comm macro name,dist,size,count comm dist name:BYTE:count*size endm

Page 280: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 279 di 299

else $comm macro name,dist,size,count comm dist name[size]:BYTE:count endm endif ?debug V 300h ?debug S "hello.c" ?debug C E968770D3F0768656C6C6F2E63 ?debug C E94019311924433A5C434F4D50494C45525C424F524C414E44435C+ ?debug C 494E434C5544455C737464696F2E68 ?debug C E94019311924433A5C434F4D50494C45525C424F524C414E44435C+ ?debug C 494E434C5544455C5F646566732E68 ?debug C E94019311925433A5C434F4D50494C45525C424F524C414E44435C+ ?debug C 494E434C5544455C5F6E66696C652E68 ?debug C E94019311924433A5C434F4D50494C45525C424F524C414E44435C+ ?debug C 494E434C5544455C5F6E756C6C2E68 ?debug C E94019311924433A5C434F4D50494C45525C424F524C414E44435C+ ?debug C 494E434C5544455C636F6E696F2E68 _TEXT segment byte public 'CODE' _TEXT ends DGROUP group _DATA,_BSS assume cs:_TEXT,ds:DGROUP _DATA segment word public 'DATA' d@ label byte d@w label word _DATA ends _BSS segment word public 'BSS' b@ label byte b@w label word _BSS ends _TEXT segment byte public 'CODE' ; int main (void) assume cs:_TEXT _main proc near push bp mov bp,sp ; { /* clrscr(); */ ; printf ("Ciao, mondo in BorlandC\n\n"); mov ax,offset DGROUP:s@ push ax call near ptr _printf pop cx ; printf ("Premere un tasto qualsiasi per chiudere l'applicazione"); mov ax,offset DGROUP:s@+26 push ax call near ptr _printf pop cx ; getch();return(0); call near ptr _getch

Page 281: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 280 di 299

xor ax,ax jmp short @1@58 @1@58: ; } pop bp ret _main endp ?debug C E9 ?debug C FA00000000 _TEXT ends _DATA segment word public 'DATA' s@ label byte db 'Ciao, mondo in BorlandC' db 10 db 10 db 0 db 'Premere un tasto qualsiasi per chiudere l' db 39 db 'applicazione' db 0 _DATA ends _TEXT segment byte public 'CODE' _TEXT ends public _main extrn _getch:near extrn _printf:near _s@ equ s@ end

COMPILAZIONE IDE

Specificatori di segmento Il BorlandC supporta quattro modificatori delle modalità d’indirizzamento. _cs, _ds, _es, _ss Questi modificatori, applicati ad una dichiarazione di puntatore, trasformano il puntatore in un offset all’interno del segmento specificato.

Page 282: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 281 di 299

int _es *ptr; Esempio, visualizzazione e modifica del contenuto della memoria utilizzando il modificatore di accesso far. #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <conio.h> void visual_mem(void); void modif_mem(void); int main(void) { char car; clrscr(); for (;;) { printf ("\n(V) Visualizza - (M) Modifica - (U) Uuscita"); car=getch(); printf ("\n") ; switch (tolower(car)) { case ‘v’: visual_mem();break; case ‘m’:modif_mem();break; case ‘u’:exit(0); } } getch();return (0); } /* Questa funzione visualizza 256 byte della memoria a partire dall’indirizzo a 20 bit indicato dall’utente organizzato in 16 righe di 16 byte */ void visual_mem() { register int i;unsigned char car; char s[80]; unsigned char far *p; clrscr(); printf ("Indirizzo di partenza (esadecimale): "); scanf ("%p%*c",&p); printf ("%p: " ,p); /*visualizza l’indirizzo*/ for (i=1; i<=256; i++) { car = *p; printf ("%02X ",car ); /* visualizzazione in esadecimale*/ p++; if (!(i%16)) { /*va a capo ogni 16 byte*/ printf ("\n"); if (i!=256) printf ("%p: ",p); /*visualiza l’indirizzo*/ } } } /* Questa funzione modifica il contenuto di un byte in memoria */ void modif_mem() { unsigned char far *p;char s[80];char valore; clrscr(); printf ("Indirizzo del byte da modificare (esadecimale): "); scanf ("%p%*c", &p); printf ("Nuovo valore (esadecimale): "); scanf ("%x%*c", &valore); /* modifica il valore */

Page 283: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 282 di 299

*p = (unsigned char) valore; }

DOS Le funzioni eseguono un’istruzione INT 21H e il valore è restituito in AH, se il CF è settato significa che si è verificato un errore. int intdos(union REGS *inregs, union REGS *outregs); int intdosx(union REGS *inregs, union REGS *outregs,struct SREGS *segregs); Esempio, utilizzare INT 21H servizio 2CH per leggere l’ora di sistema. Ritorna. CH = ore (0-23) CL = minuti (0-59) DH = secondi (0-59) DL = hundredths (0-99) #include <stdio.h> #include <dos.h> #include <conio.h> int main(void) { union REGS in,out;clrscr(); in.h.ah=0X2C; intdos(&in,&out); printf ("Sono le %.2d:%.2d:%.2d",out.h.ch,out.h.cl,out.h.dh); getch();return 0; }

BIOS int int86(int intno, union REGS *inregs, union REGS * inregs); int int86x(int intno, union REGS *inregs, union REGS *outregs,struct SREGS *segregs); Dove. intno: è il numero d’interruzione S/W da chiamare, se il carry flag è settato significa che si è verificato un errore.

Page 284: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 283 di 299

inregs: si mettono, prima della chiamata, i valori che andranno messi nei registri della CPU. inregs: dopo la chiamata, si troveranno i valori dei registri al ritorno dalla funzione BIOS. Se i valori originari possono essere persi, si può anche usare una sola variabile di tipo union REGS. Il BIOS è progettato per essere usato in linguaggio assembly, in questo modo si mettono nei registri della CPU i parametri da passare, si esegue l’interrupt, si leggono i risultati dai registri della CPU. In C non si possono usare direttamente i registri della CPU, per cui si usa un’apposita union definita nell’header <dos.h> che contiene una copia di ciascun registro della CPU. union REGS r; I registri a 8 bit sono in r.h, quelli a 16 bit in r.x. r.h.bl = 0x45; /*registro BL*/ r.h.bh = ‘z’; /*registro BH*/ r.x.bx = 0xa3f0; /*registro BX*/ Esempio, utilizzare INT 10H servizio 2H: Set Cursor Position. AH = 02 BH = numero di pagina (0 per il graphics modes) DH = riga DL = colonna #include <stdio.h> #include <dos.h> #include <conio.h> #define VIDEO 0x10 /* funzioni video */ void movetoxy(int x, int y); int main(void) { clrscr(); movetoxy(35,10); printf("Ciao, mondo!\n"); getch();return (0); } void movetoxy(int x, int y) { union REGS regs; regs.h.ah = 2; regs.h.dh = y; regs.h.dl = x; regs.h.bh = 0; int86(VIDEO, &regs, &regs); /* chiamata al BIOS */ }

Page 285: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 284 di 299

PSEUDOVARIABILI - IDENTIFICATORI DI REGISTRI I dati nelle pseudo variabili non rimangono a lungo. _AX = unsigned int AX _AL = unsigned char AL. #include <conio.h> #include <dos.h> void writechar(char ch); int main(void) { clrscr(); gotoxy(1,1); writechar(‘*’); getch(); return 0; } void writechar(char ch) { struct text_info ti; gettextinfo(&ti); _AH = 9; /* interrupt 0x10 function 9 */ _AL = ch; /* character to be output */ _BH = 0; /* video page */ _BL = ti.attribute; /* video attribute */ _CX = 1; /* repetition factor */ geninterrupt(0x10); /* output the char */ }

#include <dos.h> #include <conio.h> unsigned _stklen = 0x200; unsigned _heaplen = 0; int main(void) { clrscr(); _DX = (unsigned) “Ciao, mondo!\r\n$”;

Page 286: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 285 di 299

_AX = 0x900; __int__(0x21); getch();return (0); }

#include <stdio.h> #include <dos.h> #include <conio.h> void cls (int color); void setcurs(int column, int row); union REGS reg; int main(void) { cls(15); setcurs (40,12); printf (“Centro!”); getch();return(0); } void cls(int color) { reg.x.ax = 0x0600; reg.x.cx = 0; reg.x.dx = 0x184F; reg.h.bh = color; int86(0x10,&reg,&reg); } void setcurs(int column, int row) { union REGS reg; reg.h.ah = 2; /* servizio 02H set cursor position */ reg.h.bh = 0; /* page number */ reg.h.dh = row; reg.h.dl = column; int86 (0x10,&reg,&reg); }

Page 287: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 286 di 299

ASSEMBLY INLINE Istruzione asm Ambiente a linea di comando

IDE Il compilatore C usa un assembler ridotto interno di nome BASM, inoltre, bisogna tenere conto delle seguenti regole e restrizioni. I commenti devono essere scritti in formato C e non assembly. Non è possibile utilizzare la tecnica delle macro. Dentro una funzione l’istruzione asm è in CS e fuori è in DS. C preserva solo SI e DI, variabili register. Per riferirsi a variabili del C usare: BYTE (WORD) PTR. Le funzioni non più lunghe di 480 byte. Le funzioni inline del C++ non accettano l’istruzione asm. È possibile accedere ai campi del record: asm MOV AX, INDIRIZZO.CAP. Le istruzioni di salto devono utilizzare le label dei goto del C: A: ... asm JMP A. Una routine ASM, chiamata dal C, ritorna il valore in AX o in DX:AX. Svantaggi: portabilità, manutenzione, debugging, compilazione lenta. È buona regola non mescolare C e asm nella stessa funzione. Esempio, calcolare il minimo tra due numeri interi. #include <stdio.h> #include <conio.h> int min (int v1,int v2); int main(void) { int a,b; clrscr(); printf (“Valori: “); scanf (“%d %d”,&a,&b); printf (“\nMinimo %d”, min(a,b)); getch();return (0); } int min (int v1,int v2) {asm { mov ax,v1 cmp ax,v2 jle exit mov ax,v2 } exit:return (_AX); }

Page 288: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 287 di 299

Esempio, calcolare il prodotto di due numeri interi. #include <stdio.h> #include <conio.h> int mul (int v1,int v2); int main(void) { int a,b; clrscr(); printf (“Valori “);scanf (“%d%d”,&a,&b); printf (“\n\nProdotto %d “,mul(a,b)); getch();return (0); } int mul (int v1,int v2) { asm {mov ax,v1 mov dx,v2 mul dx } return (_AX); }

Esempio, progettare un’applicazione che, acquisito un valore intero da tastiera, ne visualizzi la sua configurazione binaria. Se utilizzassimo esclusivamente il C le fasi di acquisizione e visualizzazione, non presenterebbero alcuna difficoltà, mentre sarebbe abbastanza complessa la costruzione della stringa di valori, bit, da visualizzare. Per contro, se scegliessimo l’assembly, l’analisi dei singoli bit della configurazione binaria sarebbe molto semplice, mentre creerebbe non poche difficoltà la gestione dell’I/O con la conversione dei valori numerici da decimale a binario e viceversa. #include <stdio.h> #include <conio.h> int main (void) { int a; clrscr(); printf (“Inserire un valore intero: “);scanf (“%d”,&a); printf (“\nIl corrispondente valore binario vale: “); asm {mov bx,a mov cx,10h } ciclo: asm {rcl bx,1 jnc zero mov dl,’1’

Page 289: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 288 di 299

jmp visualizza } zero: asm {mov dl,’0’ } visualizza: asm {mov ah,02h int 21h loop ciclo } getch(); return (0); }

Esempio, convertire una stringa da minuscolo a maiuscolo. #include <iostream.h> #include <conio.h> const char LOWER_CASE_A=‘a’; const char LOWER_CASE_Z=‘z’; const int ADJUST_VALUE=32; const int MAX_STRING_LENGTH=100; char *TestString = “testo inizialmente in minuscolo!”; unsigned int StringToUpper(unsigned char far * DestFarString, unsigned char far * SourceFarString); char UpperCaseString[MAX_STRING_LENGTH]; int main (void) { unsigned int StringLength; clrscr(); StringLength = StringToUpper(UpperCaseString, TestString); cout<<“Stringa Originale: “<<“\n”<<TestString<<“\n\n”; cout<<“Stringa maiuscola: “<<“\n”<<UpperCaseString<<“\n\n”; cout<<“Numero di char: “<<StringLength; getch(); return (0); } /* Funzione per eseguire il trasferimento in maiuscolo Input: DestFarString - array in cui memorizzare le stringhe in maiuscolo SourceFarString - string contenente i char da convertirein maiuscolo Returns: La length in char della string senza contare lo 0 finale */ unsigned int StringToUpper(unsigned char far * DestFarString, unsigned char far * SourceFarString) { unsigned int CharacterCount; asm {cld push ds lds si,SourceFarString les di,DestFarString } CharacterCount = 0; StringToUpperLoop: asm {lodsb cmp al,LOWER_CASE_A

Page 290: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 289 di 299

jb SaveCharacter cmp al,LOWER_CASE_Z ja SaveCharacter sub al,ADJUST_VALUE } SaveCharacter: asm stosb CharacterCount++; asm {and al,al; jnz StringToUpperLoop; } CharacterCount--; asm pop ds return(CharacterCount); }

Istruzione __emit__ È possibile includere, direttamente all’interno di applicazioni C, parti di codice scritte in linguaggio macchina: in qualsiasi punto e con pieno accesso a variabili, costanti e funzioni. Adatta per funzioni molto brevi, 20 byte; codice compatto e veloce, impossibile il debugging. Esempio, incrementare una variabile intera. #include <stdio.h> #include <conio.h> int main(void) { int a; clrscr(); printf (“Inserisci un valore intero: “); scanf (“%d”,&a); __emit__ (0xff,0x46,0xfe); /* inc word ptr [bp-02] */ printf (“\n\nIl valore incrementato: %d”,a); getch();return (0); } Esempio, calcolare il minimo tra due numeri interi. #include <stdio.h> #include <conio.h> int min (int v1,int v2); int main(void) { int a,b; clrscr(); printf (“Valori “); scanf (“%d %d”,&a,&b); printf (“\n\nMinimo %d “, min(a,b)); getch();return (0); } int min (int v1,int v2)

Page 291: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 290 di 299

{ /*non servono push bp mov bp,sp */ __emit__ (0x8b,0x46,0x04); /*mov ax,[bp+04] (v1)*/ __emit__ (0x3b,0x46,0x06); /*cmp ax,[bp+06] (v2)*/ __emit__ (0x7e,0x03); /*jle exit */ __emit__ (0x8b,0x46,0x06); /*mov ax,[bp+06] (v2) */ __emit__ (0x8b,0xe5); /*mov sp,bp */ __emit__ (0x5d); /*pop bp */ __emit__ (0xc2,0x06,0x00); /*ret 6 */ } Esempio, calcolare il prodotto di due numeri interi. #include <stdio.h> #include <conio.h> int mul (int v1,int v2); int main(void) { int a,b; clrscr();printf (“Valori “);scanf (“%d%d”,&a,&b); printf (“\n\nProdotto %d “,mul(a,b)); getch();return (0); } int mul (int v1,int v2) { __emit__ (0x8b,0x46,0x04); /*mov ax,[bp+04] (v1)*/ __emit__ (0x8b,0x56,0x06); /*mov dx,[bp+06] (v2)*/ __emit__ (0xf7,0xe2); /*mul dx*/ __emit__ (0x8b,0xe5); /*mov sp,bp*/ __emit__ (0x5d); /*pop bp*/ __emit__ (0xc2,0x06,0x00); /*ret 6*/ return(0); } Esempio, progettare un’applicazione, in linguaggio macchina che resetti il PC. unsigned char far array[] = { 0xFB,0x31,0xDB, /* FB STI*/ 0x8E,0xDB,0xBB, /* 31DB XOR BX,BX*/ 0x72,0x04,0xB8, /* 8EDB MOV DS,BX*/ 0x34,0x12,0x89, /* BB7204 MOV BX,0472*/ 0x07,0xEA,0x00, /* B83412 MOV AX,1234*/ 0x00,0xFF,0xFF /* 8907 MOV [BX],AX*/ /* EA0000FFFF JMP FFFF:0000*/ }; int main(void) { void (far *funct)() = (void(far *)()) array;(*funct)(); return (0); } Anche se è bene non esagerare, questa è una delle tecniche preferite dai programmatori di sistema che ottengono così il meglio di più mondi: linguaggi diversi si prestano infatti più o meno bene alla soluzione di problemi diversi.

Page 292: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 291 di 299

MODULI SEPARATI Anche se il C consente d’inserire direttamente nel file sorgente blocchi d’istruzioni assembly, per molte applicazioni risulta più conveniente sviluppare i moduli C e quelli assembly separatamente e provvedere poi, in fase di linking, a collegarli tra loro. La compilazione separata è una tecnica utilizzata frequentemente per realizzare applicazioni in cui la parte assembly risulti molto consistente. Il compilatore permette ciò tramite il menu Project dell’ambiente IDE, dove è possibile compilare e linkare i vari moduli. C’è però un prezzo da pagare per sfruttare a pieno le potenzialità offerte dall’assembler. Infatti, mentre quando si usa direttamente codice inline, è il compilatore a preoccuparsi della gestione delle specifiche di segmento, del riferimento alle variabili e del salvataggio dei registri, quando si ricorre alla traduzione separata tutti questi aspetti rimangono a carico del programmatore. Per garantire la compatibilità tra i moduli, le routine assembly esportabili nel codice C, risulta indispensabile mantenere la corrispondenza tra i nomi simbolici delle routine usate nei due linguaggi e i modelli di segmentazione utilizzati.

Il metodo più semplice consiste nel dichiarare nel modulo C le routine assembly come funzioni esterne. Nel modulo assembly, poiché il compilatore modifica automaticamente i nomi delle funzioni e delle variabili esterne ponendovi davanti un carattere di sottolineatura, per mantenere la corrispondenza, è sufficiente aggiungere in testa al nome il carattere di sottolineatura. TASM non fa differenza tra maiuscolo e minuscolo (/MX). Convenzioni sul salvataggio dei registri. BP, SP, CS, IP, SS devono essere preservati. AX, BX, CX, DX, ES, FLAG, SI, DI, DS possono essere manipolati. char, unsigned char, signed char = BYTE int, unsigned int, signed int = WORD long, unsigned long, signed long = DWORD float = PWORD, double = QWORD, long double = TBYTE Passaggio parametri Poiché le routine assembly sono interpretate dal compilatore come funzioni, occorre analizzare nel dettaglio non solo le modalità per la trasmissione dei parametri ma anche quelle necessarie per la restituzione dei risultati. Il C utilizza per il passaggio parametri in ingresso lo stack e per la restituzione dei risultati i registri. In particolare, durante l’esecuzione, al momento della chiamata di una funzione i valori dei

Page 293: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 292 di 299

parametri sono posti nello stack mettendo per primo l’argomento più a destra e continuando via via verso sinistra, in Pascal viceversa. Ad esempio la chiamata seguente. int test (int i, int j); j è il primo elemento a essere inserito, seguito da i. int PASCAL test (int i, int j) Usa la convenzione di chiamata del Pascal. -p usa la convenzione di chiamata del Pascal nella versione a linea di comando. int CDECL test (int i, int j); Forza la convenzione di chiamata del C. La convenzione di chiamata Pascal è migliore perché produce codice più compatto e inoltre non bisogna ripulire lo stack dopo la chiamata. Il valore della funzione, invece, è restituito tramite i registri secondo tre precise regole. 1. Se il risultato è a 8 bit, il valore è posto in AL. 2. Se il risultato è a 16 bit, il valore è posto in AX. 3. Se il risultato è a 32 bit, il valore è posto in DX:AX. Facendo riferimento alla funzione test di tipo NEAR, alla quale devono essere passati i parametri i e j, il codice standard di chiamata è il seguente. PUSH j ;nello stack è spinto j PUSH i ;nello stack è spinto i CALL test ;nello stack è salvato IP Esempio, progettare due semplici moduli, uno in C e l’altro in assembly che operano su variabili char e int. Ambiente a linea di comando File PRIMO.C #include <stdio.h> #include <conio.h> extern char far prova1 (char a); extern int prova2 (int a); int main (void) { char c=‘a’; /*parametro di chiamata di prova1*/ char c1; /*valore restituito da prova1*/ int i=5; /*parametro di chiamata di prova 2*/ int i1; /*valore restituito da prova2*/ clrscr();printf (“Valori dei parametri: C = %c I = %d “,c,i); c1=prova1(c); i1=prova2(i); printf (“\n\nValori restituiti: C1 = %c I1 = %d “,c1,i1); getch();return(0); } File PRIMO_1.ASM

Page 294: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 293 di 299

DOSSEG .MODEL SMALL .CODE PUBLIC _prova1 PUBLIC _prova2 _prova1 PROC FAR PUSH BP MOV BP,SP MOV AL,BYTE PTR [BP+6] INC AL POP BP RET _prova1 ENDP _prova2 PROC NEAR PUSH BP MOV BP,SP MOV AX,WORD PTR [BP+4] INC AX POP BP RET _prova2 ENDP END

Page 295: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 294 di 299

Ambiente IDE

Esempio, progettare un’applicazione per lo scambio di due variabili. File SWAPP.C #include <stdio.h> #include <conio.h> extern void swapp1 (int *p,int *q); int main (void) { int a,b; clrscr(); printf (“Inserisci i valori: “);scanf (“%d%d”,&a,&b); printf (“\nA = %d B = %d “,a,b); swapp1(&a,&b); printf (“\nA = %d B = %d “,a,b); getch();return (0); } File SWAPP_1.ASM DOSSEG .MODEL SMALL .CODE PUBLIC _swapp1 _swapp1 PROC NEAR PUSH BP MOV BP,SP SUB SP,2 PUSH SI PUSH DI MOV SI,WORD PTR [BP+4] MOV DI,WORD PTR [BP+6] MOV AX,WORD PTR [SI] MOV WORD PTR [BP-2],AX MOV AX,WORD PTR [DI] MOV WORD PTR [SI],AX

Page 296: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 295 di 299

MOV AX,WORD PTR [BP-2] MOV WORD PTR [DI],AX POP DI POP SI MOV SP,BP POP BP RET _swapp1 ENDP END

La funzione swapp2 opera direttamente sulle variabili del modulo. File SWAPP.C #include <stdio.h> #include <conio.h> extern void swapp2 (int p,int q); int a,b; int main (void) { clrscr(); printf (“Inserisci i valori: “);scanf (“%d%d”,&a,&b); printf (“\n\nA = %d B = %d “,a,b); swapp2(a,b); printf (“\n\nA = %d B = %d “,a,b); getch();return (0); } File SWAPP_1.ASM DOSSEG .MODEL SMALL .DATA EXTRN _a:WORD,_b:WORD .CODE PUBLIC _swapp2 _swapp2 PROC NEAR MOV DX,_a XCHG _b,DX MOV _a,DX RET _swapp2 ENDP END Le due versioni permettono d’illustrare due tecniche diverse per accedere e modificare le variabili definite nel file SWAPP.C. Esempio, molto spesso le applicazioni richiedono, per accedere ai dati o alle funzioni, l’inserimento di una password che è spesso crittografata in modo da essere completamente irriconoscibile durante l’analisi del file. Per motivi di sicurezza il confronto tra il codice segreto e la parola chiave inserita dall’utente

Page 297: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 296 di 299

è eseguito da un modulo separato nel cui ambiente locale è memorizzato in forma crittografata il codice segreto. Progettare il modulo assembly per il confronto tra il codice inserito e la parola chiave di riferimento e un’applicazione C che si attivi solo all’inserimento della parola chiave corretta. Prima di passare alla codifica della routine di controllo, occorre definire la modalità da utilizzare per crittografare la chiave. Ad esempio, utilizzando come codice segreto la parola KRAFEN, la sua forma crittografata potrebbe essere memorizzata in dieci byte aventi i seguenti valori decimali.

43 35 50 32 15 50 35 35 35 34 32 46 File PASS.C #include <stdio.h> #include <conio.h> #include <dos.h> extern int far ricevi (char s[]); void programma(void); int main (void) { char codice[6]; int i,esatto; for (i=0;i<6;i++) codice[i]=0; clrscr(); printf (“Inserire la password: “); for (i=0;i<6;i++) { codice[i]=getch();printf (“*”);} printf (“\n\n\n”); printf (“Controllo codice in corso”);delay (3000); esatto=ricevi(codice); if (esatto) {printf(“\n\n\nCodice corretto, premere un tasto per continuare ...”); while (!kbhit());getch();programma();} else printf (“\n\n\nCodice errato!!!!”); getch();return(0); } void programma (void) { clrscr(); printf (“Esecuzione programma”); } File PASS_1.ASM DOSSEG .MODEL SMALL .DATA criptogramma DB 40,35,50,32,15,50,35,35,35,34,32,46 parola_chiave DB 6 DUP (?) .CODE PUBLIC _ricevi _ricevi PROC FAR

PUSH DS PUSH ES PUSH BP PUSH DX PUSH CX MOV BP,SP LEA SI,criptogramma ;costruzione della parola chiave

Page 298: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 297 di 299

LEA DI,parola_chiave MOV CX,0006H ciclo: ;si sommano due byte del criptogramma MOV AL,[SI] INC SI ADD AL,[SI] INC SI MOV BYTE PTR [DI],AL ;si memorizza il risultato nella parola chiave INC DI LOOP ciclo MOV DI,WORD PTR [BP+14] ;ciclo di confronto LEA SI,parola_chiave MOV CX,0006H ciclo2: CMPSB JNE errore LOOP ciclo2 MOV AX,0FFFFH ;codice corretto JMP ritorno errore: MOV AX,0000H ;codice errato ritorno: POP CX POP DX POP BP POP ES POP DS RET _ricevi ENDP END

Esempio, progettare un modulo assembly che esegua la somma di numeri interi e calcoli la media di tali valori. File MEDIA.C #include <stdio.h> #include <conio.h> #define N 10 extern float Average (int far *ValuePtr,int NumberOfValues); float IntDivide (int Dividend,int Divisor); int TestValues[N]={1,2,3,4,5,6,7,8,9,10}; int main (void) { clrscr(); printf(“Il valore medio : %f\n”,Average(TestValues,N)); getch();return(0); } float IntDivide (int Dividend,int Divisor) { return ((float) Dividend/(float) Divisor);}

Page 299: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 298 di 299

File MEDIA_1.ASM ; Average somma gli interi quindi passa la somma e il numero di valori a IntDivide() ;Input: int far *ValuePtr: array di valori di media ;nt NumberOfValues: numbero dei valori per la media DOSSEG .MODEL SMALL EXTRN _IntDivide:PROC .CODE BEGIN: PUBLIC _Average _Average PROC PUSH BP MOV BP,SP LES BX,[BP+4] ;punta ES:BX all’array di valori MOV CX,[BP+8] ;# dei valori di cui fare la media MOV AX,0 ;azzera il totale AverageLoop: ADD AX,ES:[BX] ;somma il valore corrente ADD BX,2 ;punta al valore successivo LOOP AverageLoop PUSH WORD PTR [bp+8] PUSH AX ;passa il total come PA piu’ a sinix CALL _IntDivide ;calcola la media floating-point ADD SP,4 ;scarica i parametri POP BP RET ;la media e’ in TOS iAPX87 ENDP _Average END BEGIN

Esempio, progettare un modulo assembly che conta il numero di linee e il numero di caratteri di una stringa. File CONTA.C #include <stdio.h> #include <conio.h> char * TestString=“Linea 1\nlinea 2\nlinea 3”; extern unsigned int LineCount(char * StringToCount, unsigned int * CharacterCountPtr); int main (void) { unsigned int LCount, CCount; clrscr();LCount = LineCount(TestString, &CCount); printf(“Linee: %d\nCaratteri: %d\n”, LCount, CCount); getch();return(0); } File CONTA_1.ASM NEWLINE EQU 0ah DOSSEG .MODEL SMALL .CODE PUBLIC _LineCount _LineCount PROC push bp

Page 300: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

C e C++ um 299 di 299

mov bp,sp push si mov si,[bp+4] sub cx,cx mov dx,cx LineCountLoop: lodsb and al,al jz EndLineCount inc cx cmp al,NEWLINE jnz LineCountLoop inc dx jmp LineCountLoop EndLineCount: inc dx mov bx,[bp+6] mov [bx],cx mov ax,dx pop si pop bp ret _LineCount ENDP END

Page 301: Linguaggio C, C++ e BorlandC - ubertini.it · (c) visualizza un valore int come un carattere senza segno. (s) scrive i caratteri puntati dall’argomento, interrompe la scrittura

var Massimo_Ubertini = new Persona(); .WithTitle ("Insegnante a tempo indeterminato"); .WorkingIn (

{ type: "school", value: " I.T.I.S. Giacomo Fauser" }, { type: " address", value: Via Ricci, 14 28100 Novara" }, { type: "tel", value: +39 0321482411" }, { type: "fax", value: "+39 0321482444" }, { type: "web", value: "http://www.fauser.edu" });

.FocusedOn ("Programmazione Visual C#"); .Contact (

{ type: "web", value: "http://www.ubertini.it" }, { type: "email", value: "[email protected]" }, { type: "email", value: "[email protected]" });

.Is ("Laureato in Informatica"); .PassionateAbout (

new Technology ("Tecnologie informatiche"), new Technology ("Scienze e tecnologie applicate"), new Technology ("Informatica"), new Technology ("Sistemi e Reti"), new Technology ("Tecnologie e progettazione di sistemi informatici"), new Technology ("Gestione progetto, organizzazione d’impresa"));