4.7. Instructiunea for

2019/02/12 in Programare in C

Instructiunea for, ca si instructiunea while, se utilizeaza pentru a realiza o structura repetitiva conditionata anterior. Ea are formatul:

for(exp1;exp2;exp3)
instructiune

unde exp1, exp2 si exp3 sunt expresii.

Antetul instructiunii for este for(exp1;exp2;exp3), iar instructiune este corpul ei si se executa repetat. Expresia exp1 se numeste partea de initializare a ciclului for, iar exp3 este partea de reinitializare a lui. Expresia exp2 este conditia de continuare a ciclului for si ea joaca acelasi rol ca si expresia din ciclul while.

Instructiunea for se executa astfel:

  1. se executa secventa de initializare definita de exp1;
  2. se evalueaza expresia exp2. Daca are o valoare diferita de 0 (este adevarata), atunci se executa instructiunea care formeaza corpul ciclului. Altfel (expresia are o valoare diferita de 0 adica fals) se termina executia instructiunii for si se trece la instructiunea urmatoare;
  3. dupa executarea corpului ciclului se executa secventa de reinitializare definita de exp3. Apoi se reia executia de la pasul 2.

Ca si in cazul instructiunii while, instructiunea din corpul ciclului for nu se executa niciodata daca exp2 are valoarea 0 chiar de la inceput.

Expresiile din antetul lui for pot fi si vide. Caracterele punct si virgula vor fi totdeauna prezente.

Exemplu:

Secventa de insumare a elementelor unui tablou tab:

s = tab[0] + tab[1] + ...+ tab[n-1]

se realizeaza executand repetat instructiunea compusa:

{
s = s + tab[i];
i++;
}

unde initial s = 0 si i = 0, atat timp cat i < n. Ea se poate realiza cu ajutorul instructiunii for:

4.18. Sa se scrie un program care citeste si afiseaza.

for(s = 0, i = 0; i < n; i++)
  s = s + tab[i];

In acest caz expresia exp1 este formata din doua atribuiri: s = 0 si i = 0, exp2 este reprezentata de conditia i < n iar exp3 este expresia i++.

Conform definitiei instructiunii for, la inceput se evalueaza exp1, deci se executa atribuirile:

s = 0

si

i = 0;

Se evalueaza exp2, adica se determina valoarea conditiei:

i < 0

Daca ea este adevarata, atunci se executa corpul instructiunii for, adica suma:

s = s + tab[i]

Apoi se evalueaza exp3, adica se executa incrementarea:

i++

Dupa incrementarea lui i se revine la evaluarea conditiei exp2. In felul acesta, ciclul continua pana cand conditia:
i < n devine falsa, adica in momentul in care i = n. Deci ciclul se intrerupe dupa ce elementele:

tab[0], tab[1], ..., tab[n-1]

au fost adaugate la s.

Secventa de mai sus poate fi scrisa cu ajutorul instructiunii while astfel:

s = 0;
i = 0;
while(i < 0) {
  s = s + tab[i];
  i++;
}

In general, instructiunea for:

for(exp1;exp2;exp3)
instructiune

poate fi scrisa cu ajutorul unei secvente in care se utilizeaza instructiunea while:

exp1;
while(exp2) {
  instructiune
  exp3;
}

aceasta echivalare nu are loc intr-un singur caz si anume atunci cand, in corpul instructiunii se utilizeaza instructiunea continue.

Reciproc, orice instructiune while poate fi scrisa cu ajutorul unei instructiuni for in care exp1 si exp3 sunt vide.

Astfel instructiunea while de mai jos:

while(exp)
instructiune

este echivalenta cu instructiunea for:

for(;exp;)
instructiune

O instructiune for de forma:

for(;;)
instructiune

este valida si este echivalenta cu instructiunea while de mai jos:

while(1)
instructiune

Un astfel de ciclu se poate termina prin alte mijloace decat cel obisnuit, cum ar fi instructiunea de revenire dintr-o functie, un salt la o eticheta etc.

Din cele de mai sus rezulta echivalenta celor doua cicluri. Se recomanda utilizarea instructiunii for in ciclurile in care sunt prezente partile de initializare si reinitializare. De obicei, aceste cicluri asanumitele cicluri cu pas.

Exercitii:

4.18. Sa se scrie un program care citeste intregul n din intervalul [0, 170], calculeaza si afiseaza pe n!.

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

main()
{
    int n, i;
    double f;

    printf("valoarea lui n:");
    if (scanf("%d", &n) != 1) {
        printf("nu s-a tastat un intreg\n");
        exit(1);
    }
    if (n < 0 || n > 170) {
        printf("n nu apartine intervalului [0, 170]\n");
        exit(1);
    }
    for ( f = 1.0, i = 2 ; i <= n ; i++)
        f *= i;
    printf("n = %d\tn! = %g\n", n, f);
}

4.19. Sa se scrie un program care citeste pe n si a, calculeaza si afiseaza valoarea lui an, n este numar intreg nenegativ, iar a este un numar oarecare. Calculele se fac in flotanta dubla precizie.

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

