[C] Lezione 2

In questa lezione vedremo:

- come formulare una condizione;

- come costruire un ciclo;

- vari tipi di operatori.

Condizioni if () else e switch ()

 

Abbiamo già visto in altre pagine cosa sono le condizioni. Esse servono per creare una struttura di decisione basata su una condizione. In altre parole, vengono eseguiti un'istruzione o un gruppo di istruzioni solo se una determinata condizione è verificata. Ecco un esempio con if ():

 

#include <stdio.h>

 

int main() {

    int var;

    scanf("%d", &var);

    if (var == 10) {

        printf("var e' uguale a 10\n");

    }

    if (var < 10) {

        printf("var e' minore di 10\n");

    }

    if (var > 10) {

        printf("var e' maggiore di 10\n");

    }

    if (var != 10) {

        printf("var e' diverso da 10\n");

    }

    if (var <= 10) {

        printf("var e\' minore o uguale a 10\n");

    }

    if (var >= 10) {

        printf("var e' maggiore o uguale a 10\n");

    }

 

    if (var == 100) printf("var e\' uguale a 100\n");

    else printf("var e\' diverso da 100\n");

 

    return 0;

 

}

 

Sembra essere stato chiaro l'esempio. Le graffe sono obbligatorie se if () deve eseguire più di una singola istruzione. Inoltre nella condizione sono consentite assegnazioni, esecuzioni di funzioni e altro. La condizione è considerata vera se il suo valore booleano è vero o numericamente diverso da zero. Il segno di not (!) ribalta il valore della condizione, quindi true diventa falsefalse diventa true. Ad esempio dire:

 

if (!(var > 4)) {...} // Se var non è maggiore di 4 ...

 

E' come dire:

 

if (var <= 4) {...} // Se var è minore o uguale a 4 ...

 

Quando si usa ! davanti a una condizione si consiglia di mettere le parentesi tonde ( ) per evitare errori di interpretazione del codice da parte del compilatore. In questo caso infatti !var avrebbe sempre un valore pari a 0 se var ha un valore diverso da 0 e 1 altrimenti, perciò non avrebbe mai un valore più grande di 4: pertanto la condizione non sarebbe mai verificata. 

 

 

Operatori booleani:

&& unisce due o più condizioni, e restituisce true solo se tutte le condizioni sono vere (and logico). 

|| unisce due o più condizioni, e restituisce true solo se almeno una condizione è vera (or logico).

! ribalta il valore di verità della condizione a cui si riferisce (not logico).

 

Per non fare casini è buona abitudine mettere le parentesi fra due condizioni. Esempio:

 

if ((var == 4) || ((var == 2) && (var2 == 2))) {...}

In generale, può essere utile osservare che la priorità degli operatori di comparazione è maggiore di quella degli operatori booleani, e che quella dell'operatore di congiunzione logica (&&) ha priorità su quella di disgiunzione logica (||). 

 

switch () crea condizioni, ma solo su una variabile. Si utilizza così:

 

#include <stdio.h>

 

int main() {

    int var;

    scanf("%d", &var);

 

    switch (var) { // Condizioni su var

        case 0: // Se vale 0...

            printf("Hai inserito 0");

            break;

        case 1: // Se vale 1...

            printf("Hai inserito 1");

            break;

        case 2: {

            printf("Hai inserito 2");

        } // Si possono mettere le parentesi graffe

    }

    return 0;

}

 

In questo caso è più conveniente e veloce che mettere tanti if. default si mette alla fine, ed esegue le istruzioni con qualsiasi valore inserito. L'istruzione break è necessaria perché ci permette di uscire dallo switch senza eseguire le istruzioni successive. Se non fosse stata inserita, l'esecuzione di un determinato caso avrebbe implicato quella dei successivi.

 

Lo stesso esempio con default:

 

#include <stdio.h>

 

int main() {

    int var;

    scanf("%d", &var);

 

    switch (var) {

        case 0:

            printf("Hai inserito 0");

            break;

        case 1:

            printf("Hai inserito 1");

            break;

        case 2:

            printf("Hai inserito 2");

            break;

        default:

            printf("Hai inserito un numero maggiore di 2 o minore di 0");

    }

    return 0;

}

 

 

Il ciclo while () (o do while ())

 

Un ciclo while ci permette di ripetere una o più istruzioni fino a quando la condizione fra parentesi non diventa falsa.

 

char Stringa[256];

while (!(Stringa[0] == 'P' && Stringa[1] == 'a' && Stringa[2] == 's' &&

&& Stringa[3] == 's' && Stringa[4] == 'w' && Stringa[5] == 'o' &&

&& Stringa[6] == 'r' && Stringa[7] == 'd' && Stringa[8] == '\0'))

{

    fflush(stdin);
    printf("Inserisci la chiave per uscire: ");
    scanf("%s", Stringa);
}

printf("[Fine esecuzione]");

 

\0 è il carattere nullo, e Stringa[8] == '\0' ci serve per capire se la stringa inserita sia effettivamente lunga 8 caratteri, e non più lunga. Output:

 

Inserisci la chiave per uscire: Ciccio

