back to top

Imparare a programmare in C

Il C nasce dalle esigenze di Dennis Ritchie, ricercatore dei Bell Laboratories, di poter utilizzare un linguaggio che supportasse i tipi di dato per poter continuare lo sviluppo del sistema operativo UNIX.

Tale linguaggio era stato scritto per computer DEC PDP-11 nel 1972, sistemi molto diffusi in quegli anni specie a livello accademico, poi, col passare del tempo e a seguito di molte standardizzazioni, alla fine degli anni ’70 si ottenne una prima forma di quello che oggi chiamiamo "C classico".

Le alte potenzialità offerte da questo nuovo linguaggio portarono ad avere diverse forme di esso per far si che lo si potesse usare su macchine differenti, ciò portò alla inevitabile incompatibilità tra versioni differenti, il che andò a discapito della portabilità (la capacità di poter usare lo stesso codice su macchine con architetture differenti). Soltanto nel 1990 vi fu la pubblicazione di uno standard definitivo del linguaggio C, noto come ANSI/ISO 9899:1990. Tale standardizzazione ha reso il C un linguaggio libero da qualsiasi tipo di vincolo hardware e quindi tutte le applicazioni che scriveremo saranno eseguibili su qualsiasi macchina praticamente senza apportare modifiche al codice ma semplicemente ricompilando il codice scritto usando il compilatore adatto.

Il C sostanzialmente è un linguaggio di tipo procedurale il che significa che esso esegue una serie di azioni una dopo l’altra, a meno che tale flusso non venga deviato mediante istruzioni condizionali o istruzioni d’iterazione.

Altra caratteristica importante è quella della programmazione strutturata ovvero della possibilità di poter suddividere il nostro problema in parti più piccole, dette moduli, di modo che sia più semplice comprendere, correggere e ri-usare il nostro codice.

Per poter realizzare i nostri programmi avremo bisogno di imparare il keyset o meglio le parole riservate che il C mette a disposizione per effettuare determinate azioni. Tale insieme non è molto vasto ma ciò non deve far pensare che poche parole riservate non permettano di effettuare tutto ciò che abbiamo in mente… tutto sta ad impararne il corretto uso.

Nel corso della guida impareremo come codificare un programma C e come compilarlo.

Editor e ambiente di sviluppo

Per utilizzare il C abbiamo bisogno di un compilatore. Tale strumento software non è altro che il tramite tra il nostro codice e il linguaggio macchina del nostro calcolatore, in sintesi esso si occupa della traduzione da alto livello (il codice scritto in C) a quella di basso livello (linguaggio macchina).

Esistono molti compilatori per il C per qualsiasi sistema operativo da Windows a Linux. La scelta di uno in particolare è ininfluente sui programmi che scriveremo.

Siccome tale guida è destinata a chi sta muovendo i primi passi nel mondo della programmazione, noi useremo un compilatore freeware il Dev-C++ disponibile sul sito della casa sviluppatrice: http://www.bloodshed.net.

START -> TUTTI I PROGRAMMI -> Blodshed Dev-C++ -> Dev-C++

Una volta lanciato dovrebbe apparire questa schermata:

A questo punto testiamo che tutto funzioni correttamente:

Clicchiamo su File -> Nuovo -> File Sorgente

e scriviamo all’interno della finestra il seguente codice:

/* Test per la corretta installazione del compilatore*/

#include <stdio.h>
#include <stdlib.h>
int main()
{
    printf( "Hello, world! n"); 
    system ("pause");
    return 0;
}

Al momento non ci preoccuperemo di tutto ciò che abbiamo scritto ma andremo a cliccare sull’icona compila ed esegui:

da sinistra a destra sono:

  • compila
  • esegui
  • compila ed esugui
  • ricostruisci
  • debug

Fatto ciò, se tutto è andato a buon fine, avremo come risultato la seguente schermata:

A questo punto abbiamo tutti gli strumenti per imparare a programmare in C!

Il primo programma

Passiamo ora ad un primo esempio, il codice sotto riportato effettua la moltiplicazione fra due numeri interi.

/* Semplice programma per effettuare la moltiplicazione */

#include<stdio.h>
#include<stdlib.h>

//dichiarazione di variabili
int a, b, c;

