8.2. Realizarea apelului prin referinta utilizand parametri de tip pointer

2019/03/04 in Programare in C

Am vazut ca in limbajul C apelul este prin valoare. Aceasta inseamna ca la un apel, parametrilor formali li se atribuie valorile parametrilor efectivi care le corespund (vezi 4.15.).

In cazul in care un parametru efectiv este un nume de tablou, apelul prin valoare devine prin referinta, parametrului formal corespunzator lui i se atribuie ca valoare adresa primului element al tabloului. Deci, daca se face apelul:

int tab[...];
...
f(tab);
...

si f are antetul:

void f(int v[])

atunci la apel, v are aceeasi valoare cu tab. Aceasta inseamna ca tab[0] si v[0] exprima acelasi lucru, adica valoarea lui tab[0]. In general, tab[i] si v[i] au aceeasi valoare si anume valoarea elementului de indice i al tabloului tab.

Folosind pointeri, si in alte cazuri putem sa transformam apelul prin valoare in apel prin referinta. Astfel, daca x este o variabila simpla, atunci putem sa transferam, la un apel, in locul valorii lui x, adresa lui x:

int x;
...
h(x); /* se transfera valoarea lui x */
...
g(&x); /* se transfera adresa lui x */
...

In acest caz, cele doua functii vor avea antete diferite si anume:

void h(int i)

si

void g(int *pi)

In cazul functiei g parametrul pi este pointer spre date de tip int.

La apel, lui pi i se atribuie ca valoare adresa lui x. De aceea, functia g are posibilitatea de a modifica valoarea lui x. De exemplu, daca in corpul functiei g se utilizeaza atribuirea:

...
*pi = 100;
...

atunci valoarea 100 se atribuie variabilei x, a carei adresa s-a atribuit la apel lui pi.

In felul acesta, apelul prin valoare se poate folosi pentru a realiza apelul prin referinta.

In principiu, in limbajul C, apelul prin referinta se poate realiza daca parametrul efectiv are ca valoare o adresa, iar parametrul formal corespunzator este un pointer.

Exercitii:

8.1. Sa se scrie o functie care dintr-o data calendaristica definita prin numarul zilei din an si anul respectiv, determina luna si ziua din luna respectiva.

Aceasta functie a fost definita la exercitiul 6.7. Ea a avut trei parametri:

zz ziua din an;
an anul;
tzzll tablou de tip int de 2 elemente.

Functia atribuie ziua determinata elementului tzzll[0] si luna elementului tzzll[1].

In functia de mai jos se inlocuieste parametrul tzzll cu doi parametri de tip pointer.

void pluna_si_ziua(int zz, int an, int *zi, int *luna)

/* determina luna si ziua din luna;
  zz - ziua din an;
  an - anul;
  zi - pointer a carui valoare este adresa zonei de memorie 
     in care se pastreaza ziua determinata de functie;
luna - pointer a carui valoare este adresa zonei de memorie 
     in care se pastreaza luna determinata de functie. */
{
  int bisect = an%4 == 0 && an%100 || an%400 == 0;
  int i;
  extern int nrzile[];

  for(i = 1; zz > nrzile[i] + (i==2 && bisect); i++)
    zz -= (nrzile[i] + (i==2 && bisect));

  *zi = zz;
  /* pastreaza valoarea lui zz in zona de memorie 
  alocata parametrului efectiv corespunzator 
  parametrului formal zi */

  *luna = i;
  /* pastreaza valoarea lui i in zona de memorie 
  alocata parametrului efectiv corespunzator 
  parametrului formal luna */
}

8.2. Sa se scrie o functie care afiseaza caracterele unui tablou si citeste un intreg de tip int.

Functia are doi parametri:

text tablou unidimensional de tip caracter si care are aceeasi utilizare ca parametrul text din functia 6.3.
x pointer spre intregi de tip int; are ca valoare adresa zonei de memorie in care se pastreaza valoarea intregului citit.

Aceasta functie este asemanatoare cu functia definita la exercitiul 6.3. Diferenta consta in aceea ca functia definita in exercitiul 6.3. atribuie numarul citit variabilei globale v_int, in loc sa-l transfere functiei care face apelul, ca in cazul de fata.

Functia de fata returneaza aceleasi valori ca si cea amintita mai sus, adica 0 la intalnirea EOF si 1 altfel.

int pcit_int(char text[], int *x)
{
  char t[255];

  for( ; ; ) {
  printf(text);
  if(gets(t) == NULL)
    return 0;
  if(sscanf(t, "%d", x) == 1)
    return 1;
  }
}

Observatie: Deoarece x are ca valoare chiar adresa de inceput a zonei de memorie in care se pastreaza intregul convertit din ASCII in int prin functia sscanf, in apelul acestei functii se foloseste ca parametru efectiv chiar x. El nu mai trebuie precedat de operatorul adresa (&).

8.3. Sa se scrie o functie care citeste un intreg de tip int care apartine unui interval dat.

Functia are parametrii:

