Il 0% ha trovato utile questo documento (0 voti)
19 visualizzazioni25 pagine

Progetto D'esame

Copyright
© © All Rights Reserved
Per noi i diritti sui contenuti sono una cosa seria. Se sospetti che questo contenuto sia tuo, rivendicalo qui.
Formati disponibili
Scarica in formato DOCX, PDF, TXT o leggi online su Scribd
Il 0% ha trovato utile questo documento (0 voti)
19 visualizzazioni25 pagine

Progetto D'esame

Copyright
© © All Rights Reserved
Per noi i diritti sui contenuti sono una cosa seria. Se sospetti che questo contenuto sia tuo, rivendicalo qui.
Formati disponibili
Scarica in formato DOCX, PDF, TXT o leggi online su Scribd
Sei sulla pagina 1/ 25

PROGETTO D’ESAME DI ‘ ELEMENTI DI

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.

Gruppo composto dagli studenti:

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.

Senza utilizzare il costrutto funzionale.

PRIMO PROGRAMMA

#include <math.h>

#include <iostream>

#include <cstdlib>

#include <cstring>

#include <stdio.h>

// inserisco le librerie

Int main () {//blocco di codice principale

//(Fase input)

//definisco le variabili di tipo intero

Int input;//è il contenitore dove vengono inseriti l’input dell’utente

Int prodotto=1;// è la variabile che utilizzo per tenere traccia dei prodotti
//la dichiaro uguale a 1 dato che è l’elemento neutro del prodotto

Int semaforo=2;//questa variabile la utilizzo come semaforo, la inizializzo a 2 (verde)

//(Fase manipolazione)

Printf(“inserire dei numeri interi\n”);

printf(“il programma ti restiituira’ il prodotto dei numeri inseriti\n”);

//vengono stampate a video le modalità di funzionamento del programma all’utente

Do{ //sfrutto un costrutto iterativo di tipo do while, il ciclo viene ripetuto finchè è vera la condizione del
while

Printf(“inserendo 36 il programma ti dara’ il prodotto \n”);

printf (“inserendo 11 il programma ignorera’ tutti i numeri finche’ non inserirai 22\n”);

//vengono stampate a video le istruzioni da comunicare all’utente

Scanf (“%d”,&input);//sfrutto la funzione scanf per permettere all’utente di interagire con il


programma e fargli inserire i numeri

//vado ad utilizzare il costrutto condizionale if per verificare il valore immesso dall’utente

If (input==11) {semaforo=1;};

//se il valore immesso è pari a 11 il semaforo assume valore 1 (rosso)

//dunque il prodotto non considera tutti i successivi numeri immesso

If (input==22){semaforo=2;};//se il valore inserito è pari a 22 il semoforo o resta 2 (verde)

//o se precedentemente era stato immesso 11(rosso) ritorna a 2 (verde)

If (semaforo==2 && input!=36 && input!=22&& input!=11)

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

Printf(“il prodotto e’%d\n”,prodotto);//viene stampato a video il risultato tramite il


segnaposto %d che è collegato alla variabile prodotto

System (“PAUSE”);//utilizzo la chiamata di sistema, che mi permette di mettere in pausa il programma

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 Funzioneprodotto (){//dichiarazione della funzione

//definisco le variabili di tipo intero

Int input;//è il contenitore dove vengono inseriti l’input dell’utente

Int prodotto=1;// è la variabile che utilizzo per tenere traccia dei prodotti

//la dichiaro uguale a 1 dato che è l’elemento neutro del prodotto

Int semaforo=2;//questa variabile la utilizzo come semaforo, la inizializzo a 2 (verde)

Printf(“inserire dei numeri interi\n”);

printf(“il programma ti restiituira’ il prodotto dei numeri inseriti\n”);

//vengono stampate a video le modalità di funzionamento del programma all’utente

Do{ //sfrutto un costrutto iterativo di tipo do while, il ciclo viene ripetuto finchè è vera la
condizione del while

Printf(“inserendo 36 il programma ti dara’ il prodotto \n”);

printf (“inserendo 11 il programma ignorera’ tutti i numeri finche’ non inserirai 22\n”);

//vengono stampate a video le istruzioni da comunicare all’utente

Scanf (“%d”,&input);//sfrutto la funzione scanf per permettere all’utente di interagire con il


programma e fargli inserire i numeri

//vado ad utilizzare il costrutto condizionale if per verificare il valore immesso


dall’utente

If (input==11) {semaforo=1;}//se il valore immesso è pari a 11 il semaforo assume valore 1 (rosso)

//dunque il prodotto non considera tutti i successivi numeri immessi

If (input==22){semaforo=2;};//se il valore inserito è pari a 22 il semoforo o resta 2 (verde)

//o se precedentemente era stato immesso 11(rosso) ritorna a 2 (verde)

If (semaforo==2 && input!=36 && input!=22&& input!=11)


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

Return(prodotto);//ritorno del valore finale ottenuto

Int main (){//blocco di codice principale

Int risultato;//dichiarazione della variabile risultato di tipo intero

Risultato=Funzioneprodotto();//chiamata della funzione all’interno del programma

Printf(“il prodotto e’%d\n”,risultato);//viene stampato a video il risultato tramite il segnaposto %d


che è collegato alla variabile prodotto

System (“PAUSE”);//utilizzo la chiamata di sistema, che mi permette di mettere in pausa il


programma

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:

Cosa è un linguaggio di programmazione?

Cosa sono i costrutti in un linguaggio di programmazione?

Cosa sono le istruzioni iterative o ciclicle?

Quale differenza esiste tra il Repeat ... Until e il Do ... While?

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 si intende per funzione nel linguaggio C?

Cosa sono la dichiarazione di una funzione e il corpo di una funzione? A cosa servono i parametri di una funzione?

Quali modalita' di passaggio dei parametri esistono?

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?

Come è memorizzato nella memoria del calcolatore 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

caratteri 'do' e 're' occorrono nella stringa inserita.

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 miafunzione(char valore){ // definisco una funzione di tipo char

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

return (valutazione); // risultato finale della funzione

int main(){

char stringa_inserita[100];

char stringa_visualizzata[100];

char variabile;

int ripetizioni=0;
int contatore=0;

int contatore2=0;

printf("inserisci la stringa \n");

scanf("%s",stringa_inserita); //chiede all'utente la frase e la inserisco nella variabile stringa inserita

while(stringa_inserita[contatore]!='\0'){ //eseguo un ciclo iterativo che scansiona ogni carattere della stringa

variabile=stringa_inserita[contatore];

variabile=miafunzione(variabile); //controlliamo il carattere tramite la funzione precedentemente definita

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

printf("le ripetizioni sono %d\n",ripetizioni);


printf("la stringa senza do e re e'%s\n",stringa_visualizzata);

system("PAUSE");

return 0;

SECONDA ESERCITAZIONE
ESERCIZIO 1
#include <stdio.h>

#include <iostream>

#include <cstdlib>

#include <cstring>

typedef struct{

}impegno;

funzione_scambia(impegno agenda[],int posmin ,int i ){

char scambio[20];

int numero;

strcpy(scambio,agenda[i].sintesi);strcpy(agenda[i].sintesi,agenda[posmin].sintesi);strcpy(agenda[posmin].sintesi,scam
bio);

numero=agenda[posmin].ora; agenda[posmin].ora=agenda[i].ora; agenda[i].ora=numero;


numero=agenda[posmin].data; agenda[posmin].data=agenda[i].data; agenda[i].data=numero;
numero=agenda[posmin].mese; agenda[posmin].mese=agenda[i].mese; agenda[i].mese=numero;

int ora;

int mese;

char sintesi[20]; int data;

funzione_riempimento(impegno agenda[],int n){

int i; int j; int numero; char testo[20];

for(i=0;i<n;i++){

printf("inserisci il %d impegno \n",i+1);

printf("inserisci il mese \n "); scanf("%d",&numero); agenda[i].mese=numero;

printf("inserisci la data \n"); scanf("%d",&numero);

agenda[i].data=numero; printf("inserisci ora \n"); scanf("%d",&numero); agenda[i].ora=numero;

printf("inserisci una breve sintesi \n");

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)

funzione_ordinamento(impegno agenda[],int n){

int i,j;

char scambio[20]; int posmin;

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

for(j=i+1;j<n;j++){ if(agenda[j].mese==agenda[posmin].mese &&

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 main(){ // ho implementato l'agenda come un array di impegni int n;

int i;

impegno miaagenda[400];

printf("inserisci il numero di impegni \n");


scanf("%d",&n);

funzione_riempimento(miaagenda,n);

funzione_ordinamento(miaagenda,n);

printf("gli appuntamenti ordinati sono i seguenti \n");

for(i=0;i<n;i++){

printf("%d appuntamento il mese %d giorno %d alle ore %d \


n",i,miaagenda[i].mese,miaagenda[i].data,miaagenda[i].ora);

printf("sintesi: %s\n",miaagenda[i].sintesi); }

system("PAUSE"); }

Esercizio 2

Spiegare cosa si intende per macchina di Turing e macchina di Turing universale.

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:

 scrittura di un nuovo simbolo nella cella,


 posizionamento in un nuovo stato,
 spostamento di una cella verso destra o verso sinistra.
La macchina di Turing ha permesso di dimostrare che alcuni problemi non ammettono nessuna soluzione
generale calcolabile. La tesi di Church-Turing afferma infatti che, se esiste un algoritmo per eseguire un
compito che manipola simboli, allora esiste una macchina di Turing in grado di eseguire quel compito e
definire i limiti della computabilità: se per un problema non esiste una macchina di Turing in grado di
risolverlo allora il problema si dice incomputabile o irrisolvibile.

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

Le variabili possono essere di due tipi:

 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".

La dichiarazione consente di raggiungere due fondamentali obiettivi: da un lato, con la dichiarazione


tipizzata si specifica il tipo della variabile, cosicché il compilatore è in grado di verificare eventuali errori
semantici presenti all'interno di un programma sintatticamente corretto. Dall'altro, viene valutata e definita
la quantità di memoria destinata, in fase di esecuzione, a contenere i dati a cui la variabile si riferisce.

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:

a = b + c; // a, b, c sono variabili di tipo int, b contiene il valore 3 e c contiene il valore 4

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

struct{ // ho implementato la coda come array circolare

char nome[20];

}registrazione;

funzione_chiamata_sportello(registrazione sportello[],int *testa,int coda,int max_element){ char nome_chiamato[20];

if(*testa==coda)

else{

}}

printf("errore, non c'e'nussuna persona in attesa\n");

strcpy(nome_chiamato,sportello[*testa].nome);

printf("la persona allo sportello e'\n %s",nome_chiamato); *testa=*testa+1;

*testa=(*testa)%max_element;

funzione_aggiungi_in_coda(registrazione sportello[],int *coda,int testa,int max_element){ char nome[20];

if((*coda+1)%max_element==testa)

printf("errore, la coda e'piena \n"); else{

printf("inserisci il nome della persona da inserire in coda \n");

scanf("%s",nome);

strcpy(sportello[*coda].nome,nome);

*coda=(*coda)+1;

*coda=(*coda)%max_element; }
}

int main(){

int max_element;

printf("INSERISCI il numero di elementi massimo della coda \n");

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("la lista delle persone in attesa e' la seguente:\n");

for (i=testa; i!=coda; i=(i+1)%max_element){

printf( "%s ",sportello[i].nome);}

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;

inserire(elemento dizionario[], int *k){

int pos;

char el[10],desc[10],temp[10];

printf("\n digita elemento:\n");

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

if(strcmp(el,dizionario[0].elemento)<0){ // se il nuovo è più piccolo lo metto a sx di tutto.

strcpy(dizionario[1].elemento,dizionario[0].elemento); // qui sta considerando il caso in cui


inserirsco secondo elemento.

strcpy(dizionario[1].descrizione,dizionario[0].descrizione); // il nuovo elemento va a sx del


vecchio. CREA BUCO.

strcpy(dizionario[0].elemento,el);

strcpy(dizionario[0].descrizione,desc);

}else { // se è più piccolo lo metto dopo 0.

strcpy(dizionario[1].elemento,el);

if(*k>1){

if (strcmp(el,dizionario[0].elemento)<0){ // elemtento nuovo minore del primo che


è il min. elem è il nuovo min.

for(int j=*k-1;j>=0;j--){

strcpy(dizionario[j+1].elemento,dizionario[j].elemento);

strcpy(dizionario[j+1].descrizione,dizionario[j].descrizione); // sposta tutti gli elementi


a dx creando un buco in pos 0.

// elem in 1 lo metti in 2 poi elem in 0 lo metti in 1 e ho 0 libero.


}

strcpy(dizionario[0].elemento,el);

strcpy(dizionario[0].descrizione,desc); // mette nel buco in pos 0 il nuovo elemento.

if(strcmp(el,dizionario[*k-1].elemento)>0){ // se il nuovo è maggiore dell'ultimo elemento


che è max, allora lui è il max.

strcpy(dizionario[*k].elemento,el);// lo metto dopo il vecchio max k-1.

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

pos=i+1; // ho la sua posizione e ora...

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

rimuovere(elemento dizionario[], int *k){

char el[10];

int i,j,pos;

if(*k==0){

printf("\ndizionario vuoto, non si può rimuovere nulla\n");

if(*k>0){

printf("\ninserisci l'elemento da eliminare\n");


scanf("%s",el);

for(i=0;i<*k;i++){

if(strcmp(el,dizionario[i].elemento)==0){// cerca la posizione dell'elemento che


voglio eliminare

pos=i;

for(j=pos;j<*k-1;j++){

strcpy(dizionario[j].elemento,dizionario[j+1].elemento); // va copiare l'elemento


successivo al posto da quello da eliminare spostando tutto verso destra chiudendo il buco.

strcpy(dizionario[j].descrizione,dizionario[j+1].descrizione);

stampa(elemento dizionario[], int k){

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

printf("inserire:\n0 per inserire elemento;\n1 per rimuovere elemento;\n2 per visualizzare


e chiudere");

scanf("%d",&i);

if(i==0){

inserire(dizionario, &k);

k=k+1; // conteggia elementi.

stampa(dizionario,k);

}
if(i==1){

rimuovere(dizionario, &k);

k=k-1; // detrae numero elementi.

stampa(dizionario,k);

stampa(dizionario,k);

system("PAUSE");

return(0);

ESERCIZIO 3
#include <stdio.h>

#include <iostream>

#include <cstdlib>

#include <cstring>

typedef float vettore [10];

typedef vettore matrice[10];

void Scrittura1 (matrice mat1,int righe,int colonne){ int i,j;

float numero; for(i=0;i<righe;i++){

}}

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 ("\nStampo la matrice \n"); for (i=0;i<righe;i++){

printf("\n"); for(j=0;j<colonne;j++){

printf("\t%f", mat1 [i][j]); }

} printf("\n"); }

void Somma(matrice mat1,matrice mat2,int righe3,int colonne3){ int i,j,righe,colonne;

FILE *fp_in;

matrice matricesomma; for (i=0;i<righe3;i++){

}
for(j=0;j<colonne3;j++){

matricesomma[i][j]=(mat1[i][j])+(mat2[i][j]); }

printf("\nStampo la matrice somma:\n"); fp_in=fopen("Matrice_somma.txt","w");

for(i=0;i<righe3;i++){

printf("\n");

fprintf(fp_in,"\n");

for(j=0;j<colonne3;j++){

}}

fclose(fp_in);

printf("\t%f",matricesomma [i][j]); fprintf(fp_in,"\t%f ",matricesomma [i][j]);

void Prodotto(matrice mat1,matrice mat2,int righe4,int colonne4,int colonne5){ matrice prodotto;

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("\nStampo la matrice prodotto\n"); for (i=0;i<righe4;i++){

printf ("\n");

for (j=0; j<colonne5;j++){

printf ("\t%f”, prodotto[i][j]); }

void riga_numero (matrice mat1, int righe, int colonne) {

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

int main (){

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

printf ("Inserisci il numero di colonne\n");

scanf ("%d",&colonne); Scrittura1(mat1,righe,colonne);

printf ("Inserisci il numero di righe della seconda matrice\n");

scanf ("%d",&righe1);

printf ("Inserisci il numero di colonne della seconda matrice\n");

scanf ("%d",&colonne1); Scrittura1(mat2,righe1,colonne1);

if(righe==righe1 && colonne==colonne1)

Somma(mat1,mat2,righe,colonne); else

printf("\nerrore:sommma impossibile"); if(colonne==righe1)

//le operazioni devono rispettare le regole di calcolo Prodotto(mat1,mat2,righe,colonne,colonne1);

else

printf("errore:matrici non conformabili");

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

In modulo e segno: 75/2=1

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

Il primo 0 rappresenta il segno +;

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

Il primo 1 rappresenta il segno - .

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:

-82= 0 1 0 1 0 0 1 0, devo complementare il numero perché è negativo:

10101101

A questo sommiamo 1:

1 0 1 0 1 1 0 1+ 0 0 0 0 0 0 0 1=

10101110.

Somma tra i numeri in complemento a 2: 75-82=

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.

Per individuare le variabili, esistono 3 modi diversi:

 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

di esecuzione il programma esegue effettivamente quella istruzione;

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

Un esempio di debug si ha nel seguente programma:

int main(){

int a=5;

int b=4;

int somma;

somma=a-b; printf(“%d”,somma);} riga5

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 1 riga 2 riga 3

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>

void funzionePOPPUSH(char stack[],int *stackpointer,int decisione){

int indice;

if (decisione==1){ *stackpointer=*stackpointer+1;

if(*stackpointer==100)

("lo stack è pieno, non posso inserire altri caratteri");

else{

fflush(stdin);

printf("inserisci il carattere \n");

scanf("%c",&stack[*stackpointer]); }

}
if (decisione==2){

if(*stackpointer==-1){ // lo stackpointer non punta a nessun carattere

printf("non ci sono caratteri nello stack \n");}

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.

Potrebbero piacerti anche