LABORATORUL 05

Instrucțiuni (1) - algoritmi și structuri de decizie

Durată: 2 ore Limbaj: C / C++ Precedent: Laboratorul 4 PDF îndrumar EN English version

Un program este transpunerea unui algoritm. Înainte de a scrie cod, problema trebuie descrisă ca o succesiune ordonată de pași. Această lucrare tratează reprezentarea algoritmilor și prima categorie de instrucțiuni care schimbă ordinea execuției: cele de decizie.

1Obiectivele lucrării

  • Descrierea unui algoritm prin schemă logică și prin pseudocod
  • Distingerea instrucțiunilor expresie de cele compuse
  • Utilizarea corectă a instrucțiunilor if și if-else
  • Rezolvarea ambiguității else prin folosirea acoladelor
  • Aplicarea instrucțiunii switch și înțelegerea rolului lui break

2Algoritmi și moduri de reprezentare

Algoritmul este o succesiune finită de acțiuni care, aplicate datelor de intrare, conduc la rezultatul dorit. Descrierea lui este o etapă intermediară între formularea matematică și programul scris într-un limbaj concret.

MetodăAvantajeDezavantaje
Limbaj naturalnu necesită pregătire specialăambiguu la probleme complexe
Schemă logicăvizual clară, ușor de urmăritdevine greu de desenat pentru algoritmi mari
Pseudocodconcis, apropiat de codul finalnu este standardizat
Tabele de decizieideal pentru multe alternative combinatenepotrivit pentru algoritmi secvențiali

3Simboluri în schemele logice

START / STOP terminator citește / scrie intrare-ieșire x = a + b prelucrare a > b ? decizie Blocurile se leagă prin segmente orientate; din blocul de decizie pleacă două ramuri, marcate DA și NU. Exemplu: START → citește a, b → a > b ? → (DA) scrie a / (NU) scrie b → STOP
Fig. 1 - Simbolurile de bază folosite în schemele logice

4Tipuri de instrucțiuni

TipFormăObservații
Instrucțiune expresieexpresie;orice expresie urmată de ; devine instrucțiune
Instrucțiune vidă;nu face nimic; utilă acolo unde sintaxa cere o instrucțiune
Instrucțiune compusă (bloc){ ... }grupează mai multe instrucțiuni; poate conține declarații locale
Instrucțiuni de decizieif, if-else, switchaleg o ramură de execuție
Instrucțiuni repetitivewhile, do-while, fortratate în Laboratorul 6
Instrucțiuni de saltbreak, continue, return, gotoîntrerup fluxul normal
Blocul creează un domeniu nouVariabilele declarate între acolade există doar în interiorul blocului. La ieșire sunt distruse, iar numele lor devine din nou disponibil.

5Instrucțiunea if

forme sintactice
if (expresie)
    instructiune;                  // forma simpla

if (expresie)
    instructiune_1;
else
    instructiune_2;                // forma cu alternativa

if (expresie_1) {
    ...
} else if (expresie_2) {
    ...
} else {
    ...                            // alternative in cascada
}
Ce înseamnă „adevărat" în CExpresia din if nu trebuie să fie neapărat o comparație. Orice valoare nenulă este considerată adevărată, iar zero este fals. De aceea if (n) este echivalent cu if (n != 0).

6Simulator de decizie

Urmăriți pas cu pas cum se evaluează condițiile într-o cascadă if-else if și de ce ordinea testelor contează.

Clasificarea unei note - cascadă de decizii
De ce funcționează cascadaTestul nota < 9 pare să accepte și note sub 5, dar acele cazuri au fost deja tratate de condițiile anterioare. Într-o cascadă, fiecare ramură presupune implicit că toate cele de dinaintea ei au fost false.

7Instrucțiunea switch

sintaxă generală
switch (expresie) {
    case c1:
        instructiuni;
        break;
    case c2:
        instructiuni;
        break;
    default:
        instructiuni;
}
AspectRegulă
Tipul expresieitrebuie să fie întreg sau caracter - nu float, nu șir
Etichetele casetrebuie să fie constante, distincte între ele
breakoprește execuția; fără el se continuă în cazul următor
defaultopțional; se execută dacă niciun caz nu se potrivește
Căderea în cascadă (fall-through)Absența lui break face ca execuția să continue în cazurile următoare, indiferent de valoarea lor. Uneori este intenționat - pentru a grupa mai multe valori care primesc același tratament - dar cel mai des este o omisiune.

8Erori frecvente

1. Punct și virgulă după if if (a > b); este sintactic corect: corpul lui if devine instrucțiunea vidă, iar blocul următor se execută întotdeauna. Compilatorul nu semnalează nimic.
2. Ambiguitatea else Un else se asociază întotdeauna cu cel mai apropiat if neasociat, indiferent de indentare. Indentarea înșeală ochiul, nu compilatorul. Folosiți acolade mereu.
3. Compararea numerelor reale cu == if (0.1 + 0.2 == 0.3) este fals, din cauza reprezentării binare aproximative. Corect: if (fabs(a - b) < 1e-9).
4. Atribuire în loc de comparație if (x = 0) atribuie și dă fals mereu; if (x = 5) atribuie și dă adevărat mereu. Unii programatori scriu if (5 == x) tocmai pentru ca greșeala să devină eroare de compilare.

9Cod sursă

ecuatie.c - rezolvarea ecuației de gradul II
#include <stdio.h>
#include <math.h>