text tablou unidimensional de tip caracter care are aceeasi utilizare ca parametrul text din functia 6.4. (functia cit_int_lim).
inf intreg de tip int care are aceeasi utilizrae ca in functia cit_int_lim.
sup intreg de tip int care are aceeasi utilizrae ca in functia cit_int_lim.
pint pointer spre intregi de tip int; are ca valoare adresa zonei de memorie in care se pastreaza numarul citit.

Ca si cit_int_lim, functia de fata returneaza 0 la intalnirea EOF si 1 altfel.

int pcit_int(char text[], int *x); /* prototip */
int pcit_int_lim(char text[], int inf, int sup, int *pint)

/* citeste un intreg de tip int ce apartine 
intervalului [inf, sup] si il pastreaza in 
zona de memorie a carei adresa este valoarea 
parametrului pint;
returneaza 0 la intalnirea EOF sau, altfel, 1 */

{
  for( ; ; ) {
    if(pcit_int(text, pint) == 0)
      return 0; /* s-a intalnit EOF */
    if(*pint >= inf && *pint <= sup)
      return 1;
    printf("intregul tastat nu apartine intervalului:");
    printf("[%d, %d]\n", inf, sup);
    printf("se reia citirea\n");
  }
}

8.4. Sa se scrie o functie care citeste o data calendaristica compusa din zi, luna si an.

Functia valideaza data calendaristica respectiva. Ea are trei parametri de tip pointer:

pzi are ca valoare adresa zonei de memorie in care se pastreaza numarul zilei.
pluna are ca valoare adresa zonei de memorie in care se pastreaza numarul lunii.
pan are ca valoare adresa zonei de memorie in care se pastreaza anul.

Pentru validarea datei calendaristice se apeleaza functia v_calend, definita la exercitiul 6.5.

Functia de fata este analoga cu cea definita la exercitiul 6.8. Diferenta consta in utilizarea parametrilor si a functiei care citeste cele trei valori din compunerea unei date calendaristice. Astfel, functia de fata utilizeaza trei parametri de tip pointer spre int, pentru transferul celor trei variabile citite, spre deosebire de functia cit_data_calend definita in exercitiul 6.8., care utilizeaza in acelasi scop un tablou de tip int. De asemenea, functia de fata apeleaza functia pcit_int_lim pentru a citi cele trei valori ale datei calendaristice, spre deosebire de functia cit_data_calend care utilizeaza functia cit_int_lim.

int pcit_int_lim(char [], int, int, int *); /* prototip */
int pcit_data_calend(int *pzi, int *pluna, int *pan)

/* citeste o data calendaristica, o valideaza si 
o pastreza in zonele de memorie a caror adrese 
sunt definite de cei trei parametri formali de tip pointer; 
returneaza 0 la intalnirea EOF sau 1, altfel */

{
  static char ziua[] = "ziua: ";
  static char luna[] = "luna: ";
  static char anul[] = "anul: ";
  static char er[] = "s-a tastat EOF";

  for( ; ; ) {
  
    if(pcit_int_lim(ziua, 1, 31, pzi) == 0){
      printf("%s\n", er);
      return 0;
    }
	
    if(pcit_int_lim(luna, 1, 12, pluna) == 0){
      printf("%s\n", er);
      return 0;
    }
	
    if(pcit_int_lim(anul, 1600, 4900, pan) == 0){
      printf("%s\n", er);
      return 0;
    }
	
    if(v_calend(*pzi, *pluna, *pan))
      return 1;
    printf("data calendaristica este eronata\n");
    printf("se reia citirea datei calendaristice\n");
  }
}

8.5. Sa se scrie un program care citeste o data calendaristica si afiseaza data calendaristica pentru ziua urmatoare.

Acest program este analog cu cel definit la exercitiul 6.9. Programul de fat utilizeaza functii ce au ca parametri pointeri, spre deosebire de cel de la exercitiul 6.9., care apeleaza functii ce utilizeaza variabila globala v_int si parametri de tip tablou.

#include <stdio.h>
#include <stdlib.h>
#include "FUNCTIA094B.C" /*functia pcit_int */
#include "FUNCTIA094C.C" /*functia pcit_int_lim */
#include "FUNCTIA092C.C" /*functia v_calend */
#include "FUNCTIA092D.C" /*functia zi_din_an */
#include "FUNCTIA094A.C" /*functia pluna_si_ziua */
#include "FUNCTIA094D.C" /*functia pcit_data_calend */

int nrzile[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};

main()

/* citeste o data calendaristica, o valideaza 
si in caz ca este corecta, afiseaza data 
calendaristica a zilei urmatoare */

{
  int zz, ll, aa;

  /* citeste si valideaza data calendaristica */
  if(pcit_data_calend(&zz, &ll, &aa) == 0)
    exit(1);
  if(zz == 31 && ll == 12) {
  /* 31 decembrie, ziua urmatoare este 1 ianuarie din anul urmator */
    zz = 1; /* 1 */
    ll = 1; /* ianuarie */
    aa++; /* anul urmator */
  }
  else /* se determina ziua urmatoare */
    pluna_si_ziua(zi_din_an(zz, ll, aa)+1, aa, &zz, &ll);
  /* afiseaza data calendaristica a zilei urmatoare */
  printf("ziua: %d\tluna: %d\tanul: %d\n", zz, ll, aa);
}

