Progetto D'esame
Progetto D'esame
INFORMATICA ’
Corso di laurea di ingegneria aereospaziale-meccanica gruppo A-Dao
Anno accademico 2021/2022
Io sottoscritto Federico Ceccarino N35430066 ho rifatto da solo tutti gli esercizi ed esempi di
programmazione già svolti e presentati dal Prof. Neri durante le lezioni dell'insegnamento prima di
intraprendere lo svolgimento delle Esercitazioni qui allegate che costituiscono il Progetto di Esame.
Abbate Virgilio, Bilancino Giulia, Candela Flavio, Carotenuto Carmine, Carotenuto Luca, Carrubba Claudia,
Catalano Vittorio, Cautiero Matteo, Ceccarino Federico, Centracchio Lorenzo, Conte Luigi Pio, Cozzolino
Pierpaolo, Criscuolo Francesco, Damiano Cristian.
PRIMA ESERCITAZIONE
ESERCIZIO 1
Scrivere un programma in grado di: leggere un numero imprecisato di numeri dallo standard input (scanf) e calcolare il
prodotto dei numeri letti fino a che viene letto il numero 36. A questo punto il programma termina e stampa il
risultato;
se pero' l'utente ha inserito il numero 11, tutti i numeri dati successivamente sono ignorati fino a che è inserito il
numero 36 e quindi il programma termina oppure è inserito il numero 22 e quindi il programma riprende ad effettuare
il prodotto dei numeri in input.
PRIMO PROGRAMMA
#include <math.h>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <stdio.h>
// inserisco le librerie
//(Fase input)
Int prodotto=1;// è la variabile che utilizzo per tenere traccia dei prodotti
//la dichiaro uguale a 1 dato che è l’elemento neutro del prodotto
//(Fase manipolazione)
Do{ //sfrutto un costrutto iterativo di tipo do while, il ciclo viene ripetuto finchè è vera la condizione del
while
printf (“inserendo 11 il programma ignorera’ tutti i numeri finche’ non inserirai 22\n”);
If (input==11) {semaforo=1;};
//utilizzo l’operatore && per fare in modo che le varie condizioni debbano essere verificate
contemporaneamente
//così facendo nel prodotto non vengono considerati i valori 36 11 e 22 che servono solo per
dare istruzioni al programma
{prodotto=prodotto*input;};
//se le condizioni sono tutte verificate vado ad eseguire il prodotto, nella variabile inserita
precedentemente
While (input!=36);//la condizione che deve essere verificata è che l’input sia diverso da 36
//Fase output
Return 0; //il keyword di C per far ritornatre il valore di una funzione (in questo caso nulla)
}
ESERCIZIO 2
Riscrivere il programma dell'Esercizio 1 utilizzando il costrutto funzionale. Commentare le differenze e i vantaggi dei
due programmi.
SECONDO PROGRAMMA
#include <math.h>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <stdio.h>
// inserisco le librerie
Int prodotto=1;// è la variabile che utilizzo per tenere traccia dei prodotti
Do{ //sfrutto un costrutto iterativo di tipo do while, il ciclo viene ripetuto finchè è vera la
condizione del while
printf (“inserendo 11 il programma ignorera’ tutti i numeri finche’ non inserirai 22\n”);
{prodotto=prodotto*input;};
//se le condizioni sono tutte verificate vado ad eseguire il prodotto, nella variabile
inserita precedentemente
While (input!=36);//la condizione che deve essere verificata è che l’input sia diverso
da 36
Return 0; //il keyword di C per far ritornare il valore di una funzione (in questo caso nulla)
}
Il vantaggio più importante che ho trovato è sicuramente il poter dividere il codice in blocchi in
modo da avere un programma più schematizzato e chiaro, inoltre questo mi ha permesso di operare opportune
correzioni in maniera più rapida ed efficace.
ESERCIZIO 3
Rispondere alle seguenti domande:
Come posso riscrivere la porzione di codice che contiene un Repeat ... Until in un Do ... While?
Un linguaggio di programmazione è una notazione formale che serve per descrivere algoritmi, in particolare essi
possono essere divisi in linguaggi di alto livello e basso livello. I linguaggi di alto livello non sono direttamente
comprensibili dal calcolatore, quindi i programmi scritti con tali linguaggi necessitano di essere compilati, ovvero
tradotti da un compilatore, prima di poter essere eseguiti. Il vantaggio dei linguaggi di alto livello è che è facilmente
comprensibile dal programmatore, e ciò rende più facile la scrittura e il debug dei programmi. I linguaggi di basso
livello sono invece direttamente comprensibili dal calcolatore e non necessitano di essere compilati. Come tutti i
linguaggi, anche i linguaggi di programmazione sono caratterizzati dal lessico, dalla sintassi e dalla semantica. Il lessico
è rappresentato dalle parole del linguaggio che vanno a formare il suo vocabolario (ad esempio un’istruzione), la
sintassi del linguaggio è formata dalle regole che prescrivono la costruzione delle frasi del linguaggio (esempio il ; a
termine di ogni frase nel linguaggio C), la semantica viene intesa come semantica operazionale, ovvero come il
calcolatore esegue le istruzioni scritte nel programma.
Un costrutto è un’istruzione complessa che permette di combinare tra loro istruzioni elementari, in particolare vi sono
i costrutti di controllo, i quali permettono di modificare la sequenza dinamica di esecuzione delle istruzioni. Essi si
dividono in costrutti di sequenza, iterativi e selettivi. I costrutti iterativi prescrivono la ripetizione di una o più istruzioni
al verificarsi di una determinata condizione (condizione booleana). La ripetizione del ciclo considerato continua fino a
quando la condizione di terminazione diventa falsa (o vera, a seconda del costrutto utilizzato), a quel punto il ciclo si
interrompe e il calcolatore continua nella normale esecuzione del programma. Repeat-until e do-while sono entrambi
dei costrutti ciclici ed hanno una sintassi molto simile, in quanto il calcolatore prima esegue il blocco di codice scritto
all’interno del ciclo e poi viene valutata la condizione di terminazione. La differenza più importante tra i due costrutti è
che il ciclo do-while continua fino a quando la condizione di terminazione è vera, quindi quando questa diventa falsa il
ciclo si interrompe, al contrario invece del ciclo repeat-until, dove avviene esattamente l’opposto. Un’altra differenza è
rappresentata dal fatto che repeat -until non è presente nel linguaggio C. Per quello appena detto, per scrivere lo
stesso programma una volta con do-while e una volta con repeat- until si dovranno scrivere le due condizioni di
terminazione una opposta all’altra, ad esempio nel do-while condizione (x<5), mentre in repeat until condizione(x>5),
in questo modo il numero di ripetizioni dei due cicli sarà lo stesso (a parità di condizioni iniziali).
ESERCIZIO 4
Rispondere alle seguenti domande:
Cosa sono la dichiarazione di una funzione e il corpo di una funzione? A cosa servono i parametri di una funzione?
Per funzione noi intendiamo un blocco di codice che identifichiamo con un nome, in modo da poterlo utilizzare nel
programma semplicemente scrivendo il nome ad esso associato. La definizione della funzione si divide quindi in due
parti: il titolo della funzione, in cui viene indicato il nome della funzione ed eventuali parametri, e il corpo della
funzione, formato da tutte quelle istruzioni che definiscono operativamente il blocco di codice. La funzione viene
quindi utilizzata per evitare di dover riscrivere lo stesso blocco di codice più volte all’interno per programma ed è alla
base della programmazione strutturata, in quanto ci permette di dividere il problema in tanti problemi più semplici
che vengono risolti ciascuno con un sottoprogramma. Alcune volte può capitare di dover scrivere due funzioni diverse
in merito a blocchi di codice simili ma che producono risultati diversi (esempio: due funzioni, una per addizione e una
per moltiplicazione di due numeri). Da qui viene l’idea di parametrizzare la funzione, ovvero permettere l’inserimento
di dati in entrata tali da modificare la funzione stessa, in questo modo da un’unica funzione si possono ottenere
diverse sequenze dinamiche di esecuzione e diversi valori di ritorno. Usando la parametrizzazione le variabili che
ricevono i dati in entrata vengono chiamati parametri formali e devono essere dichiarati nel titolo della funzione
chiamata, mentre le variabili i cui valori diventano i dati in entrata della funzione vengono detti parametri effettivi e
devono essere indicati in ogni chiamata della funzione. Esistono diversi modi per avere il passaggio dei parametri tra la
funzione chiamata e quella chiamante, tra cui vi sono il passaggio di parametri per valori, in cui si copia il valore
inserito nel parametro effettivo e lo si va ad inserire nel parametro formale, e il passaggio di parametri per referenza,
in cui si associa al parametro formale lo stesso indirizzo di memoria del parametro effettivo in modo da poter
modificarlo direttamente.
ESERCIZIO 5
Rispondere alle seguenti domande:
Cosa è un array?
Un array è una sequenza di variabili dello stesso tipo, viene utilizzato quando vogliamo manipolare più valori
contemporaneamente. La dichiarazione di un array si ha identificandolo con un nome, indicando il tipo di valori che
può contenere e specificando la cardinalità dell’array, ovvero quanti valori può contenere. Il nome dell’array identifica
l’indirizzo di memoria dell’array stesso. In particolare, l’array viene memorizzato in sequenza e il numero di celle di
memoria necessarie dipende dal tipo e dalla cardinalità dello stesso. L’array viene sovradimensionato per poter avere
a disposizione tutte le celle di cui si ha bisogno, per questo si utilizza una variabile di riempimento che ha il compito di
indicarci quante celle dell’array sono effettivamente riempite e quale è la prossima cella libera.
Esercizio 6
Scrivere un programma che chiede in input una stringa all'utente e conta quante volte le sequenze di
Il programma produce in uscita, il numero di volte che 'do' e 're' occorrono nella stringa data in ingresso
e una nuova stringa ottenuta rimuovendo le sequenze 'do' e 're' dalla stringa in ingresso. Strutturare il codice
utilizzando il costrutto funzionale.
#include <stdio.h>
#include <iostream>
#include <cstdlib>
#include <cstring>
char valutazione='0';
if (valore=='d'){ //se il carattere inserito è d a valutazione viene assegnato 1 nel caso in cui il carattere è r a
valutazione viene assegnato 2
valutazione='1';}
else if(valore=='r'){
valutazione='2';
int main(){
char stringa_inserita[100];
char stringa_visualizzata[100];
char variabile;
int ripetizioni=0;
int contatore=0;
int contatore2=0;
while(stringa_inserita[contatore]!='\0'){ //eseguo un ciclo iterativo che scansiona ogni carattere della stringa
variabile=stringa_inserita[contatore];
if(variabile=='0'){
stringa_visualizzata[contatore2]=stringa_inserita[contatore];
contatore2++;
else
if(variabile=='1'){
if(stringa_inserita[contatore+1]=='o'){
ripetizioni++;
contatore++;} // aumento qui la variabile contatore perchè il carattere successivo non deve essere considerato in
quanto è già stato analizzato
else{
stringa_visualizzata[contatore2]=stringa_inserita[contatore];
contatore2++;}
else
if(variabile=='2'){
if(stringa_inserita[contatore+1]=='e'){
ripetizioni++;
contatore++; }
else{
stringa_visualizzata[contatore2]=stringa_inserita[contatore];
contatore2++;}
contatore++;
stringa_visualizzata[contatore2]='\0';
system("PAUSE");
return 0;
SECONDA ESERCITAZIONE
ESERCIZIO 1
#include <stdio.h>
#include <iostream>
#include <cstdlib>
#include <cstring>
typedef struct{
}impegno;
char scambio[20];
int numero;
strcpy(scambio,agenda[i].sintesi);strcpy(agenda[i].sintesi,agenda[posmin].sintesi);strcpy(agenda[posmin].sintesi,scam
bio);
int ora;
int mese;
for(i=0;i<n;i++){
fflush(stdin); gets(agenda[i].sintesi); }
}
// ho usato un triplo selectort, in modo da ordinare prima in base al mese, poi in base al giorno e infine in base all'ora
if(agenda[j].mese<agenda[posmin].mese){
posmin=j;}} if(posmin!=i)
int i,j;
int numero;
for(i=0;i<n-1;i++){
i=posmin;
for(j=i+1;j<n;j++){
for(i=0;i<n-1;i++){
posmin=i;
funzione_scambia(agenda,posmin,i);
agenda[posmin].data>agenda[j].data){
posmin=j;}}
}}
for(i=0;i<n-1;i++){
i=posmin;
for(j=i+1;j<n;j++){
if(agenda[j].mese==agenda[posmin].mese&&agenda[posmin].data==agenda[j].data&&agenda[posmin].ora>agenda[j].
ora){
if(posmin!=i)
if(posmin!=i)
funzione_scambia(agenda,posmin,i);
posmin=j;}}
funzione_scambia(agenda,posmin,i);
int i;
impegno miaagenda[400];
funzione_riempimento(miaagenda,n);
funzione_ordinamento(miaagenda,n);
for(i=0;i<n;i++){
printf("sintesi: %s\n",miaagenda[i].sintesi); }
system("PAUSE"); }
Esercizio 2
La macchina di Turing fu proposta nel 1936 dal logico britannico Alan Turing, si tratta di un modello ideale
di agente di calcolo volto a simulare la logica di qualsiasi algoritmo computazionale; partì dall’idea di
rendere automatica una macchina da scrivere. Il modello formale fu proposto nel 1936, come sistema
astratto che, opportunamente programmato, era capace di eseguire ogni tipo di operazione. È un modello
(non a stati finiti) fondamentale nella teoria dell’informatica, in quanto permette di raggiungere risultati
teorici sulla calcolabilità e sulla complessità degli algoritmi. Questa macchina, pur nella sua semplicità, può
calcolare in un numero finito di passi elementari qualsiasi funzione computabile. Si può dunque definire la
macchina come un modello di agente di calcolo generale che modellizza ciò che effettivamente può
compiere un calcolatore. Attualmente ne esistono tante varianti, la più semplice delle quali è la macchina di
Turing a nastro, formata da:
un’unità di controllo contenente un programma con un numero finito di istruzioni, che, per ogni
coppia (stato, simbolo letto) determina il cambiamento di stato ed esegue un’azione elaborativa;
da un nastro di lunghezza potenzialmente illimitata suddiviso in celle, ogni cella contiene un
simbolo oppure è vuota;
da un’unità di lettura e scrittura sul nastro in grado di spostarsi avanti e indietro di un numero
qualsiasi di celle e di leggere e scrivere in una qualsiasi delle celle un simbolo di un alfabeto
prefissato.
Ad ogni istante la macchina si trova in uno stato appartenente ad un insieme finito e legge un simbolo sul
nastro. La funzione di transizione, in modo deterministico fa scrivere un simbolo, fa spostare la testina in
una direzione o nell'altra o fa cambiare lo stato.
Ogni macchina di Turing possiede un alfabeto che contenga il simbolo speciale b (blank, spazio), i simboli 0
e 1, e un numero finito di ulteriori simboli, come X e Y, usati come segnaposto. In ogni istante solo un
numero finito di celle contiene simboli diversi da b. Il nastro funziona quindi da input, da output e da
memoria. La macchina è costituita da un numero finito k di stati o configurazioni ed è pensata per eseguire
solo un tipo di operazione primitiva. A ogni operazione corrispondono tre azioni o comandi:
Una macchina di Turing la cui parte di controllo è capace di leggere da un nastro anche la descrizione
dell’algoritmo è una macchina universale capace di simulare il lavoro compiuto da un’altra macchina
qualsiasi. Si parla di Macchina di Turing Universale riferendosi all’interprete di un linguaggio
Esercizio 3
Spiegare cosa si intende per a) tipo di dato e definizione di tipo di dato b) variabile e dichiarazione di
variabili.
Con il termine “dato” si indica una rappresentazione di fatti e concetti in modo formale perché sia possibile
una loro elaborazione da parte di strumenti automatici. Il dato senza un contesto può non avere significato:
uno stesso numero può esprimere cose diverse in situazioni diverse; così come una stessa parola può avere
significato diverso in contesti diversi. L’ambiguità viene risolta dando al dato un’interpretazione, perché un
dato diventi informazione (percezione del dato attraverso un’interpretazione) si devono pertanto
specificare:
un tipo, ossia l'insieme degli elementi in cui si effettua la scelta, caratterizzato dal numero di questi
(cardinalità del tipo). La cardinalità di un tipo può essere finita o infinita ma praticamente si
trattano solo tipi a cardinalità finita per poter essere inseriti in un elaboratore;
un valore, ossia il particolare elemento scelto; concetto che comporta una certa astrazione e non
deve essere confuso con il problema della rappresentazione dell'informazione;
l'attributo (semantica o significato), ossia l'identificatore (o anche metadato) che conferisce
significato all'elemento scelto.
Esempio: Tipo di dato: i numeri interi compresi tra [MIN, MAX]. Valori: 200, 313, 827, ... Attributo: nome
della variabile: abitanti_comune
“Per tipo di dato si intende un insieme di valori associati a delle operazioni definite su di essi". Con le
definizioni di tipo è possibile trattare l'informazione in maniera astratta, cioè prescindendo dal modo
concreto con cui essa è rappresentata all'interno di una macchina.
I tipi di dati possono essere classificati secondo la struttura in tipi atomici o primitivi e tipi derivati. I tipi
primitivi sono i tipi semplici che non possono essere decomposti, come ad esempio numeri interi o
booleani; ogni linguaggio tipizzato ne possiede. I tipi derivati si ottengono dai tipi atomici mediante
opportuni operatori forniti dal linguaggio: essi includono i tipi strutturati (record) o gli array, ma anche i
puntatori di un tipo fissato (in linguaggi come il C), i tipi funzione (specialmente nei linguaggi funzionali).Essi
sono:
Booleani: Il tipo booleano ha due soli valori: true ("vero") e false ("falso"). Essi vengono utilizzati in
modo speciale nelle espressioni condizionali per controllare il flusso di esecuzione e possono essere
manipolati con gli operatori booleani AND, OR, NOT e così via.
Numeri: I tipi di dati numerici includono i numeri interi e i numeri razionali in virgola mobile, che
sono astrazioni dei corrispondenti insiemi di numeri della matematica. Quasi tutti i linguaggi
includono tipi di dati numerici come tipi predefiniti e forniscono un certo numero di operatori
aritmetici e di confronto su di essi.
Caratteri e stringhe: Il tipo carattere contiene un carattere, generalmente ASCII, memorizzato in un
byte. In questi anni si sta affermando il nuovo standard Unicode per i caratteri, che prevede 16 bit
la rappresentazione di un singolo carattere. Molti linguaggi tradizionali si sono adattati a questo
standard emergente introducendo, in aggiunta al tipo "carattere a 8 bit", un nuovo tipo "carattere a
16 bit", talvolta detto wide char.
Enumerazioni: Le enumerazioni sono insiemi finiti di identificatori, generalmente specificati dal
programmatore. Puntatori: I valori di tipo puntatore sono indirizzi di memoria di variabili, oggetti (o
altri elementi di programma),l’uso di puntatori è spesso necessario per costruire strutture dati
complesse e dalla forma non prevedibile a priori e/o variabile nel tempo come grafi, alberi, liste e
così via.
Array:Un array è una sequenza finita di elementi appartenenti a un determinato tipo, indicata
mediante un numero intero.
Una variabile invece è simile a una scatola che può contenere un solo oggetto (dato).
globali, quelle variabili dichiarate al di fuori di un sottoblocco di programma, e che sono visibili in
uno o più sottoblocchi, dipendentemente dalla struttura del programma stesso;
locali, quelle definite all'interno di un sottoblocco, ad esempio all'interno di una funzione, e quindi
visibili solo internamente al sottoblocco stesso.
Ogni variabile ha un nome univoco che la distingue da tutte le altre. L'operazione per dare un nome a una
variabile è detta "dichiarazione".
Dopo aver dichiarato la variabile, si può assegnare ad essa un determinato valore. Questa operazione è
detta "assegnazione". Si può assegnare un valore numerico, una stringa alfanumerica (testo e numeri), un
valore logico-booleano (vero o falso), ecc. Una volta registrato un valore a una variabile, si può richiamare e
leggere il suo contenuto in qualsiasi momento.
ESERCIZIO 4
Con riferimento all'architettura di Von Neumann, spiegare in dettaglio come l'elaboratore esegue
l'istruzione C:
La C.U. legge l’istruzione di somma contenuta all’interno dell’instruction register, manda attraverso il bus
dei registri l’indirizzo di memoria del registro associato alla variabile b al buffer registri all’interno della
R.A.M., da qui l’indirizzo viene letto dal decodificatore che fa attivare il registro, la C.U. manda quindi il
segnale di controllo read al registro, il quale copia il suo contenuto (b=3) e attraverso il bus data interno
della R.A.M. viene inserito nel buffer data. Attraverso il bus data il valore contenuto nel buffer arriva al
registro MBR della CPU e da qui al registro OP1. Le stesse operazioni vengono fatte per la variabile c, il cui
contenuto (c=4) viene inserito in OP2. La C.U. attiva quindi l’A.L.U. che compie l’operazione di somma
prendendo gli addendi da OP1 e OP2 e inserisce il risultato (a=7) all’interno del registro accumulatore, da
qui la somma passa al MAR. La C.U. quindi inserisce all’interno del MBR l’indirizzo di memoria della
variabile c che attraverso il bus indirizzi arriva al buffer indirizzi della R.A.M., in modo da attivare il registro
associato alla variabile c (4). A questo punto il valore inserito all’interno del MBR passa tramite il bus data al
buffer dati della R.A.M., successivamente tramite il segnale di controllo write la C.U. informa il registro
dell’operazione di store, quindi la somma (7) dal buffer dati passa attraverso al bus data interno della
R.A.M. al registro associato alla variabile C. Tutte queste operazioni sono temporizzate e sincronizzate
tramite il battito di clock.
TERZA ESERCITAZIONE
ESERCIZIO 1
#include <stdio.h>
#include <iostream>
#include <cstdlib>
#include <cstring>
typedef
char nome[20];
}registrazione;
if(*testa==coda)
else{
}}
strcpy(nome_chiamato,sportello[*testa].nome);
*testa=(*testa)%max_element;
if((*coda+1)%max_element==testa)
scanf("%s",nome);
strcpy(sportello[*coda].nome,nome);
*coda=(*coda)+1;
*coda=(*coda)%max_element; }
}
int main(){
int max_element;
scanf("%d",&max_element);
registrazione sportello[max_element];
int testa=0;
int coda=0;
int accensione=1;
int i;
while(accensione!=0){
printf("DECIDERE l'azione da fare,0 per terminare il lavoro dello sportello, 1 per chiamare allo sportello il cliente
arrivato prima,2 per aggiungere un nuovo cliente\n");
scanf("%d",&accensione);
if (accensione==1)
funzione_chiamata_sportello(sportello,&testa,coda,max_element);
if (accensione==2)
funzione_aggiungi_in_coda(sportello,&coda,testa,max_element);
printf("\n"); }
system("PAUSE");
return 0; }
ESERCIZIO 2
#include <stdio.h>
#include <iostream>
#include <string.h>
typedef
struct {
char elemento[10];
char descrizione[10];
} elemento;
int pos;
char el[10],desc[10],temp[10];
scanf("%s",el);
printf("\ndescrizione:\n");
scanf("%s",desc);
if(*k==0){
strcpy(dizionario[0].elemento,el);
strcpy(dizionario[0].descrizione,desc);
if(*k==1){
strcpy(dizionario[0].elemento,el);
strcpy(dizionario[0].descrizione,desc);
strcpy(dizionario[1].elemento,el);
if(*k>1){
for(int j=*k-1;j>=0;j--){
strcpy(dizionario[j+1].elemento,dizionario[j].elemento);
strcpy(dizionario[0].elemento,el);
strcpy(dizionario[*k].descrizione,desc);
}else{
for(int i=0;i<*k-1;i++){// se il mio elem sta nel mezzo quindi è compreso tra due
valori.
if((strcmp(el,dizionario[i].elemento)>=0)&&(strcmp(el,dizionario[i+1].elemento)<=0)){
for(int j=*k-1;j>=pos;j--){// sposto gli elementi alla sua destra creando un buco. ad es.
pos=3 e l'ultima posizone è 5 allora copio- 5 in 6- poi -4 in 5- e 3 in 4- ho il pos=3 libero.
strcpy(dizionario[j+1].elemento,dizionario[j].elemento);
strcpy(dizionario[j+1].descrizione,dizionario[j].descrizione);
strcpy(dizionario[pos].elemento,el);
strcpy(dizionario[pos].descrizione,desc);
char el[10];
int i,j,pos;
if(*k==0){
if(*k>0){
for(i=0;i<*k;i++){
pos=i;
for(j=pos;j<*k-1;j++){
strcpy(dizionario[j].descrizione,dizionario[j+1].descrizione);
int i=0;
while(i<k){
printf("\n%s : %s \n",dizionario[i].elemento,dizionario[i].descrizione);
i++;
}}
int main(){
elemento dizionario[100];
int i=0;
int k=0;
while(i!=2){
scanf("%d",&i);
if(i==0){
inserire(dizionario, &k);
stampa(dizionario,k);
}
if(i==1){
rimuovere(dizionario, &k);
stampa(dizionario,k);
stampa(dizionario,k);
system("PAUSE");
return(0);
ESERCIZIO 3
#include <stdio.h>
#include <iostream>
#include <cstdlib>
#include <cstring>
}}
for(j=0;j<colonne;j++){
printf ("Inserisci il numero reale nella posizione [%d] [%d] della matrice :\n",i,j);
scanf("%f", &numero);
mat1[i][j]=numero;
printf("\n"); for(j=0;j<colonne;j++){
} printf("\n"); }
FILE *fp_in;
}
for(j=0;j<colonne3;j++){
matricesomma[i][j]=(mat1[i][j])+(mat2[i][j]); }
for(i=0;i<righe3;i++){
printf("\n");
fprintf(fp_in,"\n");
for(j=0;j<colonne3;j++){
}}
fclose(fp_in);
int i,j,k;
for(i=0;i<righe4;i++){
}}
for(j=0;j<colonne5;j++){ prodotto[i][j]=0;
for (k=0;k<colonne4;k++){
prodotto[i][j]=prodotto[i][j]+mat1[i][k]*mat2[k][j];
}}
printf ("\n");
int massimo=mat1[0][0];
int indice;
int i,j;
for (i=0;i<righe;i++){
for(j=0;j<colonne;j++){
if (mat1[i][j]>massimo) {
}}
massimo=mat1[i][j];
indice=i; }
printf("\nLa riga con l'elemento maggiore della prima matrice e' la riga %d",i); }
matrice mat1;
matrice mat2;
matrice matsomma;
int i,j,righe,colonne,righe1,colonne1;
printf ("Inserisci il numero di righe (per la somma devono essere uguali righe e colonne,\n per il prodotto righe
colonne devono essere compatibili)\n");
scanf ("%d",&righe);
scanf ("%d",&righe1);
Somma(mat1,mat2,righe,colonne); else
else
riga_numero(mat1,righe,colonne);}
ESERCIZIO 4
Rappresentare i numeri 75 e -82 in modulo e segno e in complemento a due. Effettuare la somma due numeri nelle
due rappresentazioni. (max 2 pagine).
37/2=1
18/2=0
9/2=1 4/2=0
2/2=0
1⁄2=1
75= 0 1 0 0 1 0 1 1
82/2=0
41/2=1
20/2=0
10/2=0
5/2=1
2/2=0
1⁄2=1
-82= 1 1 0 1 0 0 1 0
La somma tra i due numeri in modulo e segno si ottiene facendo la differenza tra 82 e 75, poiché 82>75, il numero
avrà segno negativo.
82 01010010- 75 01001011=
00000111
Il segno è determinato dal numero in modulo più grande, in questo caso -. 1000111= -7. In complemento a due:
Per 75 otteniamo:
75= 0 1 0 0 1 0 1 1
Per -82:
10101101
A questo sommiamo 1:
1 0 1 0 1 1 0 1+ 0 0 0 0 0 0 0 1=
10101110.
0 1 0 0 1 0 1 1+ 1 0 1 0 1 1 1 0=
1 1 1 1 1 0 0 1, ora poiché abbiamo ottenuto un risultato negativo, dobbiamo sottrarre 1 e poi complementare il
numero trovato.
1 1 1 1 1 0 0 1- 0 0 0 0 0 0 0 1=
1 1 1 1 1 0 0 0, da cui complementando si ottiene: 00000111=7, ricordando che il numero è negativo ottengo 75-82=-
7.
ESERCIZIO 5
Spiegare cosa si intende per ingegneria del software e quali contributi apporta al processo di sviluppo del software
Uno dei problemi più importanti della programmazione è che non esiste attualmente nessun programma che sia in
grado di scrivere algoritmi e nessun programma che sia in grado di verificare la correttezza o meno di un altro
programma. Non si può quindi dimostrare che un programma sia corretto, ma si provano il maggior numero di casi di
test possibili e si verifica il funzionamento, in questo modo si riesce a dare però solo un’affidabilità probabile del
programma. Dal momento che la scrittura degli algoritmi è un’attività molto costosa, che i programmi devono essere
testati e modificati più volte da diversi programmatori, si seguono delle metodologie e tecniche predefinite di
manutenzione e progettazione del software racchiuse nell’area dell’ingegneria informatica nota come “Ingegneria del
software”. I requisiti che deve soddisfare un software per poter essere prodotto su scala industriale sono: un buon
livello qualitativo del programma in rapporto ai costi di produzione, una produttività medio-alta e l’impiego di
personale il meno specializzato possibile, poiché non risulta conveniente avere programmi che possano essere
programmati solo da poche persone, le quali diventano indispensabili e costose. La progettazione del software avviene
a cascata e si divide in fasi, si parte dall’analisi del problema da risolvere esposto in una modalità comprensibile
all’informatico, si passa poi alla programmazione, successivamente alla verifica del programma e infine alla
manutenzione del programma stesso. Tutti questi step vengono eseguiti in questo ordine, ma nel caso si riscontri un
errore durante una delle fasi, si ritorna al punto precedente. Si nota, in particolare, la divisione tra analisi del
problema, ovvero cosa voglio ottenere, e la programmazione, ovvero come risolvo il problema, questa divisione è
dovuta a vari fattori, tra cui il fatto che scelte anticipate di progetto potrebbero modificare l’analisi sui requisiti e su
tutte le informazioni necessarie per comprendere un problema.
ESERCIZIO 6
Spiegare cosa si intende per Debug di un programma. Mostrare con un esempio le modalita' di debug discusse nel
corso.
Il debug è l’operazione che consiste nell’individuare e correggere gli errori semantici, ovvero quegli errori che non
vengono notati dal calcolatore in quanto viene rispettata la sintattica del linguaggio, ma che ci fanno ottenere risultati
che per l’essere umano non hanno senso. Una volta notato l’errore, la procedura di individuazione parte dal
rintracciare il blocco di codice in cui esso è presumibilmente presente, aiutandosi anche con l’ultima istruzione di
input/output funzionante. Una volta individuato il blocco di codice si passa al tracciamento delle variabili, ovvero allo
studio delle evoluzioni dei valori che la variabile contiene durante l’esecuzione del programma.
Il tracing delle variabili, ossia uno schema effettuato su carta nel quale si scrive riga di codice per riga di codice
l’evoluzione delle variabili nel tempo, in modo da trovare quella che si comporta in modo anomalo;
Inserire dei “printf” nei punti ritenuti critici, in modo da capire se in fase
Utilizzare il debug predefinito del calcolatore, utilizzando dei breakpoint, cioè delle linee di codice evidenziate a
partire dalle quali si fa eseguire il programma per individuare gli errori;
int main(){
int a=5;
int b=4;
int somma;
il valore che mi aspetto di somma è 7, invece ottengo 1, devo quindi andare a tracciare le variabili per capire in quale
riga si trovi l’errore
c Indef Indef
riga 4
Riga1
indef
Riga2
Riga3 5 4 Indef
Riga4 5 4 1
A questo punto mi fermo, ho capito che l’errore si trova nella riga 4, in cui al posto di avere una somma ho una
differenza.
ESERCIZIO 7
Scrivere un programma C che implementi uno stack di caratteri utilizzando l'operatore array e metta
a disposizione le relative funzioni Push e Pop che interagiscono con l'utente tramite tastiera e video.
#include <stdio.h>
#include <iostream>
#include <cstdlib>
#include <cstring>
int indice;
if (decisione==1){ *stackpointer=*stackpointer+1;
if(*stackpointer==100)
else{
fflush(stdin);
scanf("%c",&stack[*stackpointer]); }
}
if (decisione==2){
else{
printf("%c\n",stack[*stackpointer]);
*stackpointer=*stackpointer-1; }
}}
int main(){
char stack[100];
int stackpointer=-1;
int numero;
do{
printf("premi 1 se vuoi fare un azione di POP,2 se vuoi fare un azione di PUSH,0 se vuoi chiudere il programma\n");
scanf("%d",&numero); if (numero==1||numero==2)
funzionePOPPUSH(stack,&stackpointer,numero); }
while(numero!=0);
system("PAUSE");}
ESERCIZIO 8
Spiegare:
a) come è rappresentata una parola (stringa di caratteri) all'interno del calcolatore. b) come può essere rappresentata
una fotografia digitale all'interno del calcolatore.
La rappresentazione delle parole, ovvero stringhe di simboli, nel calcolatore avviene tramite array di stringhe di bit, in
cui ad ogni simbolo viene associato una rappresentazione in binario. Tale associazione viene fatto attraverso un
codice, tra quelli più utilizzati in passato vi è il codice ASCII, pubblicato dall’American National Standards Institute, in
cui venivano associati ai simboli dei numeri nella notazione esadecimale scrivibili con combinazioni di 8 bit, in modo da
poter inserire in ogni cella di memoria una lettera di una parola. Dal momento che si avevano a disposizione 8 bit per
un simbolo, si potevano scrivere 232 simboli. La scelta della numerazione esadecimale è dovuta alla facilità di
passaggio al codice binario. Dato il numero di simboli, con il codice ASCII si poteva codificare solo l’alfabeto latino,
quindi nel momento in cui il computer è diventato di utilizzo globale ed era diventato necessario inserire altri alfabeti,
è stato implementato col codice UNICODE, che rappresenta una convenzione internazionale. In questo caso la codifica
è a 32 bit. Un altro dato da rappresentare è quello multimediale, dove abbiamo grandezze analogiche che devono
essere discretizzate. La discretizzazione avviene tramite una fase di campionamento, in cui viene preso il valore di un
segnale ogni certo intervallo di tempo, detto frequenza di campionamento, abbiamo poi la fase di quantizzazione, in
cui i valori campionati vengono valutati in base a degli intervalli stabiliti e tutti i valori che si trovano nella stessa fascia
vengono identificati con lo stesso segnale digitale. le immagini rappresentano una grandezza analogica, in quanto
sono formati da un insieme continuo di informazioni, ovvero luce e colore, che quindi necessitano di essere
discretizzati per poter essere rappresentati sul calcolatore. Una prima codifica si ha con le BITMAP, con la quale si
suddivide l’immagine in un reticolato di punti, detti pixel, e ad ogni pixel viene associato un valore binario (0 o 1) che
rappresenta la presenza o meno della luce, in modo da ottenere una rappresentazione in bianco e nero. Andando ad
implementare questo sistema, ovvero considerando per ogni punto una stringa di bit, è possibile ottenere per ogni
cella una combinazione di rosso, blu e verde, in modo che il colore possa essere identificato col modello RGB. Questo
avviene usando per ogni colore base 8 bit, in modo da ottenere per ogni colore una rappresentazione a 24 bit, con la
possibilità di rappresentare circa 16 milioni di colori diversi. La rappresentazione delle immagini può essere salvata in
file di formato diverso, infatti esistono dei formati di compressione per ridurre lo spazio necessario di memoria,
questo provoca però una perdita di informazioni. Un altro modo per rappresentare le immagini è dato dalle immagini
vettoriali, in cui si va ad associare a delle parti delle immagini delle figure geometriche, in questo modo occupo meno
spazio di memoria e non perdo informazioni, ma questa rappresentazione non è facile da ottenere poiché non sempre
è possibile scomporre un’immagine reale in elementi primitivi.