Inserisci la chiave per uscire: Passwordaaaa

Inserisci la chiave per uscire: Password

[Fine esecuzione]

 

In alternativa a while si può usare il do while, che invece di controllare la condizione prima delle istruzioni, controlla la condizione dopo aver eseguito le istruzioni:

 

int var;

do {

     printf("Inserisci la chiave numerica per uscire: ");
    scanf("%d", &var);
} while (var != 14);

printf("[Fine esecuzione]");

 

 

Come controllare che il buffer da tastiera sia vuoto e come svuotarlo

 

Funzioni come scanf e getchar possono scrivere su un buffer alcuni valori. Osserviamo l'esempio:

 

char var[2];

 

printf("Inserisci un carattere: ");

var[0] = getchar();

 

printf("Inserisci un altro carattere: ");

var[1] = getchar();

 

printf("[Fine esecuzione]");

 

Output:

 

Inserisci un carattere: a

Inserisci un altro carattere: [Fine esecuzione]

 

Cosa è successo? Perché non ha chiesto il secondo carattere? Pensateci: avete inserito due caratteri, non uno solo. Il secondo carattere è \n (ovvero l'accapo, quando avete premuto Invio). Infatti, il primo getchar assegna a var[0] il primo carattere inserito (a) e copia il carattere rimanente nel buffer. Il secondo getchar prende il carattere dal buffer, evitando così di chiederlo all'utente. Ecco come ovviare al problema:

 

char var[2];

 

printf("Inserisci un carattere: ");

var[0] = getchar();

 

if (var[0] != '\n')

    while (getchar() != '\n'); // In questo modo svuota il buffer

 

printf("Inserisci un altro carattere: ");

var[1] = getchar();

 

// Qui è inutile svuotare il buffer dato che non si usa più getchar

 

printf("[Fine esecuzione]");

 

Se var[0] è uguale a \n, vuol dire che non c'è buffer, perché quando si inserisce una stringa da tastiera, l'ultimo carattere è sempre \n. Ma se var[0] invece è diverso da \n, c'è necessariamente almeno un valore nel buffer. Quindi, ripetiamo la funzione getchar finché questa non è uguale a \n, perché vuol dire che abbiamo raggiunto la fine del buffer, quindi lo abbiamo svuotato tutto!

 

 

Il ciclo for ()

 

L'obiettivo principale del ciclo for è come quello del while, ovvero, ripetere le stesse istruzioni più volte. Ma, a differenza del while, qui troviamo un'assegnazione iniziale (inizio), una condizione, che viene controllata ad ogni iterazione, e se è falsa finisce il ciclo (fine), e un'assegnazione che si ripete ad ogni iterazione (incremento o decremento). Esempio:

 

int i; // i sta per Indice

for (i = 0 /* inizio */; i < 20 /* condizione */; i = i + 1 /* incremento */)

    printf("%d", i); // Scrive i numeri da 0 a 19

 

(senza commenti)

 

int i;

for (i = 0; i < 20; i = i + 1)

    printf("%d", i);

 

Questo tipo di ciclo permette di iterare le stesse istruzioni lungo un array. Esempio:

 

int i, j, Matrice[10][10]; // Si possono dichiarare più variabili

                           // nella stessa riga solo se sono dello

                           // stesso tipo.

for (i = 0; i < 10; i = i + 1)

    for (j = 0; j < 10; j = j + 1)

        Matrice[i][j] = i * j;

 

 

Il C ci permette di scrivere gli incrementi/decrementi in altri modi:

a++ o ++a : incrementa di un'unità.

a += x : incrementa di una quantità x

a-- o --a : decrementa di un'unità

a -= x : decrementa di una quantità x

a *= x : moltiplica a per x e ne assegna il risultato

a /= x : divide a per x e ne assegna il risultato

 

Gli operatori ++ e -- possono essere sia preposti che posposti, oltre a poter essere usati solo per i tipi interi, ma il loro significato non è lo stesso. La differenza sta nell'ordine tra incremento e valore restituito. Osserviamo l'esempio:

 

int a, b;

a = 0, b = 0; // Anche le assegnazioni si possono fare nella

              // stessa riga.

printf("++a: %d\nb++: %d", ++a, b++);

 

Output:

 

++a: 1

b++: 0

 

Infatti, con l'operatore preposto ++a la variabile viene prima incrementata, e quindi viene restituito il suo nuovo valore. Con l'operatore posposto b++, viene prima restituito il valore iniziale di b e questa viene poi incrementata.

 

 

L'operatore ternario

 

L'operatore ternario funziona esattamente come un if (x) y else z.

 

int var, cond;

cond = 0;

var = (cond == 1) ? 0 : 1;

// cond è uguale a 1? Se è così 0, altrimenti 1.

printf("%d", var); // Stampa 1

 

Questo operatore può farvi risparmiare spazio nel codice, ma la velocità di esecuzione è pressoché identica a quella di un if (x) y else z. Inoltre per casi più complicati il codice potrebbe perdere di leggibilità.

 

<--- Torna alla lezione 1   |   Passa alla lezione 3 --->