int main(void)
{
    double a, b, c, delta, x1, x2;

    printf("Coeficientii a, b, c: ");
    scanf("%lf %lf %lf", &a, &b, &c);

    if (a == 0) {
        if (b == 0) {
            if (c == 0) printf("Identitate: orice x este solutie\n");
            else        printf("Ecuatie imposibila\n");
        } else {
            printf("Ecuatie de gradul I: x = %.4f\n", -c / b);
        }
    } else {
        delta = b * b - 4 * a * c;

        if (delta > 0) {
            x1 = (-b + sqrt(delta)) / (2 * a);
            x2 = (-b - sqrt(delta)) / (2 * a);
            printf("Doua radacini reale: %.4f si %.4f\n", x1, x2);
        } else if (delta == 0) {
            printf("Radacina dubla: %.4f\n", -b / (2 * a));
        } else {
            printf("Radacini complexe: %.4f +/- %.4fi\n",
                   -b / (2 * a), sqrt(-delta) / (2 * a));
        }
    }
    return 0;
}
calculator.c - switch cu grupare de cazuri
#include <stdio.h>

int main(void)
{
    double a, b;
    char op;

    printf("Expresie (ex: 12 * 4): ");
    scanf("%lf %c %lf", &a, &op, &b);

    switch (op) {
        case '+':
            printf("%.2f\n", a + b);
            break;
        case '-':
            printf("%.2f\n", a - b);
            break;
        case '*':
        case 'x':                       // grupare intentionata: * si x fac acelasi lucru
        case 'X':
            printf("%.2f\n", a * b);
            break;
        case '/':
            if (b == 0) printf("Impartire la zero!\n");
            else        printf("%.2f\n", a / b);
            break;
        default:
            printf("Operator necunoscut: %c\n", op);
    }
    return 0;
}
ambiguitate.c - de ce sunt necesare acoladele
#include <stdio.h>

int main(void)
{
    int a = 1, b = 2;

    // GRESIT: indentarea sugereaza altceva decat face compilatorul
    if (a > 0)
        if (b > 5)
            printf("b mare\n");
    else                                // se leaga de AL DOILEA if, nu de primul!
        printf("Aici NU inseamna a <= 0\n");

    // CORECT: acoladele elimina orice ambiguitate
    if (a > 0) {
        if (b > 5) {
            printf("b mare\n");
        }
    } else {
        printf("a <= 0\n");
    }
    return 0;
}

10Atelier de cod

Deciziile se înțeleg cel mai bine urmărindu-le pas cu pas. Rulați programele, apoi schimbați datele de intrare ca să forțați fiecare ramură.

Ecuația de gradul al II-lea - toate cazurile
#include <stdio.h>
#include <math.h>

int main(void)
{
    double a, b, c, delta;

    printf("Coeficientii a, b, c: ");
    scanf("%lf %lf %lf", &a, &b, &c);

    if (a == 0) {
        if (b == 0) printf("Nu este ecuatie de gradul II\n");
        else        printf("Gradul I: x = %.4f\n", -c / b);
    } else {
        delta = b * b - 4 * a * c;
        if (delta > 0) {
            printf("Doua radacini: %.4f si %.4f\n",
                   (-b + sqrt(delta)) / (2 * a),
                   (-b - sqrt(delta)) / (2 * a));
        } else if (delta == 0) {
            printf("Radacina dubla: %.4f\n", -b / (2 * a));
        } else {
            printf("Radacini complexe\n");
        }
    }
    return 0;
}
De încercatSchimbați datele de intrare în 1 -4 4 (rădăcină dublă), apoi în 1 1 5 (rădăcini complexe) și în 0 2 6 (gradul I). Fiecare set de valori duce execuția pe altă ramură.
switch - exercițiu cu zilele lunii
#include <stdio.h>

int main(void)
{
    int luna, zile = 0;

    printf("Luna (1-12): ");
    scanf("%d", &luna);

    switch (luna) {
        case 1: case 3: case 5: case 7:
        case 8: case 10: case 12:
            zile = 31;
            break;

        /* Completati cazurile pentru lunile de 30 de zile (4, 6, 9, 11)
           si pentru februarie (28). Adaugati si un default care sa
           afiseze "Luna invalida". */
    }

    if (zile > 0) printf("Luna %d are %d zile\n", luna, zile);
    return 0;
}

11Sarcini de lucru

  • Desenați schema logică pentru determinarea maximului dintre trei numere, apoi implementați-o.
  • Scrieți programul complet de rezolvare a ecuației de gradul II, tratând toate cazurile particulare.
  • Implementați un calculator cu switch, care tratează și împărțirea la zero.
  • Reproduceți eroarea cu ; după if și explicați comportamentul observat.
  • Rulați exemplul cu ambiguitatea else și verificați de care if se leagă.
  • Testați if (0.1 + 0.2 == 0.3) și corectați comparația folosind o toleranță.
  • Scrieți un program care afișează numărul de zile dintr-o lună, folosind switch cu grupare de cazuri.

12Aplicație de aprofundare

ExtindereScrieți un program care determină ziua săptămânii pentru o dată calendaristică introdusă de utilizator, folosind congruența lui Zeller. Validați data (lună între 1 și 12, zi validă pentru luna respectivă, ținând cont de anii bisecți) și afișați un mesaj clar de eroare pentru fiecare situație nevalidă. Folosiți switch pentru afișarea numelui zilei.

13Întrebări de verificare

14Resurse