main()
{
    int n, i;
    double a, p, f;

    printf("Baza a = ");
    if (scanf("%Lf", &a) != 1) {
        printf("nu s-a tastat un numar\n");
        exit(1);
    }
    printf("Exponentul n = ");
    if (scanf("%d", &n) != 1 || n < 0) {
        printf("nu s-a tastat un intreg nenegativ\n");
        exit(1);
    }
    for ( i = 0, p = 1.0L ; i < n ; i++)
        p *= a;
    printf("a = %Lg\tn = %d\tp = %Lg\n", a, n, p);
}

Observatii:

  1. Functia pow, de prototip: double pow(double x, double y); poate fi utilizata pentru date de tip double;
  2. Metoda utilizata in programul de fata nu este eficienta pentru n relativ mare, deoarece necesita multe inmultiri. Numarul lor poate fi redus substantial procedand ca mai jos.

Fie f = a. Executand repetat instructiunea de atribuire f = f * f se genereaza puterile:

(1)

a2, a4, a8, a16, a32, ...

adica exponentii lui a sunt puteri ale lui 2.

Termenii sirului (1) se genereaza printr-un numar relativ mic de inmultiri. an se poate exprima folosind numai factori din sirul (1) si eventual si pe a, daca n este impar. Acest lucru rezulta din faptul ca orice numar natural pozitiv se poate exprima ca o suma de puteri ale lui 2. Intr-adevar, nu avem decat sa reprezentam numarul n in binar si sa insumam puterile lui 2 din sirul:

(2)

1, 2, 4, 8, 16, 32, 64, ...

care corespund cifrelor 1 din reprezentarea in binar.

Corespondenta dintre termenii sirului (2) si cifrele reprezentarii binare se face de la dreapta la stanga, adica 1 se pune in corespondenta cu ultima cifra a reprezentarii binare, 2 cu penultima, 4 cu antepenultima etc.

Exemplu:

Fie n = 43. In binar, n se reprezinta astfel:

n = 101011

Rezulta ca n este suma termenilor 1, 2, 8 si 32 alesi din sirul (2) conform cifrelor 1 din reprezentarea binara a sa:

n = 1 + 2 + 8 + 32 = 43

Pentru a calcula pe an va fi suficient sa realizam produsul factorilor a2, a8, a32 din sirul (1) si a.

Din cele de mai sus rezulta ca pentru a reduce numarul inmultirilor la calculul lui an este suficient sa generam factorii sirului (1) si sa-i alegem pe cei ai caror exponenti insumati ne dau pe n. Selectarea lor se poate face simplu pornind de la reprezentarea binara a lui n. Astfel, daca ultima cifra binara a lui n este 1, atunci se selecteaza ca prim factor chiar a. Apoi, facand o deplasare spre dreapta a lui n cu o pozitie binara, se alege sau nu factorul a2, dupa cum ultima cifra binara a lui n, dupa ce s-a facut deplasarea, este 1 sau nu. Procedeul continua pana cand n devine egal cu 0.

Procesul de calcul se defineste astfel:

  1. p = 1
  2. f = a
  3. cat timp n este diferit de 0 se executa:
      3.1. daca ultima cifra binara a lui n este egala cu 1, atunci p = p * f
      3.2. se genereaza termenul urmator din sirul (1): f = f * f
      3.3. n se deplaseaza spre dreapta cu o pozitie binara.

Se observa ca punctul 3.2. este necesar numai daca n > 1. Intr-adevar, daca n = 1, atunci procesul de calcul se termina, deoarece prin deplasarea lui n la dreapta cu o pozitie binara acesta devine egal cu 0 si deci ciclul nu se mai reia. Avand in vedere faptul ca termenii sirului (1) cresc rapid, este important sa suprimam termenul care s-ar genera pentru n = 1, deoarece cu toate ca el nu este necesar, calculul lui poate conduce la o depasire flotanta superioara. In acest fel, punctul 3.2. se scimba cu:

    3.2. daca n > 1 atunci f = f * f

Programul 073 ridica pe an folosind procesul de calcul descris mai sus.

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

main()
{
    int n, i;
    double a, p, f;

    printf("Baza a = ");
    if (scanf("%Lf", &a) != 1) {
        printf("nu s-a tastat un numar\n");
        exit(1);
    }
    printf("Exponentul n = ");
    if (scanf("%d", &n) != 1 || n < 0) {
        printf("nu s-a tastat un intreg nenegativ\n");
        exit(1);
    }
	i = n;
    for ( p = 1.0L, f = a  ; n ; n >>= 1)
        if ( n & 1 )
          p *= f; /* factor selectat din sirul (1) */
        if ( n & 1 )
          f *= f; /* genereaza termenul urmator din sirul (1) */
    printf("a = %Lg\tn = %d\tp = %Lg\n", a, i, p);
}

Observatii:

  1. exp1 realizeaza initializarile de la punctele 1 si 2 descrise mai sus: p = 1.0L si f = a;
  2. exp2, conditia de continuare a ciclului s-a redus la n; deci ciclul continua atat timp cat n este adevarat, adica diferit de 0;
  3. punctul 3.3. este partea de reinitializare a ciclului for si ea consta din expresia: n >>= 1, care deplaseaza pe n cu un ordin binar spre dreapta;
  4. expresia n & 1 are valoarea 1 daca ultimul ordin binar al lui n este egal cu 1. In acest caz factorul generat in variabila f se selecteaza, deci se inmulteste cu p.

4.8. Instructiunea do-while