next up previous contents
Next: Problema propusa Up: Pointeri Previous: Folosirea neadecvata a operatiilor   Cuprins


Problema rezolvata

Sa se scrie un program care gestioneaza date despre un grup de studenti. Pentru fiecare student se memoreaza numele si numarul sau matricol. Programul trebuie sa implementeze urmatoarele operatii:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <conio.h>

typedef struct
{
  char *nume;
  int nr;
} student;

#define SIZE sizeof(student)

typedef int (*comp)(void *, void *); /* tip pointer la 
                                        functia de comparare */
typedef student *pstud;

 /*--------------------------------------------------------*/
 /*                                                        */
 /* functia eroare afiseaza un mesaj de eroare,in          */
 /* cazul in care nu poate fi alocata dinamic o zona       */
 /* de memorie; apoi se opreste executia programului       */
 /*                                                        */
 /*--------------------------------------------------------*/

void eroare(void)
{
  puts(" \n **** eroare alocare dinamica de memorie ****");
  exit(1);
}

 /*--------------------------------------------------------*/
 /*                                                        */
 /* citeste numarul de studenti si datele acestora,        */
 /* aloca dinamic spatiu pentru tabloul de studenti,       */
 /* memoreaza numele si nr. matricol ale studentilor       */
 /*                                                        */
 /*--------------------------------------------------------*/
 
void citeste(int *n, pstud *tab)
{
  pstud t;
  char sir[40];
  int i;

  /* citeste numarul de studenti */
  printf("\n dati numarul de studenti:");
  scanf("%d", n);

  /* aloca spatiu pentru toate inregistrarile de tip student */
  if (!(t=(pstud)malloc((*n)*SIZE))) /* citeste1 */
    eroare();
  *tab=t;

  /* citeste datele tuturor studentilor */
  for (i=0; i<*n; i++, t++)
  {
    printf("\n nume: ");
    scanf("%s", sir);
    /* aloca dinamic spatiu pentru numele studentului */
    if (!(t->nume=(char *)malloc(strlen(sir)+1))) /* citeste2 */
      eroare();
    strcpy(t->nume, sir);
    printf("\n numar matricol:");
    scanf("%d", &t->nr);
  }
}
 /*--------------------------------------------------------*/
 /*                                                        */
 /* afiseaza numele si numarul matricol ale celor          */
 /* n studenti din tabelul a carui adresa este data        */
 /* in parametrul tab                                      */
 /*                                                        */
 /*--------------------------------------------------------*/

void afiseaza (int n, pstud tab)
{
  int i;

  puts("\n tabelul cu studenti ");
  for (i=0; i<n; i++, tab++)
    printf("\n%-30s %4d", tab->nume, tab->nr);
}

 /*--------------------------------------------------------*/
 /*                                                        */
 /* functia comp1 compara numele a doi studenti ale caror  */
 /* adrese sunt date de pointerii p si r                   */
 /*                                                        */
 /*--------------------------------------------------------*/

int comp1(void *p, void *r)
{
  return strcmp(((pstud)p)->nume, ((pstud)r)->nume);
}

 /*--------------------------------------------------------*/
 /*                                                        */
 /* functia comp2 compara nr. matricol a doi studenti ale  */
 /* caror adrese sunt date de pointerii p si r             */
 /*                                                        */
 /*--------------------------------------------------------*/
 
int comp2(void *p, void *r)
{
  return ((pstud)p)->nr - ((pstud)r)->nr;
}


 /*--------------------------------------------------------*/
 /*                                                        */
 /* functia cauta studentul cu adresa data de parametrul s */
 /* in tabelul cu adresa indicata de parametrul tab;       */
 /* tabelul este ordonat dupa criteriul specificat prin    */
 /* parametrul f                                           */
 /*                                                        */
 /*--------------------------------------------------------*/
 
