CURSUL 5

STRUCTURI

Utilitate:

Exemple.
  1. Numerele complexe (parte reala, parte imaginara)
  2. Un punct in spatiu (coordonatele x,y si z)
  3. Entitatea student (nume, an_admis, media fiecarui an de studiu)
 
Exemplul 1. Numerele complexe
struct Complex {
    float re;
    float im;
};
// s-a declarat doar tipul de date struct Complex
// 're' si 'im' se numesc membrii structurii Complex
 
struct Complex z1, z2; // s-au declarat
// variabilele structurate z1 si z2
double modul;
// ...
z1.re=1.;
z1.im=-1; // z1 = 1 - i;
modul = sqrt (z1.re * z1.re + z1.im * z1.im); //modul=sqrt(2.)

Exemplul 2. Un punct in spatiu
struct punct{
    float x, y;
    float z;
} a; // s-a declarat tipul structurat si
// o variabila structurata (a)
 
struct punct b, tetraedru[4]; //s-a declarat o variabila structurata
                             //si un tablou de structuri
double dx,dy;
//...
a.x = a.y = a.z = 0.;
b.x = a.x + dx;
b.y = a.y + dy;
b.z = 1;
printf("\nIntroduceti coordonatele varfurilor tetraedrului");
for(i=0;i<8;i++) scanf("%f%f%f",
                    &tetraedru[i].x, &tetraedru[i].y, &tetraedru[i].z);
 
Exemplul 3. Student, an de studiu
 
struct {
    char nume[20];
    unsigned an_admis;
    float medii[5];
} Vasile, an5[30];
int i,j, nstud;
// ...
gets(Vasile.nume);
Vasile.nume[0] = toupper (Vasile.nume[0]);
scanf("%u", &Vasile.an_admis);
// ... anul V ...
// preluarea mediilor anilor precedenti
for ( i=0; i<nstud; i++ ) {
    printf("\nMediile studentului %s\n", an5[i].nume);
    for(j=0;j<5;j++) {
        printf("Media anului %d =",j+1);
        scanf("%f", &an5[i].medii[j]);
    }
}
// ...

Sintaxa declaratiilor de structuri
 

<declaratie_structura> ::= <specificator_structura> <lista_var> ;
<specificator_structura> ::= struct <nume_structura> |
                                          struct <nume_structura> { <declaratii_membri> } |
                                          struct { <declaratii_membri> }
<declaratii_membri> ::= <specificator_tip> <lista_var>;
<specificator_tip> ::= <specif_tip_simplu> | <decl_tablou> | <specificator_structura>

Referirea membrilor unei structuri

Atunci cand se utilizeaza numele variabilei structurate se foloseste operatorul '.' (punct).
Prioritatea si asociativitatea acestui operator este la fel cu cea a parantezelor.
 
Membrii unei structuri pot fi variabile simple, pointeri, tablouri, sau chiar structuri. Utilizarea lui typedef este permisa .

Exemplul 4. Un segment de dreapta in plan este reprezentat printr-o structura ce are ca membri 2 puncte (extremitatile). Fiecare punct este reprezentat printr-o structura ce are ca membri coordonatele in plan.

struct Punct2D {
    int x;
    int y;
};
typedef struct Punct2D PUNCT;
 
typedef struct {
    PUNCT a;
    PUNCT b;
} SEGMENT;
 
SEGMENT om;
 
// segmentul OM cu capetele in (0,0) si (1,2)
om.a.x = om.a.y = 0; //observati utilizarea repetata a operatorului .
om.b.x =1;
om.b.y =2;
}
Datorita asociativitatii de la stanga spre dreapta a operatorului de referentiere . expresia om.a.x este echivalenta cu (om.a).x Continuand exemplul, putem lua in considerare o structura ce reprezinta un triunghi prin coordonatele celor trei varfuri:

typedef struct Triung { PUNCT a,b,c; } TRIUNG;

Dar un triunghi ar putea fi reprezentat si printr-un segment si un punct exterior segmentului:

typedef struct Triung {
    SEGMENT lat;
    PUNCT c;
} TRIUNGHI;
TRIUNGHI trng;

Referirea coordonatelor celor trei varfuri se face astfel:

trng.lat.a.x = 0; trng.lat.a.y = 1; //observati diferenta in
trng.lat.b.x = 2; trng.lat.b.y =3; //referirea extremitatilor
trng.c.x = -1; trng.c.y = -2; //segmentului si varful extern

Observatii.
1. Expresia trng.lat.a.x este echivalenta cu ((trng.lat).a).x datorita asociativitatii operatorului de referentiere.
2. Spatiile identificatorilor pentru numele structurilor, membrii structurilor si numele de tipuri date prin typedef sunt diferite. Declaratii ca cea de mai jos sunt corecte:

