Laboratorio di Algoritmi e Strutture Dati
Programmazione
in linguaggio C
Prof. Luigi Lamberti
2009
Come NON programmare
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
Y return
R for
e while
I printf
l int
W if
C y=v+111;H(x,v)*y++= *x
H(a,b) R(a=b+11;a<b+89;a++)
s(a) t=scanf("%d",&a)
U Z I
#define Z I("123\
45678\n");H(x,V){putchar(".XO"[*x]); W((x-V)%10==8){x+=2;I("%d\n",(x-V)/101);}}
l V[1600],u,r[]={-1,-11,-10,-9,1,11,10,9},h[]={11,18,81,88},ih[]={22,27,72,77},
bz,lv=60,*x,*y,m,t;S(d,v,f,_,a,b)l*v;{l c=0,*n=v+100,j=d<u-1?a:-9000,w,z,i,g,q=
3-f;W(d>u){R(w=i=0;i<4;i++)w+=(m=v[h[i]])==f?300:m==q?-300:(t=v[ih[i]])==f?-50:
t==q?50:0;Y w;}H(z,0){W(E(v,z,f,100)){c++;w= -S(d+1,n,q,0,-b,-j);W(w>j){g=bz=z;
j=w;W(w>=b||w>=8003)Y w;}}}W(!c){g=0;W(_){H(x,v)c+= *x==f?1:*x==3-f?-1:0;Y c>0?
8000+c:c-8000;}C;j= -S(d+1,n,q,1,-b,-j);}bz=g;Y d>=u-1?j+(c<<3):j;}
main(){R(;t<1600;t+=100)R(m=0;m<100;m++)V[t+m]=m<11||m>88||(m+1)%10<2?3:0;
I("Level:");V[44]=V[55]=1;V[45]=V[54]=2;s(u);e(lv>0){Z
do{I("You:");s(m);}e(!E(V,m,2,0)&&m!=99);
W(m!=99)lv--;W(lv<15&&u<10)u+=2;U("Wait\n");I("Value:%d\n",S(0,V,1,0,-9000,9000
));I("move: %d\n",(lv-=E(V,bz,1,0),bz));}}E(v,z,f,o)l*v;{l*j,q=3-f,g=0,i,w,*k=v
+z;W(*k==0)R(i=7;i>=0;i--){j=k+(w=r[i]);e(*j==q)j+=w;W(*j==f&&j-w!=k){W(!g){g=1
;C;}e(j!=k)*((j-=w)+o)=f;}}Y g;}
Commenti
/*
per i commenti di più linee
*/
// per i commenti fino al termine della riga.
La quantità di commenti deve superare abbondantemente il codice C.
Un altro programmatore deve capire come funziona una routine senza
chiedervi nulla.
I commenti devono essere scritti PRIMA, in modo da formare
l’ossatura su cui verrà scritto il programma, non DOPO!
Inseriremo perciò alcuni righi di spiegazione all’inizio di una funzione
o di un segmento di programma, una descrizione accanto ad ogni
definizione di variabili, qualche chiarimento accanto ad istruzioni
significative.
a = a + 2; // aggiungo 2 alla variabile a
non è un commento ma una scemenza: meglio chiarire il motivo di
questo incremento.
Naming
Il Nome deve richiamare l’uso della variabile e/o della funzione. Per le
variabili è preferibile un sostantivo, per le funzioni (metodi) un
verbo.
C è case-sensitive; l’iniziale maiuscola per i nostri nomi, li distinguerà a
colpo d’occhio dalle parole del C; nomi composti avranno le singole
iniziali maiuscole ( CamelCase ):
int
NumLati;
float AreaCerchio,
MaxValore;
//
descrizione variabile
//
//
descrizione variabile
descrizione variabile
Definizione di tipi e costanti
Utilizzare il #define per i valori costanti; i nomi
vanno scritti in MAIUSCOLO.
#define PIGRECO
#define ESC
3.1415927
27
Sempre in MAIUSCOLO andranno i nomi delle
strutture
typedef struct scheda { char Campo[16];
int Valore;
} SCHEDA;
Nomi di File
Usare nomi corti, senza spazi e senza caratteri speciali o accentati,
per garantisce la compatibilità con altri sistemi operativi e protocolli
di trasferimento (es. Joliet consente al massimo 31 caratteri per i
nomi dei file).
Evitare perciò nomi come:
•Media ponderata con il secondo metodo matematico di Eulero.c
•lino&mariù.c
•prova %4.c
•dque.c
ed usare invece:
•Media_Aritmetica.c
•Primitive_Grafo.c
•Gestione_RS232.c
•Dynamic_Queue.c
Se il file contiene una sola funzione, usare lo stesso nome per il file.
Spaziatura
Inserire qualche rigo vuoto tra le diverse funzioni
e tra le varie sezioni del programma.
Inserire gli spazi tra i nomi e gli operatori
for(i=0,j=N;i<j&&j>=10;i++,j-=3)if(A[i][j])A[i][j]*=i;
diventa
for (i = 0, j = N; i < j && j >= 10; i ++, j -= 3)
if ( A[i][j] ) A[i][j] *= i;
Variabili Globali
L’uso delle variabili globali è fortemente sconsigliato
perché:
 consente a tutte le procedure di accedere ai dati, con
