Pointeri (declaratia Pointerilor)
Un mod obisnuit de
alocare asociere nume_variabila – zona de memorie este constanta pe toata
durata executiei programului(alocarea se face in faza de compilare iar
dealocarea pe parcursul executiei programului nu este posibila.
Pointerii sunt
varabile in care se pot memora adrese de memorie. Ei ofera posibilitatea de a
aloca dinamic memoria, adica pe parcursul executiei unui program se pot aloca sau dealoca zone de
memorie asociate lor.
Forma generala de alocare a unui pointer:
Tip *var; (pointer
catre int , void..)
Tip – tipul de date asociat
variabilei pointer cu numele var
Ex: int *x, int *y;
Void *z; double *p[20];
Obs: orice var
pointer trb inintializata cu o valoare valida(0 sau o anumita adresa),
obiectele au adrese diferite de 0 iar pentru 0 se foloseste constanta
NULL(stdio.h)).
Operatorii & si *
Obs: La o variabila pointer putem fi interesati de :
adresa pe care o memoreaza
valoarea ( informatia memorata la aceasta adresa )
amandoua
O metoda de a obtine adresele unor variabile pentru a fi
memorate intr-un pointer este oferita de operatorul &, numit operator de
referentiere sau adresare.
Operatorul * este numit operator de dereferentiere sau
indirectare. El permite accesul indirect la informatia memorata in zona de
memorie referita de pointeri.
Daca P este un pointer si contine valoarea unei adrese de
memorie, atunci *P furnizeaza continutul locatiei a carui adresa este memorata
in P.
int x,*p,;
p=&x;
Ceea ce este mai sus este corect si are intelesul ca adresa
var x este memorata in p.
Obs:
Declaratiile void * se folosesc in situatiile in care nu
trebuie precizat in mod expres tipurile pointerilor, tocmai pentru a permite
scrierea functiilor cat mai generale din punct de vedere al timpurilor pe care
le manevreaza.
Pointeri si tablouri
unidimensionale
In C numele unui tablou este echivalent cu un pointer constant
catre primul element al tabloului, adica sunt corecte referirile:
t,&t,&t[0], unde t este pointer catre tipul de baza al tabloului si
reprezinta adresa la care e memorat tabloul.
Pointeri catre tipul char si
sirul de caractere
Se pot enumera constante sir la o adresa indicata si de un
pointer catre un char:
char *a = “Suntem la curs” ;
Linia este valida deoarece intalnind-o, compilatorul v-a
memora sirul intr-o tabela de siruri generand sirul “a” catre adresa sirului.
Daca dorim sa afisam pe ecran mesajul continut in sir, este necesar
sa scriem:
printf(“a”);
printf(“/s”,a);
char *sir; in declaratie, sir poate avea mai multe
interpretari:
- sir este
pointer catre char;
- sir de
caractere;
- vector de
caractere alocat dinamic.
- Declararea
unui pointer catre un tablou unidimensional de dimensiune “dim” se face sub
forma:
tip (*var)[dim];
Ex:
int (*d)[10];
Pointerul “d” arata catre primul element al unui tablou format
din 10 intregi, iar orice incrementare a lui cu o unitate, t++ inseamna:
t = t+10* sizeof(int)
Obs:
Nu sunt echivalente :
1) tip *var[dim];
2) tip
(*var)[dim];
In 1) avem ca var este tablou de pointere catre tipul “tip”
in 2) avem pointeri catre un tablou cu “dim” componente de
tipul “tip”.
In 1) incrementarea cu o unitate inseamna pozitionarea pe
adresa urmatorului element al tabloului pe adresa urmatorului element al
tabloului, adica :
1) var++ ↔ var =
var + sizeof(tip)
2) var++ ↔ var =
var = var + 10 * sizeof(tip)
Au sens operatii de adunare si scadere a unui pointer cu un
nr. intreg, astfel:
p,n
p+n → adresa lui p + n*sizeof(tip) → tip este tipul
pointerului.
p-n → adresa lui p – n*sizeof(tip)
int *p1,*p2;
float *p;
1) p2=p1+3;
2) p-=5 ( alta scriere: p=p-5)
p2 contine p1+3*sizeof(int) sizeof(int)=2
p2=p1+3*2
p-=5
p=p-5*4
Daca x[n] este un tablou cu n componente, atunci pentru orice
i=0, i≤n
x=i ↔ &x[i]
*(x+i) ↔ x[i]
Scaderea a doi pointeri
P1 si P2 are sens in cazul in care P1 si P2 au acelasi tip de baza si se refera
la elementele aceluiasi tablou.
t=tablou
P1=&t[i1];
P2=&t[i2];
P1-P2→i1-i2
Structuri – reprezinta colectii de date neomogene, grupate sub
acelasi nume intr-o zona de memorie compacta. Declaratia
unei structuri are forma generala:
Struct nume_struct
{ Tip 1 camp 1
Tip 2 camp 2
Tip n camp n}
Lista variabila; - este
o lista de variabile a carui tip este dat de tip struct_nume struct
Obs:
nume_struct si lista_variabile sunt optionale, dar nu pot lipsii in acelasi
timp.
Operatorul „.” – selectarea unui camp
se face utilizand operatorul „.” Astfel:
Nume_variabila
structura.nume camp
Ex: pers1.nume; pers2.tel Obs:
asupra unui camp selectat se pot aplica toate operatiile care se pot aplica
unei variabile de acelasi tip cu campul.
Operatorul „->” – daca o variabila
este un pointer catre o structura accesul la un camp al structurii se face
utilizand operatorul sageata, sub forma:
Nume_pointer
structura->nume camp
Ex: struct rand
telefon *pers3; Pers3->nume
Obs: se remarca faptul ca operatorul sageata este o sinteza a
operatoriilor * si „.”. astfel expresia pers3->nume ↔(*pers3)nume
Operatorul
sizeof – marimea unei structuri se calculeaza folosiind operatorul sizeof.
Ex: sizeof(struct rand_tel)
Obs: este recomandata folosirea acestui operator si nu
calculul manual atat din motive de portabilitate cat si de corectitudine(in
unele situatii compilatorul pri aliniierile pe care le face obtine un necesar
de memorie mai mare decat dimensiunea obtinuta manual).
Initializarea
unei variabile de tip structura
O var de tip
structura poate fi initializata astfel:
Declaratie_structura={lista_valori
initiale};
Ex: struct rand_tel pers1={„ionescu”.”ion”.”ploiesti str
republicii”, 12345};
Copierea unei
informatii dintr-o structura in alta structura de acelasi tip este posibila
printr-o simpla instructiune de atribuire:
Ex: pers1=pers2; pers1
nume <- nume="" p="" pers2="">
return y; else return x; }
Pers1 pren <- p="" pers2="" pren="">
->
Structuri
implicate – o structura poate fi inclus in alta structura obtinandu-se
structuri implicate.
Ex: struct
info_pers
{
Struct rand_tel info;
Int varsta;
Float salariu; } angajat;
Angajat.info.nume
Obs: in interiorul unei structuri, numele campurilor trb sa
fie diferite , insa 2 structuri diferite pot avea nume de campuri identice.
Obs: atat numele structuri cat si numele unui camp al
structurii pot coincide cu numele unei variabile
Obs: o structura nu poate contine campuri care sunt de acelasi
tip cu structura in care apar, totusi campurile unei structuri pot fi pointeri
catre structura din care fac parte, acest lucru permite crearea unor structuri
recursive foarte utile in implementarea dinamica a cozilor, stivelor,
arborilor, graurilor.
in C exista posibilitatea sa accesam un bit din memorie prin
intermediul campurilor de biti.
Forma
generala a declaratiei unui camp de biti este:
Struct
nume_struct
{
Tip
nume_camp1: lungime;
Tip
nume_camp2: lungime;
Tip
nume_campn: lungime;
}lista
variabile;
Unde,
Lungime –
reprezinta numarul de biti al campului
Tip – poate
fi unsigned sau signed.
Obs: un camp de biti este defapt membrul unei structuri caruia
i se precizeaza lungimea(nr de biti). Din acest motiv accesul la informatia
obtinuta intr-un camp se face cu operatorii .(punct)(static) si
->(sageata)(dinamic)
Obs: nu toate facilitatiile oferite de structuri sunt valabile
si in cazul campurilor de biti. Astfel:
-nu se poate
utiliza operatorul & pt a lua adresa unui camp;
-numele unui
camp nu poate fi numele unui tablou;
-ordonarea campurilor
este dependenta de implementare.
Importanta
utilizarii campurilor de biti - is
gasesc utilizarea inspecial in programarea de sistem, deoarece permit
gestionarea regristriilor hardwear. Cu ajutorul lor se pot descrie eficient
anumite atribute de stare ale unor dispozitive. Ex: consideram un dispozitiv cu
6 componente , daca fiecarui componenta ii atasam un bit de stare (1 –
functioneaza, 0 – nu functioneaza), atunci starea intregului dispozitiv poate
fi descrisa prin structura urmatoare:
Struct stare_disp
{
Unsigned
camp1:1;
Unsigned
camp2:1;
Unsigned
camp6:1;
}stare;
Stare.camp4 –
furnizeaza starea acestei componente;
Avantaj: in loc sa foloseasca 6 octeti pt codificare se
foloseste doar 1 octet
O structura
poate contine atat campuri obisnuite cat si campuri de biti.
Ex: struct stare_student
{char
nume[40];
Unsigned nota:4;
Unsigned
adm:1;
Unsigned rest:3;
}student;
o uniune este o structura de date care permite folosirea in
comun a aceleiasi zone de moemorie de doua sau mai multe variabile diferite la
momente de timp diferite.
Forma
generala de declarea a unei uniuni:
Union
nume_union
{tip
nume_camp1;
Tip
nume_campn;
}lista_variabile
uniune;
Precizarile
facute la structuri referitaore la relatiile dintr uniuni, numele uniuni,
numele campurilor numele variabielelor din lista_var_uniune si numele oricareai
variabile raman valabile si aici.
Pentru
selectarea campurilor vom folosii operatorii „.” Si „->”. Operatorul sizeof
aplicat tipuli de date union v-a da lungimea uniunii. In acest caz sizeof va da
o valoare mai mare sau egala cu cea mai mare lungime a camporilor uniunii.
Deosebiea
fundamentala intre o uniune si o structura este modul in care capurile
utilizeaza memoria. La structura zonele rezervate campurilor diferite sunt si
ele diferite; la uniuni toate campurile impart aceiasi zona de memorie din
acest motiv datele memorate intr-o uniune pot fi referite diferit in functie de
tipul membrului pe care i-l avem in vedere.
Ex: union una
{int i;
Float f;
}v;
Datele din variabila v vor f privite ca intregi daca selectam
v.i si ca reale daca selectam v.f. campurle i si f se refera la aceiasi zona de
memorie. In v.i se memoreaza sizeof(int) iar in v.f se memoreaza sizeof(float).
Obs: este posibila intitializarea unei uniuni cu o constanta,
care trb sa fie de tipulr primului camp declarat.
Ex: union ceva
{int i;
Float f;
}={375};
Folosind
unuiunile si campurile de biti se poate accesa cu usurinta inormatia la nivel
de bit a unei variabile.
Pentru
interpretarea corecta a informatiei dintr-o uniune deobicei se pastreaza intr-o
varaibila ajutatoare, informatii despre continutul curent al uniunii.
O enumerare reprezinta o lista de constante intregi cu nume
Sintaxa:
enum nume_enumerare { lista enumerare } lista de variabile;
nume_enumerare si lista enumerare sunt optionale
Lista enumerare reprezinta toate valorile pe care le pot lua
variabilele din lista de variabile.
Ex:
0 1 2 3
enum culoare { rosu, alb, verde, albastru } culoarea_mea;
culoarea_mea = verde;
Elementele din lista de enumerare sunt constante intregi cu
valorile implicite 0,1,2……etc. Aceste constante nu se pot citi si nu se pot
scrie in mod nemijlocit.
Ex:
Numerele prezente in lista enumerare pot primi in mod explicit
anumite valori.
enum saptamana (luni=1, marti, joi=4, vineri, sambata,
duminica) zi;
2 5 6 7
zi=vineri;
switch (zi)
case luni:printf (“\n
luni”);break;
…..
case vineri:printf
(“\ vineri”);break;
scopul typedef este de a atribui denumiri alternative la
tipurile existente. De cele mai multe ori se foloseste la tipurile a caror
declaratie este greoaie sau poate varia de la o implementare la alta.
Typedef struct { tip_camp1 nume_camp1;
Tip_camp2 nume_camp2;
............................................
Tip_campn nume_campn;
}
nume_tip_nou;
Nume_tip_nou nume_var;
Nume_tip_nou *nume_pointer;
O functie poate fi definita sau declarata. Definirea functiei
cuprinde antetul functiei (declaratorul) si corpul functiei. Declararea unei
functii se refera doar la elementele din antetul ei.
Definirea
unei functii
In limbajul C exista 2 forme de definire a unei functii: forma
moderna si cea clasica. Desi strandardul ANSI C recomanda folosirea formei
moderne, totusi, din motive de portabilitate, standardul accepta si forma
clasica de definire a functiei
In definirea moderna are forma:
tip nume_functie (liste declaratii parametrii)
{
corpul functiei
}
Tip reprezinta rezultatul intors de functie.
Se impart in: functii care nu intorc un rezultat (void) si
functii care intorc un rezultat
Functiile care intorc un rezultat se impart in: functii care
intorc valori intregi si functii care nu intorc valori intregi.
Functiile care intorc un rezultat de tip intreg trebuie sa
aiba precizat in mod explicit cuvantul int sau sa nu aiba precizat nimic.
Lista declaratii parametrii trebuie sa contina cel putin tipurile
parametrilor. In mod curent lista contine numele parametrilor precedati de
tipurile lor.
f(int, int);
float f(int x, float y);
{ …. }
Nu pot sa am numai x si y, nu pot sa am numai f(int x,y)
Cand lista de parametrii este vida trebuie pus intre paranteze
cuvantul (void). Corpul functiei este format din declaratii locale si
instructiuni. O functie care intoarce o valoare trebuie sa contina cel putin o
instructiune return.
Intructiunea return are forma return (expresie), unde expresie
poate sa lipseasca. O functie poate sa contine mai multe instructiuni return.
In cazul in care return lipseste, revenirea in functia
apelanta se face in momentul in care se intalneste acolada de sfarsit.
Expresia din return (expresie) se atribuie practice numelui
functiei. Din acest motiv numele functiei si tipul expresiei trebuie sa fie
compatibile sau de acelasi tip.
Conversiile necesare se fac exact dupa regulile folosite la
atribuire.
Sintaxa unei definitii clasice pentru o functie este
urmatoarea:
tip nume-functie (par1, par2, … parn)
tip par1;
tip par2;
…
tip parn;
{ instructiuni }
float f(x,y) int x; float y; { if (x
->
float (x,y) int x,y; { … }
Orice functie care returneaza valori de tip diferit de int trb
declarata sau definita inainte de a fi apelata.
Declaratiile
trb facute la inceputul programului(inainte de definirea oricarei functii pt a
informa compilatorul asupra tipului rezultat si a putea genera un cod corect).
Exista doua modalitati
de a declara o functie:
1. traditionala
tip_nume_functie(); - declaratie
ex: double aria();
.......................
Double
aria(float a)
{
Const
pi=3,1415
Return
pi *r*r;
}
Obs: in forma traditionala lipseste orice referire la
parametrii functiei.
2. moderna
tip_nume_functie(lista_declaratii_parametrii);
unde, lista_dec_parametrii poate sa fie vida, caz in care
intre paranteze se va scrie cuvantul void(void) sau poate sa contina numai
lista tipurilor parametrilor.
Permite compilatorului se verifice corectitudinea apelului
unei functii(se fac veriicari privind identitatea intre numarul parametrilor
prototipului si numarul parametrilor actuali, iar daca este necesar, parametrii
actuali se convertesc la tipul parametriilor corespunzatori din prototip
inainte de a fi plasati in stiva)
Nume_functie(lista_parametrii),
Unde lista parametrii – lista de valori actuale(efective),
care vor fi transmise functiei. Din acest motiv parametri din aceasta lista se
numesc actuali sau efectivi.
Obs: la apelul functiei intre parametrii actuali si cei
formali trb sa exista o corespondenta pozitionala(p actuali si formali situati
pe pozitii indentice trb sa aiba tipuri identice sau compatibile). Compilatorul
va sesiza lipsa de identitate dintre numarul parametrilor actuali si cei
formali.
Conversile permise sunt cele considerate la operatiile de
atribuire.
Transferul informatiei intre functia apelanta si unctia
apelata se poate face prin valoare sau prin referinta.
Transferul prin valoare – consta in copierea valorilor
parametriilor actuali in zona de memorie a parametrului formal corespunzator in
momentul efectuarii apelului.
Obs: val parametrului actual in aces caz nu este afectata de
prelucrarile din cazul functiei apelate(aceste prelucrari vizeaza copii ale
parametrilor actuali si nu parametrii insasi).
Transferul prin referinta – pentru ca functia apelata sa paote
modifica valoarea unei variabile indicata ca parametru actual trb sa i se
cunoasca adresa. Solutia este ca parametru formal corespunzator sa fie declarat
pointer si sa i se transmita la apel adresa variabilei si nu valoare.
Apelul functiilor cu parametrii actuali-tablouri
O exceptie de la regula transmiterii prin valoare o reprezinta
cazul cand parametrul actual este un tablou. Deoarece copierea valoriilor
componentelor tabloului intr-un parametru formal ar fi constituit un consum
mare de timp s-a adoptat solutia transmiterii catre fucntie doar a adresei de
inceput a tabloului. In consecinta parametrul transmis poate fi numele tabloului
care este un pointer catre primul element al tabloului.
Parametrul corespunzator poate fi declarat intr-una din
modalitatiile urmatoare:
a) este declarat
ca tablou cu dimensiunea egala cu dim tabloului transmis;
b) este declarat
ca tablou fara a da dim primei componente;
c) este declarat
ca pointer.
Functii recursive – recursivitatea inseamna procesul prin care
o entitate de un anumit tip se poate descrie , definii sau prelucra folosid
entitatii de acelasi tip.
Pe vom referii la functii recursive: care au proprietatea de a
se autoapela.
Exista 2 tpuri de functii recursive:
1. Direct
recursive – f() se numeste functie recursive daca in corpul ei apar apeluri la
ea insasi adica este de forma:
Tip f(lista_declaratii_parametrii);
{
Apel f()
}
2. Indirect
recursive – functile f() si g() sunt mutal recursive daca f() contine apeluri
la g() iar g() contine apeluri la f()
Tip f(lista_declaratii_parametrii);
{
Apel g()
}
Obs: intelegerea notiunii de functie recursive se sprijina pe cunoasterea
modului in care se face apelul unei functii.
Orice programa C compilat imparte spatiu de memorie destinat
in patru zone distinct:
1-zona cod program
2-zona variabile globale
3-zona de manevra (heap) destinata alocarii dinamice a
variabilelor
4-zona de stiva(stak)
Stiva este un caz particular de lista ale carei principale
operatii de introducere si extragere sunt guvernate de disciplina LIFO(last in
first out),
Functile direct recursive
Cazul de recursivitate cel mai des intalnit este cazul functilor
direct recursive(in aceasta situatie functia apelanta este identical cu functia
apelata).
Adancimea recursivitatii este data de numarul de autoapelari.
Pt a nu se autoapela la infinit corpul functiei trb s acontina cel putin o
instructiune “if” care pe baza testarii unei conditii asigura dupa un timp
oprirea autoapelului si executia instructiuniilor amanate prin autoapel.
Ex: int f(int n)
{if(n==0) return 1;
Return n+f(n-1);}
Obs: pt o serie de problem , rezolvarea folosind functii
recursive reprezinta singura soluie viabila din punct de vedere practica. Ex:
quick sort, problema turnurilor din Hanoy
Problema turnurilor din Hanoy:
Se dau trei tije notate a, b, c, pe tija a se ala n discuri
perforate de diameter diferite asezate unul peste celalalt asezate in ordine
descrescatoare a diametrelor. Sa se gaseasca toate mutarile prin care toate
cele n discuri de pe tija a sunt aduse pe tija b in aceiasi ordine utilizand
tija c ca o tija ajutatoare. O mutare inseamna depplasarea unui singur disc de
pe o tija pe alta peste un disc de diametru mai mare.
Clasa de memorare "auto"
Variabilele declarate in interiorul functiilor sunt implicit
automate. De aceea, clasa "auto" este cea mai cunoscuta dintre toate.
Daca o instructiune compusa (bloc) incepe cu declararea unor variabile, atunci
aceste variabile sunt in domeniu in timpul acestei instructiuni compuse (pana
la intalnirea semnului }).
Exemplu:
auto int a, b, c;
auto float f;
Declaratiile variabilelor in blocuri sunt implicit automate.
La executie, cand se intra intr-un bloc, se aloca memorie
pentru variabilele automate. Variabilele sunt considerate locale acestui bloc.
Cand se iese din acest bloc, sistemul elibereaza zona de memorie ocupata de
acestea si deci valorile acestor variabile se pierd. Daca intram din nou in
acest bloc, atunci se aloca din nou memorie pentru aceste variabile, dar
vechile valori sunt necunoscute.
Clasa de memorare "register"
spune compilatorului ca variabilele asociate trebuie sa fie memorate in
registri de memorie de viteza mare, cu conditia ca aceasta este fizic si
semantic posibil. Daca limitarile resurselor si restrictiile semantice
(cateodata) fac aceasta imposibila, clasa de memorare register va fi inlocuita
cu clasa de memorare implicita "auto". De obicei, compilatorul are
doar cativa astfel de registri disponibili. Multi sunt folositi de sistem si
deci nu pot fi alocati.
Folosirea clasei de memorare "register" este o
incercare de a mari viteza de executie a programelor. De regula, variabilele
dintr-o bucla sau parametrii functiilor se declara de tip "register".
Exemplu:
{
register int i;
for (i = 0; i < LIMIT; ++i)
{
. . . . .
}
} /* la iesirea din bloc, se va elibera registrul i */
Declaratia
register i;
este echivalenta cu
register int i;
Daca lipseste tipul variabilei declarata intr-o clasa de
memorare de tip "register", atunci tipul se considera implicit
"int".
Declaratiile "static"
au doua utilizari distincte si importante:
a) permite unei variabile locale sa retina vechea valoare cand
se reintra in bloc (sau functie) (caracteristica ce este in contrast cu
variabilele "auto" obisnuite);
b) folosita in declaratii externe are alta comportare (vom
discuta in sectiunea urmatoare);
Pentru a ilustra a), consideram exemplul:
Exemplu:
void f(void)
{
static int contor = 0;
++contor;
if (contor % 2 == 0)
. . . . .
else
. . . . .
}
Prima data cand functia este apelata, "contor" se
initializeaza cu 0. Cand se paraseste functia, valoarea lui "contor"
se pastreaza in memorie. Cand se va apela din nou functia "f()",
"contor" nu se va mai initializa, ba mai mult, va avea valoarea care
s-a pastrat in memorie la precedentul apel. Declararea lui "contor"
ca un "static int" in functia "f()" il pastreaza privat in
"f()" (adica numai aici i se poate modifica valoarea). Daca ar fi
fost declarat in afara acestei functii, atunci si alte il puteau accesa.
Clasa de memorare "extern"
O metoda de transmitere a informatiei in blocuri si functii
este folosirea variabilelor externe. Daca o variabila este declarata inafara
functiei, atunci acesteia i se aloca permanent memorie si spunem ca ea apartine
clasei de memorare "extern". O variabila externa este considerata
globala tuturor functiilor declarate dupa ea, si chiar dupa iesirea din blocuri
sau functii, ea ramane permanent in memorie.
Exemplu:
#include
int a = 1, b = 2, c = 3;
int f(void);
void main()
{
printf("%3d\n", f());
printf("%3d%3d%3d\n", a, b, c);
}
int f(void)
{
int b, c; /* b si c sunt locale, deci b, c globale sunt
mascate */
a = b = c = 4; /* valoarea lui a se modifica */
return(a + b +c);
}
Pt tratarea lor unitara, fisierele pe astfel de dispozitive
sunt private ca secvente de octeti. Pozitia octetului current este memorata intr-o
variabila speciala numita indicator de pozitie. Pe baza indicatorului de
pozitie se poate face modificarea octetului current, citirea sau scrierea la o
anumita pozitie in fisier. Dupa efectuarea operatiilor de citire sau scriere,
idicatorul de pozitie este incrementat cu un numar de octeti, practic un
dispozitiv fizic este transformat de sistemul de fisiere al lb C intr-un
dispozitiv logic denumit flux.
Un flux reprezinta un pointer la o entitate de tip FILE care
contina informatii despre: pozitia currenta in flux, idicatori de eroare,
indicatori de sfarsit de fisier, zone tampon associate. Exista 2 categorii de
fluxuri:
1. Text –
presupune transferul de caractere organizate in linii de caractere (o linie se
termina prin caracterul newline) intr-un flux text pot intervenii anumite
conversii de character si de aceea este posibil sa existe anumite nepotriviri
intre caracterele introduce in flux si cele rezultate in urma transferului.
2. Binary –
reprezinta o succesiune de octeti care nu suporta nici o conversie in timpul
transferului
Obs.: lansarea in executie a unui program C are drept
consecinta si creearea in mod automtat a trei fluxuri standard: stdin, stdout,
stderr sunt pointeri constanti catre tipul file. Celelate fisire ale
programului trb deschise in mod explicit cu ajutorul functiei fopen() care
conecteaza un flux la un anumit fisier.
Prototipul functiei fopen() este:
FILE *
fopen(const char * nume, const char * mod);
Unde, nume – numele fisierului, precedat eventual de calea de
acces
Mod – un sir
de caractere care precizeaza modul deschiderii, astfel, caracterul text sau
binar al fisierului si respective scopul deschiderii: pt citire, scriere,
adaugare sau citire-scriere.
-text
Mod= “r”
citire;
“s”
scriere;
“a”
adaugare;
“r+”
citire si scriere;
“w+”
citire si scriere;
“a+”
citire si scriere rin adaugarea la sfarsitul fisierului
-binar
Mod=
“sb”;”ab”…..
Obs.: pt a indica explicit modul text se poate adauga sufixul
“t”. valorile mod de tipul “r+b” “r+t” sunt echivalente cu “rb+”, “rt+”…
Incercarea de
a deschide un fisier care nu exista in mod citire conduce la eroare;
Daca un
fisier este deschis pt scriere in modul “w”, atunci informatile continute in el
va fi distrusa.
Daca fisierul
este deschis pt scriere si acel fisier nu exista atunci el va fi creat;
Daca se
deschide un fisier in modul “a” , datele vor fi adaugate la sfarsitul
fisierului, in cazul in care fisierul exista, iar daca acesta nu exista el va
fi creat;
Daca operatia
de deschidere a unui fisier este realizata cu success atunci functia fopen()
creaza o structura FILE si intoarce adresa sa. Indicatorul de pozitie ia
valorea 0 daca fisierul este deschis in modurile “r” sau “w” si o valoare egala
cu numarul de octeti ai fisierului in cazul in care fisierul este deschis in
mod “a”;
Daca operatia
de deschidere nu a avut success – ori nu exista ori e protejat la scriere sau
riscul sa fie plin, in cazul acesta fopen() intoarce valoarea NULL.
Operatia
simetrica de deschidere a unui fisier, anume ceea de inchidere este realizata
cu ajutorul functiei fclose(), care are prototipul:
Int
flclose(FILE *fp);
Fp –
pointerul returnat de functia fopen;
Operatia de inchidere presupune:
Deconectarea
fluxului de fisiere(fapt important deoarece nu pot fi deschise un numar infinit
de fisiere simultan)
Golirea
buff-erului de iesire in fisier, daca fisierul a fost deschis in mod scriere
sau in mod actualizare.
Abandonarea
datelor necitite din buff-erul de actualizare daca fisierul este deschis pt
citire sau actualizare.
Daca operatia de inchidere are success functia va intoarce
valoarea 0. In caz contrar EOF(are valorea -1)
Functia de citire a unui carcter dintr-un fisier: int
fgetc(FILE *fp); intoarce valorea carcterului citit daca operatia a avut
success sau EOF cand s-a atins sfrasitul fisierului sau cand operatia nu are
success
Functia de scriere unui carcter intr-un fisier: int fputc(int
ch, FILE *fp); scrie in fisier un carcter pe care i-l returneaza daca operatia
a avut success in caz de eroare intoarce EOF
Functii pt citirea si scriere sirurilor de caractere(intr-un
fisier)
Scriere: int fputs(const char *sir, FILE *fp); - scrie in
fisier la adresa sirului, in caz de success intoarce ultimul carcter scris, in
caz de fail da EOF
Citire: char * fgets(char *str, int lg, FILE *fp); - citeste
un sir de lungimea cel mult lg-1 carctere si la transfera in siruri str, daca
intalneste un character new line, citirea este oprita iar carcterul va fi incorporate
in str, sirul va fi completat automat cu carcterul \0 in caz de success functia
intoarce adresa lui str iar in caz de insucces sau sfarsit de fisier se va
intoarce valoarea NULL.
Fscanf() –citire
Fprintf() – scriere
O modalitate simpla de a realize operatii de transfer cu
format este oferita de functiile de mai sus. Ele functioneaza exact ca
functiile scanf() si printf() la nivelul consolei.
Prototipuri:
Int fscanf(FILE *fp,
const char*sir_format); - intoarce numarul de valori pt care citirea, conversia
si memorarea datelor a fost facuta correct, daca apare eroare inainte a incepe
citirea functia intoarce EOF
Int fprintf(FILE *fp, const char*sir_format); - intoarce
numarul de caractere scrise efectiv in cazul in care operatia a avut success
sau o valorea intreaga negativa in caz contrar.
Functiile fread si fwrite – pentru tranferul blocurilor de
date
In ciuda avantejelor evidente, transferul cu format al datelor
este mai lent decat transferul datelor sub forma binara datorita conversilor in
format ASCII. De asemeni , n general, fisierul creat cu date ASCII formatate
foloseste memorie mai multa decat un fisier binary.
O modalitate eficienta pentru transferul unui volum mare de
date o reprezinta folosirea acestor functii fread si fwrite.
Size_t fread(void *buffer, size_t nr_oct, size_t nb, FILE
*fp); - citeste din fisierul asociat pointerului fp nb blocuri , fiecare avand
lungimea nr_oct octeti si le transfera in zona de moemorie indicata de
pointerul buffer. Functia intoarce numarul de blocuri citite efectiv. In cazul
in care s-a ajuns la sfarsitul fisierului sau daca a aparut o eroare valoarea
intoarsa este strict mai mica decat acest nb
Size_t fwrite(const void *buffer, size_t nr_oct, size_t nb,
FILE *fp); - scrie in fisierul fp din
zona buffer nb blocuri , fiecare avand lungimea egala cu nr_oct octeti.
Valoarea intoarsa de aceasta functie este egala cu numarul de blocuri scrise efectiv
daca operatia a avut success sau un numar mai mic decat nb in cazul in care
functia da eroare.
Size_t este
de tip “stdio.h” – au semnficatia unui intreg fara semn
#define numere_macro sir caractere
Nume
macro – identificatorul care v-a fi inlocuit in text de secventa de character
Ex:
#define M 20 =/= m=20; – m v-a fi inlocuit cu 20
For(i=0;i
Y=x[i]+7;
Ex: #define b 20
#define c 30
#define
d 40
#define
ARIA b*c
#define
VOLUM ARIA*d
v=VOLUM
!!!!!!!Nu
trebuie confundata substitutia in text care se face utilizand directive define
cu o atribuire.
A=(B+5)*D
=> A=25*20=500
#define
B 20
#define
C B+5
#define
D 20
A=C*D =>
A=B+5*20=120
!!!!
directive #define poate fi parametrizata obtinanduse o macrodefinitie
#define
F(x) ((x*x)+1) – va conduce la inlocuirea secventei cu textul din partea
dreapta unde parametrii formali sunt inlocuiti cu functii effective
#define nume_macro(parametrii)
Y=f(3-1)=(3-1*3-1+1)=0
gresit
Y=f(2)=((2*2)+1)=5
bun
#include „nume_fisier” sau #include
Efectul
directive consta in includerea fisierului in codul sursa unde este activate
directive
In
cazul in care are o specificatie si o cale de cautare completa preproc cauta
direct in calea respectva indifferent de “” sau de <>, altfel el tine
cont de ele, <>- fisierul va fi cautat intr-unul sau mai multe directoare
standard, “”-intai se cauta in directorul current apoi in cele standard.
|
|
Functii video
(prezentare generala)
Functii grafice – in c nu s-au standardizat functiile grafice.
Pentru borland c
Tinta oricarei reprezentari grafice este sa aduca imaginea de
pe ecran la nivelul de performanta al unei fotografii. Grafica se poate realiza
cu ajutorul interetelor grafice sau al adaptoarelor video.
Acestea contin memorie ram video care permite reinprospatarea
permanenta a imaginii de pe vdeo monitor. In mod traditional exista 2 moduri de
lucruri:
1. Text
2. Grafic
Adaptoare video: MDA (monochrome display adaptor);
CGA (color graphyics adpater);
EGA (Enchanced Graphycs Adapter);
VGA (Video Graphics Arrey Adapter)
1.In modul TEXT se lucreaza cu caractere iar carcterul este
reprezentat prin 2 octeti.Primul contine codul ASCII iar al doilea atributele
de afisare. BFFFFCCC – F=codul culorii backgroudul; C-codul cuorii;B-blink
Ecranul este
privit ca o retea celulara , fiecare celula poate memora un carcter. Originea
ecranului este in coltul din stanga sus (1:1).
|
1:1
|
2:1
|
3:1
|
|
|
|
|
|
1:2
|
|
3:2
|
|
|
|
|
|
1:3
|
|
|
|
|
|
|
|
1:4
|
|
|
|
|
|
|
O modalitate foarte rapida de afisare si care nu consuma multa
memorie.
2.In modul grafic ecranult este vazut k o retea foarte fina in
modul PIXEL (picture element). Pixelul este ceea mai mica zona care poate fi
„aprinsa” pe ecran. Culoarea pixel este reprezentata intr-un numar de biti in
memoria video.
Orginea este tot in coltul stanga sus si are coordonatele
(0:0).
Adaptoarele video sunt carcterizate prin rezolutie spatiala si
rezolutie de culoare. Rezolutia spatiala in modul text se refera la numarul de
linii si numarul de coloane, iar in modul grafic inseamna numarul de linii si
numarul de pixeli pe linie
Rezolutia de culoare – are in vedere numarul de culorii
afisate simultan.
Modul grafic necesita procesoare de mare performanta si
capacitatea de memorie sporita pt a reprezenta pixelii.
Modul text permite reprezentari grafice rudimentare, iar modul
grafic permite reprezentari grafice de mare finete. In ambele moduri exsta
posibilitatea de apreciza zone dreptunghiulare pe ecran unde se pot afisa
texte, grafice, acestea se numesc ferestre. Definirea ferestrelor se poate face
cu niste functii grafice speciale in care se precizeaza coltul din stanga sus
si coltul din dreapta jos.