int main()
{
    // richiesta inserimento primo fattore
    printf("inserisci un numero intero: "); 

    // acquisizione primo fattore
    scanf("%d", &a); 

    // richiesta inserimento secondo fattore
    printf("inserisci un altro numero intero: "); 

    // acquisizione secondo fattore
    scanf("%d", &b);

    // assegnazione a c del prodotto fra a e b
    c = a * b;

    // stampa del risultato
    printf("il risultato di %d
           moltiplicato %d e' %d n", a, b, c);

    system("PAUSE");
    return 0;
}

Esaminiamo ora a fondo tale codice: tutto ciò che è contenuto fra la coppia /**/ è solo testo di commento – nel nostro caso particolare è tutto su una sola linea – quindi tutto ciò che scriveremo all’interno di tale coppia sarà ignorato dal compilatore.

#include <…..> è la direttiva di inclusione serve per inserire all’interno del nostro codice le librerie(o header file) di cui abbiamo bisogno. Tali librerie sono scritte, generalmente, dai programmatori del compilatore e contengono funzioni da noi usate come ad esempio la printf(…) la quale è specificata all’interno del header file stdio.h.

int a,b,c significa che noi vogliamo dichiarare tre variabili rispettivamente a, b e c le quali appartengono al tipo INTERO(int). Gli altri tipi di variabili saranno specificati in seguito nel capitolo 4.

// rappresenta il commento però su una sola linea!

int main() {….} – all’interno di queste parentesi graffe va scritto il cuore del nostro programma, l’int davanti alla parola main sta a significare che tale funzione (main) restituirà un parametro intero… sveleremo a breve il significato di tutto ciò.

printf("text") tale funzione non fa altro che mandare in output sul nostro monitor il testo che c’è scritto fra doppi apici.

scanf("%d",&a) – tutto ciò non significa altro che: accogli il dato in input da tastiera e memorizzalo nella variabile a. Il fatto che sia un dato di input è specificato dall’uso tra doppi apici del %d, il fatto che debba essere memorizzato in a è dato dalla presenza di &a dopo la virgola.

c = a * b non fa altro che moltiplicare a * b e porre il risultato in c. Ricorda che in C le associazioni sono sempre da destra verso sinistra ovvero poni quello che c’è a destra dell’uguale nella variabile di sinistra.

printf("il risultato di %d moltiplicato %d e’ %d n", a, b, c) – il significato di questa istruzione può sembrare a prima vista impossibile da decifrare ma ragionando su quel che abbiamo già visto il tutto diventa molto semplice: tutto ciò che è fra doppi apici deve essere stampato a video i %d indicano la presenza di un dato e n è semplicemente il carattere terminatore di riga(il classico INVIO per capirci meglio), la domanda potrebbe essere: "ma cosa sono quelle lettere dopo la virgola?" Nulla di più facile! sono le variabili i cui valori verranno stampati al posto dei %d in ordine di precedenza. Al primo %d verrà sostituito il valore di a, al secondo il valore di b ed al terzo quello di c.

system("PAUSE") è un altra direttiva al compilatore che indica di sospendere l’esecuzione del programma fino a che non viene premuto un tasto e fa apparire la frase "Premere un tasto per continuare…".

return 0 significa che il main è stato eseguito correttamente senza errori.

Dulcis in fundo vi sarete sicuramente accorti che quasi tutte le righe del codice finisco con un punto e virgola ;… ebbene la mancanza di questi simboli è l’errore più comune di chi si avvicina al C… dunque attenzione!!!! Esso non va messo solo dopo gli #include<…> e dopo una funzione come nel caso di int main() in quanto dopo di essa vanno le parentesi graffe… a proposito per chi non lo sapesse le parentesi graffe si stampano con: SHIFT+CTRL+ALT+[ oppure ].

Variabili e costanti

Il C presenta due modi di conservare i dati: le variabili e le costanti. Come facilmente intuibile le variabili sono atte a contenere informazioni che variano nel tempo mentre il valore di una costante resta lo stesso per tutto il tempo dell’esecuzione di un programma.

In termini un pò più tecnici una variabile è una locazione di memoria a cui è stata assegnato un nome con il quale si ha l’accesso al valore della variabile. Possiamo assegnare alle variabili quasi tutti i nomi che ci vengono in mente purchè essi NON inizino per un numero, NON contengano il carattere # e non siano parole riservate dal C (troverete un appendice con tutte le parole riservate del C). E’ importante sapere che il C è case sensitive ciò significa che le variabili pippo, PIPPO e Pippo sono tre differenti variabili quindi fate attenzione a come nominate le vostre variabili cercate di usare una vostra convenzione, ad esempio scrivete sempre le variabili in minuscolo…

Abbiamo accennato in precedenza a tipi di variabili, tali tipi vengono usati per definire cosa andremo a memorizzare in una variabile e per effettuare dei controlli sui dati immessi. Ecco tutti i tipi, i loro identificatori e il range entro cui possono variare i valori delle variabili:

Tipo di variable Keyword Bytes Occupati Range
Carattere char 1 -128 a 127
Intero int 2 -32768 a 32767
Intero corto short 2 -32768 a 32767
Intero lungo long 4 -2.147.483.648 a 2.147.438.647
Carattere senza segno unsigned char 1 0 a 255
Intero senza segno unsigned int 2 0 a65535
Intero senza segno corto unsigned short 2 0 a 65535
Intero senza segno lungo unsigned long 4 0 a 4.294.967.295
Virgola mobile singola precisione float 4 1.2E-38 a 3.4E381
Virgola mobile doppia precisione double 8 2.2E-308 a 1.8E3082

Nota: la E sta per 10 elevato a.

Ora grazie a questa tabella possiamo risparmiare della memoria quando sappiamo che un intero non supera il valore di 127 useremo uno short se sappiamo che esso non è negativo gli attribuiremo un tipo unsigned int e così via.

Facciamo ora un esempio di dichiarazione di variabili e vediamo l’uso della funzione sizeof(nomeVariabile) la quale ci restituisce la dimensione della variabile:

#include <stdio.h> 
#include <stdlib.h>

//da qui parte la dichiarazione
//delle variabili
char a;
int b;
short c;
long d;
unsigned char e;
unsigned int f;
unsigned short g;
unsigned long h;
float i;
double l;

int m = 55; //significa che m assume il valore 55

int main()
{
printf( "n a occupa %d bytes", sizeof(a));
printf( "n b occupa %d bytes", sizeof(b));
printf( "n c occupa %d bytes", sizeof(c));
printf( "n d occupa %d bytes", sizeof(d));
printf( "n e occupa %d bytes", sizeof(e));
printf( "n f occupa %d bytes", sizeof(f));
printf( "n g occupa %d bytes", sizeof(g));
printf( "n h occupa %d bytes", sizeof(h));
printf( "n i occupa %d bytes", sizeof(i));
printf( "n l occupa %d bytesn", sizeof(l));
system("PAUSE");
return 0;
}

Un altro consiglio è quello di usare nomi mnemonici per le variabili grazie ai quali è facilmente intuibile a cosa serva quella data variabile il significato di int somma è sicuramente più chiaro di int xyz.

Passiamo ora alle costanti. Anche esse sono locazioni di memoria dotate di un nome ma al contrario delle variabili esse non sono modificabili. Una costante si può dichiarare in due modi: facendo precedere al tipo della variabile la keyword const oppure utilizzando la direttiva #define vediamo un altro esempio:

#include <stdio.h> 
#include <stdlib.h>

//definizione costante con direttiva
//al preprocessore #define
#define piGreco 3.14

float raggio , circ, area;

//definizione costante mediante keyword const
const float PIGRECO = 3.145;

int main()
{
    //richiesta e inserimento raggio
    //usando il #define
    printf ("Inserire il raggio: ");
    scanf("%g", &raggio);
    //calcola il valore della circonferenza
    circ = 2 * (piGreco * raggio);  
    //stampa il valore calcolato
    printf("circonferenza %g n", circ);
    //calcola il valore dell'area
    //usando il const float
    area = PIGRECO * raggio * raggio;
    printf("area: %g n", area);

    system("PAUSE");
    return 0; 
}

Usare l’uno o l’altro modo non fa molta differenza a livello pratico, essendo questo un corso base li useremo indistintamente.

Per concludere questo capitolo, vediamo quali sono tutti gli specificatori da usare con le funzioni printf() e scanf() a seconda del tipo di variabili usate:

Specificatore Tipo variabile
%c char
%d (%i) int intero con segno
%e (%E) float or double formato esponenziale
%f float or double decimale con segno
%g (%G) float or double decimale con segno
%o int valore ottale senza segno
%p pointer indirizzo di un puntatore (vedremo in seguito)
%s array of char stringa di caratteri
%u int decimale senza segno
%x (%X) int valore esadecimale senza segno

Operatori e istruzioni condizionali

In questa lezione della nostra Guida a C affronteremo gli operatori, cioè quei simboli mediante i quali si possono compiere determinate operazioni all’interno del flusso logico di un programma. Gli operatori possono essere suddivisi nelle seguenti categorie: assegnazione, matematici, relazionali e logici.

L’operatore di assegnazione è l’uguale (=) il suo uso è diverso da quello matematico: x = y significa che il valore di y è assegnato ad x; nel caso in cui y è una funzione allora tale funzione viene calcolata per poi assegnarne il valore ad x.

Gli operatori matematici possono essere di due tipi: unari e binari.

Gli operatori unari sono così detti poichè essi agiscono su un solo operando. Ve ne sono solo due tipi:

Operatore Simbolo Azione Esempio
Incremento ++ Incrementa l’ operando di uno ++x, x++
Decremento Decrementa l’ operando di uno –x, x–

In sintesi ++x equivale a x = x + 1 e –y equivale a y = y -1. Inoltre avrete notato che è possibile usare tali operatori in maniera prefissa o postfissa il che sta a indicare se l’operando va incrementato prima o dopo l’assegnazione.

Nota: per aumentare la leggibilità del vostro codice è consigliato non usare frequentemente tali operatori.

Gli operatori binari sono invece quelli classici della matematica:

Operatore Simbolo Azione Esempio
Addizione + Somma due operandi x + y
Sottrazione Sottrae il secondo operando dal primo x – y
Moltiplicazione * Moltiplica due operandi x * y
Divisione / Divide il primo operando per il secondo x / y
Modulo % Restituisce il resto della divisione del primo operando per il secondo. x % y

Gli operatori relazionali vengono usati per "comparare" le espressioni. Un espressione contenente operatori relazionali ammette due tipi di risposte vero (1) o falso (0). Riassumiamo tali operandi in una tabella:

Operatore Simbolo Domanda Esempio
Uguale == L’ operando 1 è uguale all’operando 2? x == y
Maggiore di > L’ operando 1 è maggiore dell’operando 2? x > y
Minore di < L’ operando 1 è minore dell’ operando 2? x < y
Maggiore o uguale >= L’ operando 1 è maggiore o uguale dell’operando 2? x >= y
Minore o uguale <= L’ operando 1 è minore o uguale dell’operando 2? x <= y
Diverso != L’ operando 1 è diverso dall’operando 2? x != y

La seguente tabella invece esplica l’uso di tali operatori relazionali:

Expressione Come si legge Valutazione
5 == 1 5 è uguale a 1? 0 (falso)
5 > 1 5 è maggiore di 1? 1 (vero)
5 != 1 5 è diverso da 1? 1 (vero)
(5 + 10) == (3 * 5) (5 + 10) è uguale a (3 * 5)? 1 (vero)

Nota: è importante non confondersi con l’operatore di assegnazione (=) e quello di uguaglianza (==)!

Passiamo ora alle istruzioni condizionali, tali istruzioni servono per modificare il flusso sequenziale d’esecuzione di un programma, facendo eseguire un’istruzione più volte oppure nessuna.

Il più semplice dei controlli che possiamo inserire, è l’istruzione if:

if (espressione)
{
isruzione1;
isruzione2;
……………. isruzioneN;
}

Le istruzioni da 1…N saranno eseguite se e solo se l’espressione contenuta nell’if risulta vera. Facciamo un esempio:

/*Esempio d'uso dell'istruzione if*/

#include <stdio.h>
#include <stdlib.h>

int x,y;

int main()
{
//Richiesta inserimento di due interi
printf("Inserisci un numero intero: ");
scanf("%d",&x);    
printf("Inserisci un altro numero intero: ");
scanf("%d",&y);

//stampa a seconda dei valori immessi
//la relazione tra i due numeri
if ( x == y)
    printf("x e' uguale a y n");
if (x > y)
    printf("x e' maggiore di y n");
if (x < y) 
    printf("x e' minore di y n");        

system("PAUSE");
return 0;
}

In tale codice verrà eseguita la printf relativa all’unica espressione relazionale vera contenuta in uno degli if.

Un’estensione dell’if è la clausola else essa va interpretata come un oppure modifichiamo l’esempio precedente:

/*Esempio d'uso dell'istruzione if...else if...else*/

#include <stdio.h>
#include <stdlib.h>

int x,y;

int main()
{
//Richiesta inserimento di due interi
printf("Inserisci un numero intero: ");
scanf("%d",&x);    
printf("Inserisci un altro numero intero: ");
scanf("%d",&y);

//stampa a seconda dei valori immessi
//la relazione tra i due numeri
if ( x == y)
    printf("x e' uguale a y n");
else if (x > y)
    printf("x e' maggiore di y n");
else 
    printf("x e' minore di y n");        

system("PAUSE");
return 0;
}

In tal caso il risultato è sempre lo stesso di prima ma si ha una maggior velocità nei confronti per verificare la veridicità delle relazioni, inoltre notiamo che l’ultimo else è privo di relazione in quanto se x non è uguale a y e neppure maggiore sarà necessariamente minore, in sintesi quell’ultimo else rappresenta tutti i casi restanti non elencati prima. Per chiarezza facciamo un ultimo esempio sull’uso dell’else:

if (età < 18)
printf("persona minorenne");
else
printf("persona maggiorenne");

Quindi si esegue il ramo dell’else quando risulta falsa la condizione età < 18.

Operatori logici. Talvolta potremo aver bisogno di concatenare più relazioni all’interno della condizione dell’if per fare ciò esistono i seguenti operatori logici:

Operatore Simbolo Esempio
AND && exp1 && exp2
OR || exp1 || exp2
NOT ! !exp1

Tali operatori funzionano nel seguente modo:

Espressione Come viene valutata
exp1 && exp2 Vero (1) solo se entrambi exp1 e exp2 sono veri; falso (0) altrimenti
exp1 || exp2 Vero (1) se uno tra exp1 o exp2 è vero; false (0) solo se entrambi sono falsi
!exp1 Falso (0) se exp1 vero; vero (1) se exp1 è falso

Facciamo un esempio sull’uso degli operatori logici:

/*Esempio d'uso dell'istruzione if...else if...else
con condizione composta mediante una AND*/

#include <stdio.h>
#include <stdlib.h>

int x,y;

int main()
{
//Richiesta inserimento di due interi
printf("Inserisci un numero intero: ");
scanf("%d",&x);    
printf("Inserisci un altro numero intero: ");
scanf("%d",&y);

//uso di una condizione composta
if( x > 0 && y < 0)
    printf("x ed y hanno segno discorden"); 

system("PAUSE");
return 0;
}

Funzioni

Parliamo ora delle funzioni nella programmazione C. Esse sono il centro della filosofia della programmazione in C. Abbiamo già usato alcune funzioni contenute nelle librerie proprie del C, ora impareremo a creare delle funzioni nostre che potremo richiamare quando vorremo fondamentalmente per evitare di dover utilizzare più volte, all’interno di uno stesso programma, la stessa parte di codice.

Una definizione di funzione può essere la seguente: è una parte di codice C dotata di nome che svolge specifiche azioni e opzionalmente restituisce un risultato al programma chiamante. Facciamo un semplice esempio:

/*Una semplice funzione*/

#include <stdio.h>
#include <stdlib.h>

//prototipo della funzione
long cubo(long);

long x,y;

int main()
{
//richiesta del numero da elevare
//al cubo NOTA %ld indica un long int
printf("Inserisci un intero: ");
scanf("%ld",&x);
//invocazione delle funzione cubo
y = cubo(x);
printf("Il cubo di %ld e' %ld n",x,y);
       
system("PAUSE");
return 0;
}

long cubo(long var)
{
//calcolo del cubo
  long var_al_cubo = var * var * var;
  //indica il valore di ritorno della funzione
  return var_al_cubo;
}

Analiziamo il codice:

long cubo(long) e’ il prototipo della funzione serve per far capire al compilatore che useremo tale funzione e per prepararne gli operandi. Tali operandi, nel nostro caso, sono una variabile long di INPUT ed un’altra sempre long di OUTPUT(valore di ritorno). La parola cubo rappresenta la chiave di chiamata della nostra funzione.

y = cubo(x) rappresenta la chiamata della funzione e l’assegnazione del valore di ritorno della funzione alla variabile y.

long cubo(long var) indica l’inizio della specifica della nostra funzione; inoltre ora è specificato il nome che si userà per usare la variabile di ingresso, tale nome non dovrà essere necessariamente lo stesso quando chiameremo la funzione sarà lo stesso compilatore ad effettuare la traduzione x = var.

long var_al_cubo = var * var * var calcola il cubo e pone il risultato nella variabile var_al_cubo.

return var_al_cubo specifica che il parametro da restituire al chiamante è var_al_cubo.

Il funzionamento di una chiamata a funzione è il seguente:
all’atto della chiamata di una funzione vengono impacchettati i dati necessari all’esecuzione in argomenti, a questo punto si eseguono le istruzioni interne alla dichiarazione della funzione, giunti alla clausola return, il flusso di controllo riprende dal punto in cui era stata chiamata la nostra funzione.

In sintesi per realiazzare una funzione abbiamo bisogno di due "parti":

Prototipo della funzione: – tipo_di_ritorno nome_funzione( tipo-arg1,…,tipo-argN);

Definizione della funzione: – tipo_di_ritorno nome_funzione( tipo-arg1 nome_arg1,…,tipo-argN nome_argN);
{
Istruzione1;
……………… IstruzioneN;
[return var_di_ritorno;]
}
Nota: il tipo di ritorno può anche essere omesso in quanto una funzione potrebbe non ritornare alcun valore. Per far ciò basta dichiarare nel prototipo della funzione al posto del tipo di ritorno la keyword void. Facciamo un ‘altro esempio per vedere meglio ciò.

/*Una semplice funzione*/ #include <stdio.h> #include <stdlib.h> //prototipo della funzione void stampa(int); int x,y; int main() { printf("Inserisci un intero: "); scanf("%d",&x); printf("Inserisci un secondo intero: "); scanf("%d",&y); stampa(x); stampa(y); system("PAUSE"); return 0; } void stampa(int var) { printf("valori immessi: %d n",var); }

Vantaggi dell’uso delle funzioni:

  • E’ più facile scrivere programmi strutturati, a causa della decomposizione di grandi problemi in parti più piccole, ogni parte verrà svolta da una funzione il cui codice e le cui variabili sono isolate dal resto del programma.
  • E’ più facile correggere un programma strutturato in quanto l’errore verrà isolato all’interno della funzione e sarà più semplice isolarlo.
  • E’ più semplice riusare il codice già scritto in quanto facilmente separabile dal resto e facilmente modificabile in modo tale da adeguarlo alle attuali esigenze

Istruzioni di iterazione

Passiamo ora alle istruzioni d’iterazione. Analizzeremo prima il for il quale ci permette di eseguire più volte una o un insieme di istruzioni, esso deve essere usato nella seguente forma:

for (valore iniziale ; condizione incremento)
{
istruzione1;
istruzione2;
……………… istruzioneN;
}

Valore iniziale: indica che valore deve avere la variabile su cui ci appoggiamo per svolgere il nostro ciclo.

Condizione: è la condizione grazie alla quale si esce dal ciclo e si esegue la prima istruzione esterna al for ciò avviene quando tale condizione diviene FALSA. Sbagliare la condizione implica spesso dei Cicli infiniti quindi fate molta attenzione.

Incremento: specifica come deve essere incrementata la nostra variabile d’appoggio.

Facciamo subito un esempio per comprendere l’utilizzo del for:

/* Un semplice ciclo for */

#include <stdio.h>
#include <stdlib.h>

int count;

main()
{
    /* Stampa i numeri da 1 a 20 */
for (count = 1; count <= 20; count++)
    printf("%dn", count);

system("PAUSE");
return 0;
}

Notiamo che in questo codice la condizione iniziale è data dall’assegnazione del valore 1 alla variabile count, la condizione d’arrestO è che count sia uguale al valore 20; infine il passo d’incremento è unitario, ovvero ad ogni esecuzione del for la variabile count verrà incrementata di uno dal comando count++ (count = count +1).

Facciamo ora un altro esempio che contenga anche l’uso degli array (vedi capitolo successivo), tale programma dovrà far immettere all’utente al più 10 numeri oppure interrompere l’inserimento se il numero immesso è uguale a 99:

/* Fa inserire all'utente 10 interi oppure 
interrompe l'inserimento se viene immesso il valore 99*/
#include <stdio.h>
#include <stdlib.h>

//dichiarazione dell'array dove
//conserveremo i valori immessi
int value[10];
int i,num=0;

int main()
{
//ciclo for con condizione composta e incremento unitario
for (i = 0; i < 10 && num != 99; i++)
  {
    printf("Enter a number, 99 to quit: ");
    scanf("%d", &num);
    value[i] = num;    
  }
system("PAUSE");
return 0;
}

Vediamo dunque che la condizione d’uscita è composta: ripeti il ciclo sino a che i sia < AND il valore inserito sia diverso da 99 se la condizione d’arresto risulta falsa si entra nelle parentesi del for e alla prima iterazione viene richiesto di inserire un numero; tale numero verrà poi memorizzato nella posizione 0 dell’array value alla seconda iterazione "i" varrà 1 quindi il valore, sempre se diverso da 99, verrà memorizzato alla posizione value[1] e così via sino a che, o risulta pieno l’array oppure abbiamo inserito il valore 99.

E’ anche possibile innestare, ovvero mettere uno nell’altro, più cicli for. Facciamo un esempio in cui vogliamo stampare a video un rettangolo di dimensioni righe * colonne

/* Esempio di due for innestati */
#include <stdio.h>
#include <stdlib.h>

void draw_box( int, int);
int c, r;
int main()
{
  //acquisizione numero righe
  printf("Inserisci il numero di righe: ");
  scanf("%d",&r);
  //acquisizione numero colonne
  printf("Inserisci il numero di colonne: ");
  scanf("%d",&c);
  //invocazione della funzione
  draw_box( r, c);
  
  system("PAUSE"); 
  return 0;
}

void draw_box( int righe, int colonne )
{
int col;
//for esterno
for ( ; righe > 0; righe--)
  {
      //for interno
      for (col = colonne; col %gt; 0; col--)
         {
         printf("X");
         }    
   printf("n");
  }
}

Nota: l’errore più comune che si commette è quello di separare inizializzazione, condizione e incremento con la virgola (,) anzichè con il punto e virgola (;)fate quindi molta attenzione!!!

Passiamo ora all’istruzione while la quale ripete un insieme di comandi finchè è VERA la condizione specificata. Il while ha la seguente sintassi:

while ( condizione )
{
istruzione1;
istruzione2;
…………….. istruzioneN;
}

Facciamo subito un semplice esempio:

/* Uso del while */
#include <stdio.h>
#include <stlib.h>

int count;
int main()
{
/* Come l'esempio del for stampa
i numeri da 1 a 20*/
//inizializzazione
count = 1;
while (count <= 20)
{
    printf("%dn", count);
    //incremento
  count++;
}

system("PAUSE");
return 0;
}

Avrete notato l’estrema somiglianza con l’istruzione for, infatti tutto ciò che scrivete con il while è traducibile in un for in effetti è preferibile usare il for semplicemente per una questione di immediatezza nella lettura del codice, in quanto nel while va specificato all’interno l’incremento e all’esterno l’inizializzazione, tutto ciò nel for è scritto in maniera compatta. Non faremo altri esempi sull’uso del while in quanto sarebbero una semplice traduzione.

Nota: anche i while sono innestabili basta seguire lo stesso esempio del for.

L’ultima istruzione di iterazione che tratteremo è il do … while. La differenza sostanziale con il while normale è che l’istruzione del ciclo viene eseguita almeno una volta e continua ad essere eseguita sino a che la condizione risulta essere VERA.

La sintassi del do … while è la seguente:

do
{
istruzione1;
istruzione2;
…………….. istruzioneN;
}
while ( condizione );

Le istruzioni interne ad do vengono eseguite almeno una volta in quanto la condizione è posta dopo queste ultime. Logicamente anche questo costrutto è sostituibile con un while o con un for esprimendo la condizione d’arresto in modo che essa risulti essere eseguita almeno la prima volta. A causa di ciò il do…while risulta essere usato meno frequentemente del semplice while.

Un esempio in cui risulta essere utile l’uso dell’istruzione do…while è il caso in cui vogliamo creare un menu di selezione per effettuare delle scelte analizziamo il seguente codice:

/* Uso del do...while */
#include <stdio.h>
#include <stdlib.h>

int get_menu_scelta( void );

int main()
{
int scelta;
scelta = get_menu_scelta();
printf("Hai scelto il menu %dn", scelta );

system("PAUSE");
return 0;
}

int get_menu_scelta( void )
{
  int selezione = 0;
  do
    {
    printf("n" );
    printf("n1 - Aggiungi un Record" );
    printf("n2 - Cambia un record");
    printf("n3 - Cancella un record");
    printf("n4 - Esci");
    printf("n" );
    printf("nEffettua una scelta: " );
    scanf("%d", &selezione );
    }
    while ( selezione < 1 || selezione > 4 );
  return selezione;
}

Tale codice non fa altro che stampare le possibili opzioni di scelta e aspettare la scelta dell’utente sino a che esso non digiti un valore valido. Quindi la parte di codice contenuta nel do…while viene eseguita sino a che non risulta falsa la condizione selezione < 1 || selezione > 4 a questo punto si esce dall’istruzione do…while e il valore immesso viene usato per stampare a video printf("Hai scelto il menu %dn", scelta );.

Gli Array

Un array è un insieme ordinato mediante indice di variabili a cui possiamo accedere singolarmente specificando il nome dell’array e l’indice della variabile.

Come ogni variabile in C anche gli array devono essere definiti dobbiamo specificare:

  • Il tipo
  • Un nome
  • Il numero di elementi in esso contenuti

Facciamo un esempio di dichiararzione:

int array[10];

In tale array potremo memorizzare 10 valori e accederemo ad essi nel seguente modo:

array[0] = 1;
array[1] = 2;
array[2] = 3;
.................... 
array[9] = 10;

Avrete quindi capito che il primo elemento dell’array non è array[1] ma array[0];

Nota: non dichiarate array con dimensione più grande del necessario per non utilizzare memoria inutilmente.

Vi chiederete a questo punto qual è l’utilità degli array, essi risultano essere molto comodi quando dobbiamo usare gruppi di variabili, in tal caso invece di utilizzare più variabili ne dichiariamo un array e accediamo più semplicemente ad ogni variabile mediante il suo indice. L’uso degli indici diviene indispensabile se utilizziamo istruzioni di iterazione come il for o il while.

Un semplice esempio è quello in cui vogliamo memorizzare le nostre spese effettuate in ogni mese, per far ciò potremo adoperare 12 variabili di tipo float, ma la gestione di queste ultime risulterebbe lunga e più intricata, vediamo invece come è semplice utilizzare un array di dimensione 13 che chiameremo spese e dove l’indice dell’array corrisponderà al mese dell’anno:

/* Uso degli array monodimensionali*/
#include <stdio.h>
#include <stdlib.h>

/* Dichiarazione di un array e 
di una variabile di appoggio per il ciclo for*/
float spese[13];
float spesaAnnua = 0;
int count;

int main()
{

/* Riempimento dell'array tramite input da tastiera */
for (count = 1; count < 13; count++)
  {
   printf("Inserisci le spese per il mese %d: ", count);
   scanf("%f", &spese[count]);
  }

//stampa il contenuto dell'array
for (count = 1; count < 13; count++)
  {
  printf("Mese %d = EURO %.2fn", count, spese[count]);
  }

//spesa annua
for (count = 1 ; count < 13; count++)
  {
  spesaAnnua = spese[count] + spesaAnnua;   
  }

//stampa il valore della spesa annua
printf("Spesa annua = EURO %.2fn", spesaAnnua);
  
system("PAUSE");
return 0;
}

Il codice dovrebbe essere di semplice comprensione: iniziamo dalla dichiarazione dell’array, vediamo subito che esso viene dichiarato di dimensione 13 anche se i mesi sono solo 12… facciamo ciò solo per poter mantenere una coerenza fra la numerazione normale dei mesi (1 = Gennaio, 2 = Febbraio, etc. ) e l’indice di un array in cui ricordiamo il primo elemento è 0 e l’ultimo è DIMENSIONE -1.

Detto ciò analizziamo il primo ciclo for che serve per riempire il nostro array in tal caso la variabile count, ci serve da indice per accedere al valore dell’array, quindi al primo ciclo count varrà 1 (poichè così inizializzato) accederemo quindi a spese[1] al secondo giro count verrà incrementato, come indicato nel passo d’incremento (count++), accederemo quindi a spese[2] e così via sino a che non avremo riempito tutto l’array e facendo diventare quindi falsa la condizione del ciclo for in quanto count sarà diventato 13.

I restanti for operano nella medesima maniera, quello della stampa semplicemente utilizza count per prelevare il contenuto di spese[count] e mandarlo a video mentre l’ultimo ciclo for non fa altro che sommare alla variabile spesaAnnua (inizializzata a 0) il valore di spese[count].

Dobbiamo ora effettuare una distinzione: quelli trattati sino ad ora sono array monodimensionali simili ai vettori matematici, introduciamo ora gli array bi-dimensionali paragonabili alle matrici matematiche.

Tali array servono per memorizzare dati in una specie di tabella avente dimN righe e dimM colonne. Un array del genere viene dichiarato nel seguente modo:

matrice[dimN] [dimM];

per accedere ad un elemento dell’array bidimensionale basta al solito specificarne il nome e l’indice che in questo caso è una coppia quindi l’elemento in terza riga e quarta colonna sarà matrice[ 3 ][ 4 ].

Per riempire un array bi-dimensionale dobbiamo usare due cicli for innestati: il primo per far scorrere le righe ed il secondo per le colonne, useremo lo stesso trucco per stamparne il contenuto. Facciamo un esempio:

/* Inizializzazione e uso di array bi-dimensionali */

#include <stdio.h>
#include <stdlib.h>

int matrice[5][3];

int main()
{
    //ciclo d'inserimento
    for(int i=0; i<5; i++)
        for(int j=0; j<3; j++)
            matrice [i][j] = rand();
     
    //ciclo di stampa
    for( int k=0; k<5; k++)
        {
        for(int w=0; w<3; w++)        
           printf("%d t",matrice[k][w]);
        printf("n");   
        }       

system("PAUSE");
return 0;
}

Il primo ciclo for innestato dovrebbe orami essere chiaro (la funzione rand() viene utilizzata per associare all’elemento di posizione matrice[ i ][ j ] un valore a caso).

Vediamo ora il ciclo di stampa, il quale funziona nel seguente modo:

il primo for fa scorrere l’indice k che al primo giro varrà 0. Mantenendo questo valore si effettua il for innestato il quale assegna a w il valore 0 e stampa il valore contenuto nella posizione matrice[ 0 ][ 0 ].

Nota: t indica che il valore deve essere seguito da una tabulazione,per avere una stampa più chiara

Dopo ciò w assume il valore w = 1 viene stampato il valore matrice[ 0 ][ 1 ] poi matrice[ 0 ][ 2 ] a questo punto viene posto w = 3 quindi la condizione del for interno non è più verificata. Si esce da tale for e si manda a capo l’output a video tramite il comando n.

Ora k = 0 quindi non possiamo ancora uscire dal primo for quindi k viene posto a 1 dopodichè w viene rimesso a 0 così si stampa il valore di matrice[ 1 ][ 0 ] e così via sino a quando si stampa l’ultimo valore ovvero matrice[ 4 ][ 2 ].

Le stringhe

Una stringa non è niente altro che un insieme di caratteri, ma la loro gestione non è per nulla semplice ed immediata in C, molto più semplice risulta la loro gestione in C++. Proviamo comunque ad imparare le basi dell’uso delle stringhe.

Per memorizzare una stringa non esiste un tipo predefinito dobbiamo quindi usare un array di caratteri nella seguente maniera:
char string[7];
tale array potrà contenere al più sei caratteri in quanto l’ottavo viene usato per inserirvi un carattere speciale detto carattere terminatore con cui si indica che la stringa è terminata, tale carattere è

Per inizializzare una stringa possiamo operare in due modi:

 

  • char string[6] = {‘F’,’a’,’b’,’i’,’o’,’’};
  • char string[6] = "Fabio"

 

Utilizzando il secondo modo sarà il compilatore ad aggiungere in automatico il carattere terminatore alla fine della stringa.

#include
#include

//dichiarazione e inizializzazione di due stringhe
char string1[9] = {'M','r','w','c','o','r','s','i',''};
char string2[] = "Mrwebmaster";

int main()
{
    
printf("%s & %sn",string2,string1);
  
//modifica string1    
printf("Digita la nuova stringa da memorizzare in string 1:");
scanf("%s", &string1);
    
//stampa string 1
printf("String 1 ora vale:");
printf("%s n",string1);

system("PAUSE");    
return 0;  
}

Ricordo che per una trattazione più completa della gestione delle stringhe si rimanda al Capitolo XX della guida al C++.

Le strutture

Una struttura è un contenitore di dati creato a proprio piacimento dal programmatore. In tali strutture possiamo memorizzare un’insieme di dati anche differenti fra loro per tipo.

Una struttura viene dichiarata nella seguente maniera:

struct nomeStruttura
{
tipo-var1 nome_var1;
tipo-var1 nome_var2;
……. tipo-var1 nome_varN;
}istanza

Così facendo creeremo un’istanza di tipo nomeStruttura la quale avrà per valori tutti i campi specificati all’interno della struttura. Sarà possibile accedere ai singoli campi per modificarli o semplicemente usarli mediante l’operatore .

Segue un esempio per chiarire l’uso delle strutture:

#include<stdlib.h>
#include<stdio.h>

struct Persona
{
    char Nome[30];
    char Cognome[30];
    char telefono[14];    
};

void stampa_Persona(Persona);

int main()
{
Persona p1;
//inserimento Nome
printf("Inserire i dati della persona: n");
printf("Nome: ");
scanf("%s",p1.Nome);
//Inserimento Cognome
printf("Cognome: ");
scanf("%s",p1.Cognome);
//Inserimento numero telefono
printf("Telefono formato 081-xxxxxxx: ");
scanf("%s",p1.telefono);

//Inizializzazzione persona p2
Persona p2 = {"Mario","Rossi","081-7766545"};

stampa_Persona(p1);

stampa_Persona(p2);

system("PAUSE");    
return 0;  
}

void stampa_Persona(Persona x)
{
printf("%s n%s n%s nn",x.Nome,x.Cognome,x.telefono);    
}

Nel codice è stata creata una struttura Persona avente tre campi Nome, Cognome e telefono tutti di tipo array di caratteri, vi è poi la definizione di alcune variabili che ci serviranno da variabili di appoggio ed infine, prima del main, vi è il prototipo della funzione che useremo per stampare a video le varie istanze di Persona.

Persona p1; – con tale istruzione creiamo una istanza di persona chiamata p1.

scanf("%s",p1.Nome); – accettiamo in input da tastiera una stringa e tale valore viene posto nel campo nome dell’istanza p1, lo stesso vale per gli scanf successivi solo che memorizzano le stringhe rispettivamente nei campi Cognome e telefono di p1.

Persona p2 = {"Mario","Rossi","081-7766545"}; – è un altro modo di inizializzare una struttura, così facendo si assegnano dei valori alla struttura p2 già in fase di compilazione e non in fase di esecuzione come prima.

stampa_Persona(p1); – è la chiamata alla funzione stampa_Persona la quale non fa altro che fare apparire a video i vari campi appartenenti alla persona passata come parametro in questo caso p1.

Le strutture si sono evolute col tempo nei cosidetti oggetti, per questo motivo non affronteremo una trattazione più completa ma si rimanda il lettore alla guida al C++ per avere maggiori dettagli.

Altri contenuti interessanti

Pubblicitร 

Leggi anche...

Radice quadrata in C: vediamo come calcolarla in diversi modi

La radice quadrata è un'operazione matematica piuttosto comune (in...

Sperimentare la sequenza di Collatz in C++

Vediamo come verificare la congettura di Collatz con C++....

Calcolare la radice quadrata con Python

In Python è possibile calcolare la radice quadrata usando...

12 film che ogni programmatore dovrebbe guardare (per trovare ispirazione e creatività)

Molti ragazzi hanno deciso di intraprendere una carriera da...

Cartonizzare una foto con Python e la libreria OpenCV

La cartoonization è una procedura grafica che consente di...

Creare flowchart (diagrammi di flusso) online: 5 strumenti gratuiti

Hai bisogno di realizzare una flow chart ma non...
Pubblicitร