conseguente difficoltà di debug di eventuali errori;
 rende difficoltoso il riutilizzo del codice;
 quasi sempre, impedisce l’uso della ricorsione:
ogni istanza di una funzione ricorsiva deve agire
su un’immagine diversa dei dati.
Allineamento e Indentazione
Allineare le istruzioni, disponendo opportunamente
quelle che sono subordinate ad una condizione o
ad un ciclo, qualche colonna più a destra.
I simboli di BEGIN e END, ovvero { e } devono
essere sempre allineati con l’istruzione cui fa
riferimento il blocco
(if, else, while, do, switch).
Tutte le istruzioni del blocco devono rientrare:
2-3 colonne dovrebbe essere una misura corretta,
purché venga usata sempre la stessa quantità.
Un'indentazione esagerata, lascia poco spazio agli
indispensabili commenti.
Operatori aritmetici
addizione
sottrazione
moltiplicazione
divisione (intera o float)
incremento
decremento
+
*
/
++
--
assegnazione prodotto
=
+=
-=
*=
assegnazione divisione
/=
assegnazione
aggiunta
detrazione
Operatori per i valori Interi
modulo della divisione
Complemento a uno
AND bit a bit
OR bit a bit
EXOR bit a bit
shift a sinistra
shift a destra
assegnazione modulo
assegnazione AND
assegnazione OR
assegnazione EXOR
assegnazione shift a sinistra
assegnazione shift a destra
%
~
&
|
^
<<
>>
%=
&=
|=
^=
<<=
>>=
Operatori Relazionali
uguale
diverso
minore
minore o uguale
maggiore
maggiore o uguale
==
!=
<
<=
>
>=
Operatori Logici
NOT
AND
OR
!
&&
||
Operatore Ternario
?:
L'unico operatore del C che opera su tre parametri, è una forma
sintetica di alcuni casi particolari di if-else:
if (condizione)
var = espressione1
else
var = espressione1
diventa
var = condizione ? espressione1 : espressione2;
Se condizione è un valore non nullo, si esegue l'espressione1,
viceversa si esegue l'espressione2.
L’operatore ternario ?: può essere inserito in una altra espressione.
Ad esempio, per stampare il massimo tra due valori:
printf (“Il massimo è %f \n”, a > b ? a : b );
Precedenze tra gli Operatori
1.
( )
[ ]
->
.
2.
!
~
++
--
+
3.
*
/
%
4.
+
-
5.
<<
6.
<
7.
==
8.
&
9.
^
10.
|
11.
&&
12.
||
13.
? :
14.
=
15.
,
-
*
&
(cast)
sizeof()
>>
<=
>=
>
!=
(AND bit a bit)
+=
-=
*=
/=
%=
&=
^=
|=
<<=
>>=
Procedure
Scrivere molte subroutine che svolgono poche funzioni,
meglio una sola per volta.
Ogni procedura (livello inferiore) fornisce un servizio al
programma chiamante (livello superiore): quest’ultimo non
deve sapere come questa funzione viene svolta.
Ad esempio un programma che deve scambiare dati tra due
PC, utilizzerà un gruppo di funzioni del tipo Connetti /
Scrivi / Leggi / Disconnetti, senza sapere se queste si
appoggiano alla Porta Seriale, alla Parallela o alla Ethernet.
In C è usuale inserire le funzioni DOPO il main, utilizzando il
prototype per la visibilità.
Uscita da una funzione
Nella programmazione strutturata un blocco di istruzioni
deve avere UN punto di inizio e UN punto di uscita.
Questo, ovviamente, vale anche per i sottoprogrammi e il
programma principale.
 Non usare break per interrompere un ciclo
(salvo al termine di un case).
 L’unico return di una funzione deve essere
