Appunti di programmazione

Appunti per il corso universitario di programmazione, riferito al linguaggio ANSI C
Redatti all'università di Venezia

ARGOMENTI

STRUTTURA COMPUTER

DEFINIZIONI

TASTIERA E TIPI

AMBIENTE E MEMORIA

COMANDI

FUNZIONI E PROCEDURE

TIPI DERIVATI

PUNTATORI

STRINGHE

ARRAY DI PUNTATORI

STRUTTURE

TYPEDEF E ENUM

PUNTATORI A PROCEDURE

ALLOCAZIONE DINAMICA

LISTE

ALGORITMI

ERRORI

FUNZIONI E PROCEDURE

<tipo> <identificatore> (<lista parametri>) {

dichiarazioni

istruzioni / comandi

(se tipo != void deve esserci un return e con e i tipo <tipo>)

}


funzioni = procedure che dialogano in uscita = che restituiscono un risultato

dichiarare una funzione = associare un identificatore ad un insieme di istruzioni

  1. procedura / funzione permette di assegnare un identificatore ad una serie di comandi

  2. posso avere procedure semplici (non dialogano con chi le chiama = non variano rispetto a dei parametri) o procedure che si aspettano dei valori e che restituiscono valori


es.

int main (void){

int a = 1;

int b = 2;

printf (“%d”, a);

scrivisepara(); /* scrive *** */

printf (“%d”, b);

scrivisepara();

} /* scrivisepara non riceve nessun input e non restituisce nessun valore*/



void scrivisepara () {

for (a = 1; a < 4; a++) printf (“*”);

printf (“\n”);

}


ecco il funzionamento del programma dal punto di vista ambiente-memoria:


abbiamo due identificatori a in due moduli diversi e separati (funzione e main): non si riferiscono alla stessa variabile ma a due variabili diverse, pertanto quando termina la funzione scrivisepara la parte in rosso sparisce assieme alla sua variabile a (rimane la a del main)


se voglio che vari numero di asterischi


int main (void){

int a = 1;

int b = 2;

int N1, N2;

scanf (“ %d”, &N1);

scanf (“ %d”, &N2);

printf (“%d”, a);

scrivisepara(N1); /* passo il valore della variabile N1 */

printf (“%d”, b);

scrivisepara(N2);

}


void scrivisepara (int k) {

for (a = 1; a < k + 1; a++) printf (“*”);

printf (“\n”);

}


parametri formali (void scrivisepara(N1)) = dichiarati per tipo, numero e ordine nella definizione della funzione (quelli della firma, non del prototipo)

parametri attuali (scrivisepara (N1)) = quelli che vengono passati alla funzione all’atto della chiamata (o invocazione)



il passaggio dei parametri in C avviene sempre e soltanto per valore (esclusi gli array anche se solo da un certo punto di vista)

ciò significa che all’atto dell’invocazione di una funzione ogni parametro formale è inizializzato con il valore del corrispondente parametro attuale



int fibo(int n) {

int x, y, cont, temp;

x = 0;

y = 1;

cont = 1;

for (cont = 1; cont < n – 2; cont++){

temp = y;

y = x + y;

x = temp;

}

return y;

}


int w, l;

l = 55;

w = fibo (l + 1);


int somma (int x, int y){

return (x + y);

}


somma = identificatore legato ad una porzione di memoria (anche nel caso delle procedure)


nei prototipi posso scrivere anche ad es.

int somma (int, int);


albero delle chiamate:



#include <stdio.h>


int y = 5;


void P (int x){

printf(“ %d”, x + y);

}


int main(){

y++:

P(4) ; /* procedura propria */

}

stampa 10


procedura propria = non restituisce nessun valore


#include <stdio.h>


int y = 5;


void P (int x){

printf(“ %d”, x + y);

return 0 ;

}


int main(){

int y = 10 ;

P(4) ;

return 0 ;

}

stampa 9 non 14 poichè si riferisce sempre alla variabile globale


se avessimo dichiarato la y nel main come static, alla fine della procedura non sarebbe stata liberata la memoria dedicata a tale variabile ma la sua visibilità non sarebbe cambiata


scrivere un programma che chieda all’utente un n intero positivo e lo scriva sullo standard output:


void scrivi(int n);


int main(){

int z;

scanf(“%d”, &z);

scrivi(z);

}


iterativo:

void scrivi(int k){

int i;

for (i = -k; i < k, i++) printf(“%d”, i);

}


ricorsivo:

void scrivi(int n){

if (n==0) printf (“0”);

else {

printf(“%d”, -n);

scrivi(n - 1);

printf(“%d”, n);

}

}


programma che disegna un triangolo di asterischi:


iterativo

void scrivilinea(int n){

int k;

for (k = 0 ; k < n ; k++)

printf(“*”);

printf(“\n”);

}


ricorsivo

void scrivilineaR(int n){

if (n == 0) printf(“\n”);

else {

printf(“*”);

scrivilineaR(n - 1);

}

}


per fare il triangolo

iterativo

void stelle (int n){

int h;

for (h = 1; h <= n; h++) scrivilinea(h);

}


ricorsivo

void stelleR (int n){

if (n != 0) {

stelleR(n - 1);

scrivilineaR(n - 1);

}

}


lo svantaggio della ricorsività è la spesa in termini di risorse il vantaggio è la più facile manutenzione e la capacità di risolvere problemi grossi partendo da problemi più piccoli


programma per il fattoriale:


int F (int x){

int res;

if (x < 2) return 1;

res = F(x - 1);

return(res * x) ;

}


int main(){

int a;

scanf(“%d”, &a);

printf(“%d”, F(a));

}


fibonacci ricorsivo:


int fib(int n) {

if (n == 0) return 0;

if (n == 1) return 1;

return (fib(n – 1) + fib(n - 2));

}


il problema è che per calcolare ad esempio il 25 numero di fibonacci servono 25.000 chiamate: si può ovviare in parte a questo evitando di ricalcolare per ogni chiamata della funzione i valori già processati da questa (utilizzando variabili statiche)


MODO ALTERNATIVO DI SCRIVERE PIÙ PROTOTIPI SIMILI

void apri (void), salva (void), chiudi (void);

equivale a:

void apri (void);

void salva (void);

void chiudi (void);

Ritorna sopra | Home page