typedef struct TA { float min,max, TA; } TA;
TA pacient, dim, seara;

Initializarea structurilor

Toate variabilele structurate externe si statice, neinitializate explicit de programator, vor avea toti membrii initializati cu zero. In ANSI C sunt initializate cu zero si structurile din clasa auto si neinitializate explicit.

Variabilele structurate pot fi initializate explicit intr-un mod similar cu initializarea tablourilor (se pune semnul egal urmat de o lista de valori cuprinse intre acolade).

Exemplu 5. Initializare numere complexe
Se creeaza un fisier exstru4.h avand urmatorul continut:

#ifndef EXSTRU4_H
#define EXSTRU4_H
    typedef struct Complex {
        double real;
        double imag;
    } COMPLEX, *PtCOMPLEX;
#endif
#include <stdio.h>
#include <alloc.h>
#include "d:/c/exstru4.h"
void main ()
{
    COMPLEX z1={2,-3};
    COMPLEX vetor[4]= { {1.,1.}, {0.,1.}, {-5,7} };
                                                //vector[3] este {0.,0.}
    typedef COMPLEX MATRICE_C[4][3];
    MATRICE_C mat={
        { {-1.,-2.}, {0.,10.} }, //mat[0][3] este {0.,0.}
        { {2.,-8}, {5,9}, {4.,6.} },
        { {3,5.} }               // mat[2][1] si mat[2][2] sunt {0.,0.}
    }; // linia mat[3] are elementele {0.,0.}
}

Exemplu 6. Initializare cu constante sir

struct Persoana {
    char *nume;
    char *adresa;
    unsigned varsta;
} x, v = { "Vasilescu V.", "Suceava, str.Universitatii nr.1", 46};

struct Persoana ={0}; // toate campurile sunt initializate cu 0

Observatie. Pentru initializarea variabilei x in afara declaratiei trebuie alocata dinamic memorie pentru campurile nume si adresa, sau atribuiri ca cele de mai jos:

x.nume = "Vasilescu Ion"; // se atribuie pointeri
x.adresa = Vasile.adresa; // idem

Sunt gresite insa incercarile de citire de genul gets(x.nume); sau scanf("%s", x.nume); deoarece in campurile respective se pot memora adrese de siruri (pointeri la caracter) si nu siruri.

Aplicatie. Program de calcul al procentajului de promovati la un examen, in cadrul unei grupe de maxim 30 studenti. Pentru fiecare student tn parte se va introduce numele (maxim 50 caractere) si nota obtinuta la examen. Un student este reprezentat printr-un element al unui tablou de structuri care retine un pointer la caracter (indica adresa din memorie unde s-a stocat numele studentului) si un intreg (nota).

// Program Exstruc2.c
// Citire date si calcul procentaj promovati
// PSG/23/03/2000
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "D:/C/cit_int.c"
#define NSTUD 30
typedef struct STUDENT {
    char *nume;
    unsigned char nota; // notele sunt de la 1 la 10
} STUDENT;
 
void main ()
{
    STUDENT gr1111[NSTUD];
 
  int CitesteDateGrupa ( STUDENT []);
  int Integralisti (STUDENT gr[], int nstud);
 
  int nstud = CitesteDateGrupa( gr1111);
 
  printf("\nPromovati %.2f%%",
  (double)Integralisti ( gr1111, nstud )/nstud*100.);
  // ... alte prelucrari
  // La sfarsit se elibereaza memoria ocupata cu numele studentilor
  //
  for( ; nstud; nstud--) free( gr1111[nstud].nume );
}

#define NCAR 51
#define STERGE_NL(x) if(x[strlen(x)-1]=='\n') x[strlen(x)-1]=0;
int CitesteDateGrupa ( STUDENT grupa[])
{
  int i=0;
  char buf[NCAR];
  printf("\nIntroduceti numele si nota. Cand ati terminat"
         " dati <CR> la 'Nume student'.\n");
  do {
        printf("Nume student:");
        flushall(); //curata buffer intrare de '\n' lasat de scanf
        fgets(buf, NCAR, stdin); //dupa flushall citirea are loc
        STERGE_NL( buf ); //direct din Intrare si nu din buffer
        if(buf[0]==0) return i;
        grupa[i].nume = malloc ( strlen(buf) + 1 );
        printf("Nota=");
        scanf("%d",&grupa[i].nota);
  } while (++i<NSTUD);
  return i;
}
//------------------------------------------------------
int Integralisti (STUDENT gr[], int ns )
{
int nRest=0, i;
for(i=0; i<ns; i++ ) nRest += gr[i].nota<5;
return ns-nRest;
}

Referirea prin pointeri la structuri