void cauta(pstud s, pstud tab, int n, comp f)
{
  pstud t=NULL;
  int i;

  t=bsearch(s, tab, n, SIZE, f);
  if (t)
  { /* exista studentul cautat */
    i=t-tab;
    printf("\n studentul %s cu numarul matricol %d e al %d -lea 
                              in evidenta", t->nume, t->nr, i+1);
  }
  else
    printf("\n studentul nu se afla in evidenta");
}


 /*--------------------------------------------------------*/
 /*                                                        */
 /* elibereaza spatiul de memorie detinut de campurile nume*/
 /* ale  celor n studenti din tabel si apoi zona detinuta  */
 /* de tabel                                               */
 /*                                                        */
 /*--------------------------------------------------------*/

void elibereaza(pstud tabel, int n)
{
  int i;

  for (i=0; i<n; i++)
    free(tabel[i].nume);
  free(tabel);
}

 /*--------------------------------------------------------*/
 /*                                                        */
 /* functia meniu  afiseaza meniul programului             */
 /*                                                        */
 /*--------------------------------------------------------*/

void meniu(void)
{
  puts("\n c, C --- citeste tabel studenti");
  puts(" a, A --- afiseaza tabel studenti");
  puts(" n, N --- ordoneaza dupa nume");
  puts(" r, R --- ordoneaza dupa numar matricol");
  puts(" f, F --- cauta dupa nume");
  puts(" l, L --- cauta dupa numar matricol");
  puts(" x, X --- iesire din program");
}


void main(void)
{
  char opt;
  int n;     /* numarul de studenti */
  char nume[30];
  student s;
  pstud tabel=NULL; /* adresa tabloului cu studenti */

  while (1)
  {
    meniu();
    opt=tolower(getche());
    switch (opt)
    {
    case 'c':
      if(tabel) /* daca a existat anterior un 
                   alt tablou in memorie */
        elibereaza(tabel,n);
      citeste(&n, &tabel);
      break;
    case 'a':
      afiseaza(n,tabel);
      break;
    case 'n':
      qsort(tabel, n, SIZE, comp1);
      break;
    case 'r':
      qsort(tabel, n, SIZE, comp2);
      break;
    case 'f':
      printf("\n dati numele:");
      scanf("%s", nume);
      if (!(s.nume=(char *)malloc(strlen(nume)+1)))
        eroare();
      strcpy(s.nume,nume);
      cauta(&s, tabel, n, comp1);
      free(s.nume); /* elibereaza spatiul alocat pentru nume */
      break;
    case 'l':
      printf("\n dati numarul matricol:");
      scanf("%d", &s.nr);
      cauta(&s, tabel, n, comp2);
      break;
    case 'x':
      exit(0);
    default:
      puts("comanda gresita");
    }
  }
}

Observatii:

Din considerente de economie de memorie, implementarea tabloului cu studenti s-a facut dinamic; dupa ce s-a citit numarul de studenti, s-a alocat spatiul corespunzator (vezi linia /* citeste1 */). În plus, în tablou, în câmpul nume se pastreaza nu sirul de caractere pentru numele studentilor, ci un pointer la sirul de caractere. Astfel, în loc de a rezerva, pentru toate numele, acelasi numar de caractere, în momentul în care se cunoaste exact numele unui student, se aloca dinamic un spatiu de memorie de dimensiune egala cu lungimea acestuia (vezi linia /* citeste2 */). Adresa acestui spatiu se pastreza în câmpul nume.

Reamintim ca în limbajul C, mecanismul de transmitere a parametrilor este prin valoare. Prin urmare, o functie nu poate modifica valoarea unui parametru actual, chiar daca modifica parametrul formal corespondent. Pentru situatiile în care se doreste sa se transmita informatie de la functia apelata la cea apelanta prin intermediul parametrilor se procedeaza in felul urmator: la apelul functiei se transmite adresa obiectului care urmeaza sa se modifice, iar, în cadrul functiei, parametrul corespondent se declara de tipul pointer. Astfel, functia apelata cunoaste adresa obiectului respectiv si îl poate modifica, operând indirect asupra lui, prin intermediul pointerului. Din aceste considerente, deoarece citirea numarului de studenti si alocarea spatiului pentru tablou se face în functia citeste, parametrii formali corespunzatori int *n si pstud *tab vor fi de tipul pointer. La apelul functiei citeste se vor transmite nu valorile lui n si tabel, ci adresele acestora: &n, &tabel.

La accesarea elementelor tabloului de studenti s-a folosit atât lucrul cu pointeri în functiile citeste si afiseaza, cât si varianta cu indici în functia elibereaza.

Pentru compararea, dupa nume, a doi studenti s-a implementat functia comp1. Similar, functia comp2 realizeaza compararea dupa numarul matricol.

Pentru sortarea tabloului se apeleaza functia de biblioteca qsort. Aceasta are urmatorul prototip:

void qsort(void *tab, int nr_elem, int nr_oct_elem, int (*fcmp)(void *, void *));

Parametrii indica adresa tabloului de sortat, numarul elementelor tabloului, marimea în octeti a fiecarui element si adresa functiei de comparare.

Dupa sortarea tabloului, cautarea unui student se face cu functia de cautare binara bsearch:

void *bsearch(void *cheie, void *tab, int nr_elem, int nr_octeti_elem, int (*fcmp)(void *,void *));

Parametrii indica cheia cautata, adresa tabloului, numarul elementelor tabloului, marimea în octeti a fiecarui element si adresa functiei de comparare. Functia returneaza adresa primului element care corespunde cheii, respectiv valoarea NULL daca nu exista un astfel de element.

În functia cauta, pentru a afla numarul de ordine al unui student în tabela, se scade din adresa elementului de tablou corespunzator acestuia (t) adresa de început a tabelei (tab).

Pentru ca programul sa functioneze corect, comenzile f si l trebuie date dupa ce în prealabil s-au dat comenzile n si respectiv r. (Se utilizeaza pentru cautarea unui student o functie de cautare binara, deci tabela trebuie sa fie anterior sortata!)


next up previous contents
Next: Problema propusa Up: Pointeri Previous: Folosirea neadecvata a operatiilor   Cuprins
Cristian Gavrila 2001-10-02