Observatie: Parametrii efectivi &zz, &ll, &aa de la apelul functiei pcit_data_calend atribuie parametrilor formali corespunzatori adresele zonelor de memorie alocate variabilelor zz, ll si respectiv aa.

La revenirea din functia pcit_data_calend zonele de memorie alocate acestor variabile vor contine valorile corespunzatoare zilei, lunii si anului care au fost citite prin apelul respectiv (daca nu s-a tastat sfarsitul de fisier).

8.6. Sa se scrie o functie care citeste:

Functia are urmatorii parametri:

dmat tablou unidimensional de tip double in care se pastreaza elementele matricei prin liniarizare;
max maximul produsului m*n admis;
nrlin pointer a carui valoare este adresa zonei de memorie in care se pastreaza valoarea lui m;
nrcol pointer a carui valoare este adresa zonei de memorie in care se pastreaza valoarea lui n.

Aceasta functie este asemanatoare cu functia gdcitmat definita la exercitiul 5.1. Diferenta dintre ele consta in faptul ca functia gdcitmat nu utilizeaza parametri de tip pointer pentru a transfera valorile lui m si n in afara functiei, ci doua variabile globale.

int ndcit(int, double []); /* prototip */

int pdcitmat(double dmat[], int max, int *nrlin, int *nrcol)
{
  int i;
  char t[255];
  char er[] = "s-a tastat EOF\n";

  do {
    do {
      printf("numarul de linii = ");
      if(gets(t) == NULL) {
        printf(er);
        exit(1);
      }
      if(sscanf(t, "%d", nrlin) == 1 && *nrlin > 0 && *nrlin <= max)
        break;
      printf("nu s-a tastat un intreg din intervalul [1, %d]\n", max);
    } while(1);
	
    do {
      printf("numarul de coloane = ");
      if(gets(t) == NULL) {
        printf(er);
        exit(1);
      }
      if(sscanf(t, "%d", nrcol) == 1 && *nrcol > 0 && *nrcol <= max)
        break;
      printf("nu s-a tastat un intreg din intervalul [1, %d]\n", max);
    } while(1);
	
    i = *nrlin * *nrcol;
    if(i <= max)
      break;
    printf("produsul m*n = %d depaseste pe max = %d\n", i, max);
    printf("se reiau citirile lui m si n\n");
  } while(1);
  
  if(ndcit(i, dmat) != i) {
    printf("nu s-au tastat %d elemente\n", i);
    exit(1);
  }  
  return i;
}

Observatii:

  1. Functia ndcit este definita la exercitiul 4.37.
  2. Citirea numerelor m si n se poate realiza mai simplu folosind functia pcit_int_lim definita mai sus la exercitiul 8.3.

8.7. Sa se scrie un program care citeste elementele de tip double ale doua matrice a si b, calculeaza produsul lor si il afiseaza.

Daca c = a * b atunci:

c[i,j] = a[i,0]*b[0,j] + a[i,1]*b[1,j] +...+ a[i,n-1]*b[n-1,j]

pentru i = 0,1,...,m-1 si j = 0,1,...,s-1, unde:

a este o matrice de ordinul m*n;
b este o matrice de ordinul n*s.

Matricea produs c este de ordinul m*s.

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include "FUNCTIA086A.C" /*functia ndcit */
#include "FUNCTIA095.C" /*functia pdcitmat */

#define MAX 900

main()
{
    int i, j, k;
    int m, n, p, s, r, q;
    int mn, np;
    double a[MAX], b[MAX], c[MAX];

/* citeste matricea a */
    mn = pdcitmat(a, MAX, &m, &n);

/* citeste matricea b */
    np = pdcitmat(b, MAX, &p, &s);
    if (n != p) {
        printf("numarul coloanelor matricei a = %d \n", n);
        printf("difera de numarul liniilor matricei b = %d \n", p);
        exit(1);
    }

/* se realizeaza produsul c = a*b */
    for(i=0; i < m; i++) {
      q = i*n;
      for(j=0; j < s; j++) {
        r = i*s + j;
        c[r] = 0.0;
        for(k=0; k < n; k++)
          c[r] += a[q+k]*b[k*s+j];
      }
    }

/* afiseaza elementele matricei produs */
    printf("\n\n\t\tmatricea produs\n");
    k = 1;
    for(i = 0; i < m; i++) {
      p = i*s;
      for(j = 0; j < s; j++) {
        printf("c[%d, %d] = %8g ", i, j, c[p+j]);
        if( j%4 == 3) {
          /* afiseaza 4 elemente pe un rand */
          printf("\n");
          k++; /* numara randurile */
        }
        if(k == 23) {
          printf("actionati o tasta pentru a continua\n");
          getch();
          k = 1;
        }
      }
    printf("\n");
    k++;
    }
}

8.3. Legatura dintre pointeri si tablouri