l’ultima istruzione.
#include <stdio.h>
#define PIGRECO 3.14159
int
Sub1
( float );
/*=================================================
...specifiche del Software, autori, data relase...
---------------------------------------------------*/
int main ( void )
{ float Area,
// ... uso della variabile
Raggio;
// ...
int
i,
// ...
j;
// ...
istruzione;
do
{ istruzione;
if ( condizione ) // motivo della selezione
{ istruzione;
istruzione;
for ( i = 0, j = 100; i < j ; i ++, j -= 13)
{ istruzione;
istruzione;
}
}
else // alternativa
{ istruzione;
istruzione;
Area = Raggio > 0 ? Raggio * Raggio * PIGRECO : 0;
}
}
while (condizione);
}
Main.c
#include <stdio.h>
/*=======================================================
...specifiche della Subroutine, data ultima modifica...
INPUT: parametri di ingresso
OUTPUT: valore di uscita
------------------------------------------------------*/
int Sub1
( float x
// .....
)
{ int k;
// .....
istruzione;
switch ( k )
{ case 1 : istruzione;
istruzione;
break;
case 23: istruzione;
istruzione;
break;
case
4: istruzione;
istruzione;
break;
default: istruzione;
}
return valore;
}
Sub1.c
Controllo dell’Input utente
/*=================================================================
Funzione per la lettura di una stringa in input da tastiera.
Il chiamante deve aver allocato spazio non inferiore a LenMax;
accettati al massimo LenMax-1 caratteri, con il NULL finale;
il LineFeed di chiusura (10) viene eliminato;
i caratteri eccedenti verranno persi.
-----------------------------------------------------------------*/
void LeggiStringa
(
char *Stringa,
int LenMax
)
{
int i;
// puntatore alla stringa
// lunghezza massima spazio allocato
fgets ( Stringa, LenMax, stdin );
i = strlen(Stringa);
// posizione del NULL
i -= 1;
// ultimo carattere
// elimina il LineFeed di chiusura
if (Stringa[i] < 32) Stringa[i] = 0;
fflush ( stdin );
}
// svuota il buffer
Costruzione di un progetto
Se il lavoro è complesso, conviene suddividere tutte le funzioni
in più files .c, ciascuno con il corrispondente file header .h:
Ogni file C contiene una o più funzioni, legate tra loro da un
comune settore del progetto; all’inizio del file occorre citare
l’header delle sotto-funzioni chiamate, con #include.
Il corrispondente file H contiene tutti i prototype delle
funzioni scritte, le dichiarazioni typedef, struct e define.
Non contiene alcuna istruzione eseguibile.
L’ambiente di sviluppo, tramite il file di progetto, provvede a
collegare tutti i file tra loro.
NON bisogna usare gli #include per i file .c
Metacomandi
In alcuni casi, richiamare più volte lo stesso file
header può creare problemi.
In tal caso, incapsulare il file come segue:
#ifndef
#define
. . . . .
. . . . .
. . . . .
#endif
NOME
NOME
. . . .
. . . .
. . . .
Ambiente di lavoro
Per sviluppare un programma in C:
1. Editor
2. Compiler
3. Linker
Le tre funzioni sono solitamente riunite in un
unico Ambiente di Sviluppo.
Useremo il wxDev C/C++
scaricabile gratuitamente
Installazione
Installare wxdevcpp_6.10.2_setup.exe
nella cartella
C:\Programmi\Dev-Cpp.
Solo sotto Windows, per emulare alcune
funzioni per la gestione della finestra
di testo, copiare il file libconio.a
nella cartella
C:\Programmi\Dev-Cpp\lib
Le barre degli strumenti possono essere
posizionate in modo diverso a seconda delle
esigenze del programmatore
Ad ogni programma da sviluppare deve
corrispondere un Progetto : in questo modo è più
facile tenere assieme le varie parti di un Job.
 Assegnare la modalità Console
(ovvero l’utilizzo della finestra di testo)
 Scrivere il Nome del progetto
 Indicare il linguaggio C
Salvare il progetto in una cartella di lavoro.
Usare una cartella differente per ogni progetto.
Viene proposto un generico file main.c
da utilizzare come base di lavoro.
Possiamo anche ignorarlo e aggiungere tutti i files del
nostro lavoro già pronti
Per utilizzare le funzioni di gestione della
finestra di testo, occorre aggiungere l’opzione
relativa alla libreria libconio.a
Nelle opzioni del progetto:
selezionare la scheda Opzioni Addizionali
e cliccare su Aggiungi libreria
Cercare ed assegnare il file
C:\Programmi\Dev-Cpp\lib\libconio.a
Aggiungere agli header standard i prototype
della libreria conio
#include <conio.h>
Questo programma permette di leggere i codici
dei tasti ASCII e SPECIALI della tastiera.
Scarica

Norme di Leggibilità nel linguaggio C