Referirea membrilor unei structuri se poate face si prin intermediul unui pointer la variabila structura. In acest caz operatorul de referentiere este -> (minus, mai mare), care are aceeaºi prioritate si asociativitate cu operatorii . ( )[ si ].

Exemplu 7. Se reia exemplul cu numere complexe:
 
struct Complex {
    double real;
    double imag;
} z, *pz=&z;
// ...
pz->real=2.;
pz->imag=-3.; // z= 2 -3i
printf("\nz1=%lf+(%lf)i", z.real, (*pz).imag);

Expresiile pz->imag si (*pz).imag sunt echivalente (in exemplul de mai sus referind ambele membrul z.imag).
 
Variabilele structurate pot fi alocate dinamic.
Exemplul 8. Fisierul ce contine functia main() este exstru4.c unde se va aloca o variabila structurata ce va fi initializata cu -5+7i si apoi afisata.

// Program Exstru4.c
#include <stdio.h>
#include <alloc.h>
#include "d:/c/exstru4.h"
void main ()
{
 PtCOMPLEX pz;
 //.. structura alocata dinamic
 pz = (PtCOMPLEX) malloc (sizeof(COMPLEX));
 pz->real =-5;
 pz->imag=7.; // z = -5 + 7 i
 printf("\nz2=%lf %c %lfi", (*pz).real, pz->imag>=0?'+':'\b', (*pz).imag);
}
 
Exemplul 9. Sa consideram o structura care are ca membri structuri si un pointer la o alta structura.
// Program Exstru7.c
// Structuri ce contin ca membri alte structuri
// PSG/24/03/2000
#include <stdio.h>
#include <string.h>
void main ()
{
  struct adresa {
      char strada[20];
      int nr;
  } adrAlex;
  struct data { int zi,luna,an; } ziAlex={24,3,80};
  struct Pers {
        char *nume;
        struct adresa *padr;
        struct data nascut;
  } Alex , *pAlex= &Alex;
  //
  strcpy(adrAlex.strada, "str.Mirauti");
  adrAlex.nr=24;
  Alex.nume="Bonus Alexandru"; //nume este pointer
  Alex.padr= &adrAlex;
  Alex.nascut = ziAlex; //atribuire de structuri
  //
  printf("\nNume:%s (%c.%c.)\nNascut %d/%d/%d\nAdresa: %s nr.%d",
               Alex.nume,
               *pAlex->nume, pAlex->nume[6], //sau *(pAlex->nume+6),
               Alex.nascut.zi, pAlex->nascut.luna, (*pAlex).nascut.an,
               Alex.padr->strada, (*Alex.padr).nr    );

}

Programul produce urmatoarea afisare:
Nume:Bonus Alexandru (B.A.) 
Nascut 24/3/80 
Adresa: str.Mirauti nr.24
 

Structuri si functii

O structura poate fi transmisa ca argument al unei functii sau poate fi returnatã ca rezultat.
Atunci cand este transmisã ca argument, transferul este prin valoare, deci se va efectua o copie locala a structurii. Acest lucru poate fi prohibitiv din punctul de vedere al memoriei, daca structura are ca membri tablouri, sau are un numar mare de membri. Mai convenabil este ca o structura sa fie transmisa prin intermediul unui pointer. In exemplul de mai jos sunt ilustrate diverse situatii posibile.

Exemplul 10.
// Program Exstru6.c
// Exemple functii si structuri
// PSG/24/03/2000
#include <stdio.h>
#include <alloc.h>
#include "d:/c/exstru4.h"
COMPLEX suma (COMPLEX a, COMPLEX b);
PtCOMPLEX dif (PtCOMPLEX a, PtCOMPLEX b);
void afis (char *text, COMPLEX z);
void main ()
{
    COMPLEX z1={2,-3}, z2={-5,7.}, zs, *pd;
    zs = suma (z1, z2 ); // argumente structuri
    pd = dif (&z1, &z2); // argumente pointeri la structuri
    //...
    afis("z_suma" , zs);
    afis("z_dif ", *pd); //argumentul 2 trebuie sa fie o structura
    free (pd); // eliberare spatiu alocat in functia dif()
}

COMPLEX suma (COMPLEX a, COMPLEX b)
{
    COMPLEX rez;
    rez.real = a.real + b.real;
    rez.imag = a.imag + b.imag;
    return rez;
}
PtCOMPLEX dif (PtCOMPLEX a, PtCOMPLEX b)
{
    PtCOMPLEX p;
    if( (p = (PtCOMPLEX) malloc( sizeof(COMPLEX)) )==NULL) return NULL;
    p->real = a->real - b->real;
    p->imag = a->imag - b->imag;
    return p;
}
void afis (char *text, COMPLEX z)
{
    printf ("\n%s=%.2lf + (%.2lf)i", text, z.real, z.imag);
}

[Continuare - cursul 6]   [Pagina principala]