Acum este 19 Apr 2018, 20:43

Ora este UTC + 2 [ DST ]




Scrie un subiect nou Răspunde la subiect  [ 1 mesaj ] 
Autor Mesaj
 Subiectul mesajului: [Tutorial] Programare C pe Linux
Mesaj nouScris: 06 Aug 2009, 08:48 
Neconectat
Internaut expert
Avatar utilizator

Membru din: 25 Mai 2009, 10:44
Mesaje: 112
[Tutorial] Programare C pe Linux
M-am gandit sa fac un scurt tutorial de programare C pe linux, am impartit acest tutorial in sub-tutoriale, care arata cam asa:

Variabile
Constante
Operatori
Functii
Tablouri
Enumeratii
Comentarii
Domeniul variabilelor
Conversii de tip
Instructiuni

if-else
switch
while
for
do-while
break si continue
goto

Pointeri
Tablouri si pointeri
Tablouri de pointeri
Pointeri la functii
Recursivitatea
Functia main
Structuri de autoreferire
typedef
Bibloteci standard C

Voi incerca sa abordez fiecare subiect, in functie de timpul liber sper sa termin in 2-3 zile.

Inainte de a incepe tutorialul propriu-zis vreau sa fac o precizare, si anume : Programele C care utilizeaza doar bibliotecile standard pot fi compilate si executate pe orice combinatie masina-compilator, fara nici o modificare, in situatia in care nu utilizeaza facilitati specifice unui anumit compilator. Conversia unui program sursa de pe o masina pe alta se numeste portare.
Pasii care trebuie urmati in vederea crererii unui fisier executabil sunt :
1. Se creeaza fisierul-sursa, cu ajutorul unui editor de texte.
2. Se compileaza fisierul-sursa, in vederea generarii fisierului-obiect.
3. Se link-editeaza fisierul-obiect, generandu-se fisierul executabil.

Daca se utiliezeaza un mediu integrat, acesta include un editor de texte. In caz contrar poate fi utilizat orice editor de texte, cum ar fi emacs, vi, joe etc.
Pentru a urma traditia manualelor de C, voi prezenta in continuare cel mai scurt program posibil, care va afia arhicunoscutul mesaj " hello, world " :
Cod:
#include <stdio.h>
main()
{
printf("hello, world\,n");
}

Presupunand ca fisierul-sursa in care a fost salvat programul de mai sus se numeste hello.c, compilarea acestuia se poate face utilizand comanda
Cod:
gcc -o hello hello.c

Fisierul executabil rezultat se va numi hello si poate fi lansat in executie folosind comanda:
Cod:
./hello

Acestea fiind spuse, putem incepe.
1. Variabile
Variabilele sunt unitatile de informatie care se pot modifica de-a lungul executiei programului. Ele au o durata de viata bine determinata, in functie de locul si modul de declarare.
Numele variabilelor se specifica prin intermediul identificatorilor si sunt alcatuite din litere si cifre; primul caracter este intotdeauna o litera, poate fi utilizat si caracterul "_", care joaca tot rol de litera. Literele mari si mici sunt considerate distincte ( case sensitive ), in mod conventional, litere mici sunt folosite pentru variabile, iar cele mari pentur constante simbolice. Numarul de caractere din cadrul cuvantului este nelimitat, totusi o serie de cuvinte-cheie nu pot fi utilizate ca nume de variabile :
auto break case char const continue default do double else enum extern float for goto if int long register return short signed sizeof static struct switch typedef unsigned union void volatile while
Exista urmatoarele tipuri de date de baza in C :
char un caracter
int un numar intreg
float un numar flotant in simpla precizie
double un numar flotant in dubla precizie

In plus exista 2 calificatori care pot fi aplicati tipului int:short si long, referindu-se la marimi intregi. De asemenea, tipurilor char si int li se pot aplica calificatorul unsigned ( fara semn ). Cuvantul int poate fi omis in aceste cazuri.
Pentru a afla valoarea minima, respectiv maxima pe care o poate memora o variabila de tip numeric trebuie consultat fisierul /usr/include/limits.h, care contine definitii de genul CHAR_BIT - numarul de biti dintr-un char, CHAR_MAX- valoarea maxima a unui char etc.
Toate variabilele trebuie declarata inainte de a fi utilizate. O declaratie mentioneaza un tip si este urmata de una sau mai multe variabile de acel tip, specificate prin numele acestora si separate prin virgula :
Cod:
int x,c,v;
char s[154];

Variabilele pot fi, de asemenea, initializate in momentul declararii lor, cu o valoare initiala, precizand dupa numele acestora semnul egal "=" si o constanta :
Cod:
int i = 0;
char s[20] = " salutare ";

De remarcat este faptul ca la delcararea sirului de caractere s se alica in mod automat 20 de caractere,chiar daca lungimea valorii initiale este mai mica de atat. Variabilele nu sunt initializate implicit de catre compilator. Variabilele neinitializate au o valoare nedefinita, cu alte cuvinte programatorul nu trebuie sa se bazeze pe o anumita valoare a acesteia.

Variabile de tip const
Modificatorul de acces const este o promisiune a programatorului pentru compilator ca valoarea variabilei nu va fi modificata dupa initializarea sa. Daca aceasta nu este initializata, compilatori o va initializa cu valoarea 0. In acest sens de remarcat ca declaratia de genul const char *str = " hello " este un pointer catre o informatie care nu poate fi modificata. Pointerul in sine poate fi modificat ( despre asta vom discuta mai tarziu ).
Variabilele de acest tip sunt utile in general pentru declararea parametrilor functiilor. Spre exemplu, o functie care contorizeaza numarul de caractere dintr-un sir nu trebuie sa modifice continutul acestuia :
Cod:
#include <stdio.h>
int lungime_sir(const char *sir)
{
    int contor = 0;
    while(*sir++)
     contor ++;
    return contor;
}

Variabile de tip register
Cuvantul cheie register precieaza compilatorului ca variabila precizata este des utilizata si ar trebui memorata intr-una dintre registrele procesorului. Pe compilatoarele moderne, nu este necesar sa fie utilizat. Multe dintre aceste compilatoare ignora aceasta directiva.
2. Constante
O constanta numerica desemneaza o valoare numerica fixata.
Sufixul “L” sau “l” atasat unei constante numerice forteaza reprezentarea sa ca fiind de tip long. Sufixul “U” sau “u” forteaza reprezentarea unsigned, iar daca depaseste dimensiunea maxima a acestuia, va fi automat de tip unsigned long. Cele doua sufixe pot fi utilizate impreuna.
Toate constantele in virgula flotanta sunt prin definitie de tip double. Fortarea la tipul float poate fi facuta prin adaugarea sufixului “F” sau “f”.
Pentru constantele numerice sunt permise si notatiile de genul 123.456e-7 sau 0.123E3. Exista de asemenea o notatie speciala care permite exprimarea de valori octale si hexazecimale : “0” la inceputul unei constante insamna ca aceasta este exprimata in octal; prefixul 0x inseamna ca valoarea este exprimata in hexa.
O constanta de tip caracter este un singur caracter, scris intre apostroafe ' '. Valoarea este corespondentul numeric al caracterului in setul de caractere al masinii. De exemplu, in setul de caractere ASCII, caracterul “0” are valoarea 48. Constantele de tip caracter participa in operatiile numerice ca orice alte valori numerice, de exemplu in cadrul operatiei de comparatie cu alte caractere.
Anumite caractere standard pot fi reprezentate cu ajutorul secventelor escape, cum ar fi \n ( linie noua ), \t ( tab ), \\ ( backspace ), \0. . . ( corespondentul valorii specificate in octal ) sau \0x. . . ( corespondentul valorii specificate in hexa ).
O constanta-sir de caractere este o secventa compusa din zero sau mai multe caractere, intre ghilimele duble “, ca “ Hello, world !” sau “ “ ( un sir nul ). Secventele escape descrise mai sus sunt valabile si in acest caz. Utila poate fi secventa \”, care inlocuieste caracterul ghilimele duble ( imposibil de realizat ca atare ).
Practic, un sir de caractere este un tablou ale carui elemente sunt caractere de tip char. Compilatorul plaseaza in mod automat un caracter nul, \0 ( avand practic valoarea numerica zero ), la sfarsitul sirului, caracter denumit terminator nul. Functia urmatoare, lungime_sir(s), returneaza lungimea unui sir de caractere s, primit ca argument, exclusiv terminatorul \0 :
Cod:
#include <stdio.h>
int lungime_sir(char *s)
{
   int i = 0;
   while(s[i] != '\0')
     i++;
   return i;
}

La manipularile de siruri de caractere trebuie avut in vedere ca este necesar a fi alocata suficienta memorie pentru a memora intregul sir, inclusiv acest caracter de terminare.
“!= “ este un operator relational si desemneaza doua valori distincte.

Constantele simbolice
O constanta simbolica este un identificator cu valoare de constanta. Valoarea constantei poate fi orice sir de caractere introdus prin constructia #define. Exemplu #define X 100. Dupa intalnirea constructiei compilatorul va inlocui toate aparitiile constantei X cu valoarea 100. Numele constantelor simbolice scriu de obicei cu litere mari ( dar nu e obligatoriu ).
3. Operatori
Operatorii specifica operatiile care se fac cu variabile si constante, conform tipurilor acestora.

Operatori de atribuire

Operatorul de atribuire este semnul egal "=". Valoarea din dreapta semnului egal e atribuita variabilei din stanga. Intr-o instructiune se pot face
mai multe atribuiri

Cod:
rez = x = y;

Operatori aritmetici

Operatorii aritmetici binari sunt "+", "-", "*", "/" si operatorul modulo, "%". Exista operatorul unar "-", dar nu exista operatorul unar "+".
De notat este faptul ca impartirea intregilor trunchiaza orice parte fractionara. Operatorul "%" nu poate fi aplicat la float sau double.

Operatori relationali

Acesti operatori permit compararea a doua valori. Daca rezultatul comparatiei este fals, valoarea rezultata este 0 ( zero ), respectiv 1
in caz contrar. Operatorii relationali sunt: > ( mai mare ), >= ( mai mare sau cel mult egal ), < ( mai mic ), <= ( mai mic sau cel mult egal ), == ( egal ), != ( inegal ).

Operatori logici

Acestia pot fi && ( SI logic ), || ( SAU logic ), numiti si conectori logici, si "!" ( negatie logica ). Expresiile care contin operatori logici sunt evaluate
de la stanga la dreapta, iar evaluarea se opreste in momentul in care se cunoaste rezultatul ( cu alte cuvinte, daca rezultatul nu mai poate fi modificat de operatiile ramase,
acestea sunt abandonate ).
In C nu exista un tip de date corespunzator tipului bool, acesta existand in schimb in C++. In cazul operatiilor logice, o valoare nenula ( in mod conventional1 ) este considerata
ca avand valoarea de adevar adevarat, iar valoarea 0 desemnand fals.
Operatorul unar de negatie "!" converteste un operand non-zero sau adevarat in zero si un operand cu zero in fals in 1. Exemplu :

Cod:
if(!ok)

este echivalenta cu
Cod:
if(ok == 0)


Operatorii de incrementare si decrementare

Operatorul de incrementare ++ aduna 1 la operandul sau, iar -- scade 1 din operand. Acesti doi operatori pot fi utilizati atat ca prefix ( inaintea variabilei, e.g. ++i ), cat si ca sufix ( dupa variabila, e.g. i++ ).
Efectul asupra operandului este acelasi, dar folosirea cu prefix modifica variabila inainte de a-i folosi valoarea, in timp ce utilizarea ca sufix modifica variabila dupa ce valoarea sa a fost folosita in cadrul expresiei.
Folosind aceasta facilitate, putem simplifica unele constructii, cum ar fi functia de calcul a lungimii unui sir de caractere:

Cod:
#include <studio.h>
int lungime_sir(char *s)
{
   int i = 0;
   while(s[i++] != '\0');
   return i;
}

Operatori logici pe biti

Operatorii logici pe biti sunt urmatorii : " &" ( SI logic cu bit ), "|" ( SAU logic pe bit ), "^" ( SAU EXCLUSIV bit cu bit ), << ( deplasarea bitilor spre stanga ), >> ( deplasarea bitilor spre dreapta ),
si "~"( complement fata de 1 ). Acesti operatori nu se pot utiliza pentru valori de tip float sau double.
Operatorul SI "&" este utilizat in general pentru a masca anumiti biti ai unui numar. Mascarea bitilor reprezinta selectarea unir anumiti biti dintr-un octet. Pentru aceasta, se aplica operatia SI bit cu bit
cu un numar avand setati bitii respectivi pe 1. De e.g., pentru a verifica bitul cel mai putin semnificativ a variabilei flags, efectuam flags & 1.
Operatorul SAU "|" este folosit pentru a selecta anumiti biti pe 1 :

Cod:
x= x | 12;

seteaza pe 1 bitii 2 si 3 din ex, deoarece 12 ( zecimal / in baza 10 ) = 1100 ( binar / in baza 2 ).
Operatiile de deplasare << si >> realizeaza deplasari la stanga, respectiv la dreapta pentru operandul din stanga lor, cu numarul de pozitii dat de operandul din dreapta lor. Deplasarea la stanga va provoca
umplerea locurilor libere din dreapta cu zero. Deplasarea la dreapta va umple bitii liberi din stanga cu zero. Spre exemplu:
4 << 1 = 12 si 12 >> 2 = 2, deoarece 2 ( zecimal ) = 10 ( binar ), 4 ( zecimal ) = 100 ( binar ) si 12 ( zecimal ) = 1100 ( binar ).
Operatorun unar "~" furnizeaa complementul fata de 1 al unui intreg, adica converteste fiecare bit de 1 in 0 si viceversa.

Operatorii compusi de asignare

Majoritatea operatorilor binari permit atasarea unui operator de asignare de forma operator=: +=, -=, *=, /=, %=, <<=, >>, &=, |= si ^=. Astfel, espresii de genul i = i + 1 pot fi simplificate prin i +=1.
Functia de mai jos calculeaza numarul de biti setati pe 1 dintr-un numar intreg :
Cod:
#include <studio.h>
int bitcout(unsigned n)
{
   int i;
    for(i=0; n != 0, n >>= 1)
     if( n % 1)
      i++;
   return i;
}

4. Functii

Functiile repreyinta un set de instructiuni grupate intr-un bloc de program. Ele permit impartirea programelor in mai
multe subrutine mai mici. Functiile ascund deseori detaliile de implementare ale unor operatii specifice, pe care
programatorii nu trebuie neaparat sa le cunoasca. Astfel, functiile usureaza munca de scriere a codului, faciliteaza
lucrul in echipa si introduc ideea de reutilizare a codului ( aceasta idee permite programatorilor sa utilizeze munca
altora, fara a incepe totul de la capat ). Functiile pot fi localizate si fizic si in fisiere diferite, reducand in acest
mod dimensiunile codului-sursa. De asemenea, functiile pot sa rezide in biblioteci ( cum ar fi cele standard ).
Functiile pot primi un numar nelimitat de argumente, pentru fiecare in parte trebuie declarat tipul si numele.
Numele este necesar pentru a putea accesa respectivul argument in cadrul functiei. Argumentele functiei sunt trimise
prin valoare, adica functia apelata primeste o copie temporara, locala, a fiecarui argument si nu adresa sa. Ca urmare,
functia nu poate afecta valoarea originala a argumentului din functia apelanta. Atunci cand argumentul este un tablou
( e.g. un sir de caractere ), este trimista adresa de inceput a tabloului, deci apelul se face prin referinta,
iar elementele nu sunt copiate. Exista de asemenea posibilitatea ca o functie sa primeasca un numar variabil
de argumente.
Functiile pot returna o valoare al carei tit trebuie sa preceada numele functiei. Daca o functie nu returneaza
nici o valoare, tipul declarat trebuie sa fie void. Instructiunea return specifica iesirea din functie si ea poate
fi urmata de o valoare, daca este cazul. Nu este obligatoriu ca o functie void sa apeleze instructiunea return la
sfarsitul acestora.
In exemplul urmator vom utiliza functia lungime_sir:

Cod:
#include <stdio.h>
int lungime_sir(char *s)
{
int i = 0;
while(s[i++] != '\0');
return i;
}
void main()
{
char s[1000];
printf("Introduceti sirul de caractere: ");
gets(s);
printf("Sirul are %d caractere.\n", lungime_sir(s));
}


Pentru a putea utiliza o functie, aceasta trebuie mai intai declarata !! In cadrul declaratiei, numele argumentelor pot lipsi:
Cod:
int lungime_sir(char *);


In mod uzual, declaratia unei functii care este definita in alt fisier se face intr-un fisier-antet ( header ),care
poate fi inclus in orice fisier-sursa care utilizeaza functia respectiva. Fisierul-antet poate contine si declaratii
de variabile, constante etc:
functii.h
Cod:
int lungime_sir(char *);


global.c
Cod:
#include "functii.h"
int lungime_sir(char *s)
{
int i = 0;
while(s[i++] != '\0');
return i;
}


main.c
Cod:
#include <stdio.h>
/* Linia de mai jos poate fi inlocuita cu #include "global.c" */
#include "functii.h"
void main()
{
char s[1000];
printf("Introduceti sirul de caractere: ");
gets(s);
printf("Sirul are %d caractere.\n", lungime_Sir(s));
}


Compilarea acestor fisiere se va face cu:
Cod:
gcc -o test global.c main.c

5. Tablouri
Un tablou multidimensional este practic o matrice. Declararea unui astfel de tablou se face cu:
tablou [dimensiune 1] [dimensiune 2] ... [dimensiune N]
Un tablou multidimensional se initializeaza printr-o lista de valori initiale scrise intre acolade. Pentru exemplificare vom implementa o functie care returneaza numarul de zile ale unei luni:
Cod:
int nr_zile(int luna, int an)
{
  static int tabzile[2][12] = {
   { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
   { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
  };
int bisect = an % 4 == 0 && an % 100 != 0 || an % 400 == 0;
return tabzile[bisect][luna -1];
}



6. Enumeratii

Tipul enumerare enum reprezinta o modalitate de a utiliza nume in loc de numere, pentru un tip comun de date:
Cod:
enum figura_geometrica {
cerc,
patrat,
triunghi
};


Numerelor cerc, patrat si triunghi le vor fi asignate in mod automat valorile 0, 1, respectiv 2. Numerele corespunzatoare numelor sunt considerate intregi. Pentru a asigna alte valori, se foloseste o constructie de forma:
Cod:
#include <stdio.h>
enum figura_geometrica {
   cerc = 10, patrat = 20, triunghi = 30
};
main()
enum figura_geometrica g = triunghi;
  printf(“Valoarea lui g = %d\n”, g);
}

7. Comentarii

Comentariile reprezinta anumite observatii introduse de programator, ele fiind ignorate la compilare.
Inceputul unui comentariu este marcat prin /*, iar sfarsigul acestuia prin */. Comentariile pot cuprinde mai multe linii, ca in exemplul de mai jos :

Cod:
/* comentariu
linia 1..
linia 2.. etc */


Pot fi folosite si comentarii imbricae, insa acestea nu sunt permise de unele compilatoare.

Inainte de a trece la “ Domeniul variabilelor “, vreau sa fac o mica precizare, si anume: un bloc local, este o portiune de program C incadrata de paranteza deschisa “ { “ si de paranteza inchisa “ } “ ( acolade ). O functie C contine aceste doua paranteze, asadar si ea reprezinta un bloc local.
Un bloc local poate aparea oriunde in cadrul programului. Variabilele declarate in interiorul uuni bloc sunt vizibile doar in cadrul acestuia, altfel spus, viata unei variabile incepe o data cu declararea acesteia si se incheie o data cu sfarsitul blocului. Variabilele cu acelasi nume declarate intr-un bloc local au precedenta fata de variabilele declarate in afara blocului. Acestea fiind spune putem trece mai departe. >>

8. Domeniul variabilelor

Una dintre punctele forte ale limbajului C este flexibilitatea sa in definirea stocarii datelor. Sunt doua aspecte care pot fi controlate in C: domeniul si timpul de viata a variabilelor. Domeniul unei variabile se refera la locurile in care variabila poate fi accesata. Timpul de viata se refera la momentul in care variabila poate fi accesata.
Exista 3 domenii ale variabilelor :
extern – implicit pentru variabilele declarate in afara oricarei functii. Domeniul variabilei declarate extern este intregul program.
static – domeniul unei variabile declarate static in afara oricarei functii este restul programului in respectivul fisier-sursa. Domeniul unei variabile declarate static in interiorul unei functii este restul blocului local.
auto – implicit pentru variabilele declarate in afara unei functii. Domeniul este restul blocului local.
Timpul de viata al unei variabile extern stau static dureaza dinainte ca functia main() sa fie apelata si ea sfarseste la iesirea din program. Timpul de viata al argumentelor functiilor dureaza pana la iesirea din functie ( apelul instructiunii retrun ). Timpul de viata al unei variabile alocate dinamic ( pointer ) incepe o data cu alocarea spatiului de memorie prin apelul malloc() si se incheie o data cu apelul functiei free() sau la incheierea executiei programului.
O variabila globala care trebuie sa poata fi accesata din mai multe fisiere trebuie declarata intr-un fisier-antet. O asemenea variabila trebuie definita foar intr-un singur fisier-sursa. Variabilele nu trebuie definite in fisiere-antet.
Declararea unei variabile desemneaza precizarea tipului sau catre compilator, dar nealocarea de spatiu de memorie pentru ea. Definirea variabilei inseamna declararea acesteia si alocarea de spatiu de memorie pentru ea:
extern int i; /* declaratie */
int n; /* definitie */

9. Conversii de tip

Atunci cand intr-o expresie apar operanzi de tipuri diferite, acestia sunt convertiti in mod automat intr-un tip comun.
In cadrul expresiilor aritmetice, conversia operanzilor se face dupa urmatoarele reguli:
Orice operand care nu este de tip int sau double este convertit astfel :

Cod:
Tipul                   |  Se converteste la tipul
char                              int
unsigned char                     int
signed char                       int
short                             int
enum                             int
float                             double


1.Daca unul dintre operanzi este de tip double, atunci si celalalt este convertit la double.
2.In caz contrar, daca unul dintre operanzi este de tip unsigned long, celalalt este convertit si el la unsigned long.
3.In caz contrar, daca unul dintre operanzi este de tip long, celalalt este convertit la tipul long.
4.In caz contrar, daca unul dintre operanzi este de tip unsigned, celalalt este convertit la unsigned.
5.In caz contrar, ambii operanzi sunt de tipul int.
Modificarea tipului unui operand ( cast ) dintr- o expresie poate fi facuta si de catre programator. Prima utilizare a acestei tehnici este modificarea tipului unui operand dintr-o operatie artimetica astfel incat operatia sa fie efectuata coreect, atunci cand compilatorul nu poate decide corect o conversie de tip.
A doua utilizare este modificarea tipului de pointeri catre sau dinspre void *, pentru a realiza interfata cu functiile care au ca argument sau returneaza pointeri de tip void. E.g.:
Cod:
struct student *s = ( struct student *)malloc(sizeof(struct student));


10. Instructiuni

In limbajul C, orice instructiune se termina cu “ ; “, caracter considerat terminator de instructiuni.
Acoladele “ { “, “ } “ sunt folosite pentru a grupa instructiuni si declaratii, constructie echivalenta practic cu o singura instructiune. Dupa acolada inchisa care incheie un bloc nu se pune “ ; “.

if-else

Instructiunea if-else se utilizeaza pentru luarea conditionala de decizii. Sintaxa este :

Cod:
if ( expresie )
instructiune 1
else
instructiune 2


unde sectiunea else este optionala. Daca expresia din paranteza dupa if este adevarata ( adica are o valoarea nenula ), este executata instructiune 1. In caz contrar, este executata instructiune 2.

Cod:
if (i == 1)
printf(“i egal cu 1\n”);
else
return;


Deoarece sectiunea else este optionala se poate ajunge la ambiguitati atunci cand avem de-a face cu structuri if-else imbricate. In aceste cazuri este necesara utilizarea acoladelor:

Cod:
if ( i > 0 )
{
if ( x > y )
   res = x;
}
else
    res = y;


Constructia if-else poate fi inlocuita ( simplificata ) prin utilizarea operatorului ternar “ ? : ”, in expresia

Cod:
e1 ? e2 : e3


Se evalueaza mai intai expresia e1. Daca aceasta este adevarata, atunci se evalueaza expresia e2 si aceasta este valoarea expresiei conditionale. In caz contrar se evalueaza e3 si acesta va fi rezultatul expresiei conditionale. Exemplul urmator calculeaza maximul dintre douna numere :

Cod:
max = ( a > b ? a : b );


switch

Instructiunea switch se utilizeaza pentru a lua decizii multiple, testand daca o expresie se potriveste cu una dintr-un numar de valori constante si executand corespunzator anumite instructiuni. Programul urmator contorizeaza tipurile de caractere dintr-un text introdus de la tastatura:


Cod:
main()
{
   int c, nr_spatii = 0, nr_cifre = 0, nr_car = 0;
    while((c= getchar()) != EOF)
      switch(c)
       {
          case '0':
          case '1':
          case '2':
          case '3':
          case '4':
          case '5':
          case '6':
          case '7':
          case '8':
          case '9':
            nr_cifre++;
             break;
          case ' ':
          case '\t':                                         /* tab */
          case '\n':      /* linie noua */
            nr_spatii++;
            break;
          default:         /* alte caractere decat cele enumerate */
            nr_car++;
            break;
        }
printf(“Am intalnit %d cifre, %d spatii si %d alte caractere\n”, nr_cifre, nr_spatii, nr_car);
}



Fiecare caz trebuie “etichetat” cu o constanta intreaga. Daca un caz se potriveste cu valoarea expresiei din paranteza, executia este transferata in cazul respectiv, dupa intalniera primului break trecandu-se la executia urmatoarei intructiuni de dupa switch. Cazul denumit default este executat atunci cand nici unul dintre cazuri nu a fost intalnit. Instructiunea break provoaca iesirea din cadrul switch, fara a mai verifica celelalte cazuri.

While

Sintaxa instructiunii while este urmatoarea:

Cod:
while ( expresie )
instructiune


Daca expresia specifica intre paranteze este adevarata ( nenula ), este executata instructiunea. Dupa executie expresia este evaluata si din nou ciclul continua pana cand valoarea expresiei devine falsa ( nula ). Exemplul urmator va decrementa valoarea contorului i pana cand acesta devine 0:


Cod:
while ( i > 0)
i--;


for

Sintaxta instructiunii for este:

Cod:
for(e1; e2; e3)
instructiune


Expresia e1 este evaluata o singura data, la startul instructiunii for. Daca expresia e2 este adevarata ( nenula ), este executata instructiune si apoi evaluata e3, dupa care ciclul este reluat pana cand e2 devine falsa ( nula ). De obicei, e1 si e3 sunt expresii de asignare, iar e2 e o expresie relationala. Oricare dintre expresii poate fi omisa, dar caracterele “ ; “ trebuie sa ramana. Daca e2 este omisa, ea este considerata intotdeauna adevarata. Instructiunea break in cadrul instructiune provoaca iesirea din ciclul for. Instructiunea for este echivalenta cu:

Cod:
e1;
while ( e2 )
{
    instructiunea     
    e3;
  }



Astfel, instructiunea de mai jos decrementeaza variabila i pana cand aceasta devine 0:

Cod:
for(i=5; i > 0; i—)


In cadrul instructiunii for poate fi utilizat un operator special, virgula “,”. Astfel, o succesiune de expresii separate prin virgula este evaluata de la stanga la dreapta si tipul si valoarea rezultatului sunt de tipul si valoarea operandului cel mai din dreapta. Functia prezentata in exemplul urmator inverseaza caracterele dintr-un sir:

Cod:
void reverse(char *s)
{
   int c, i, j;
   for(i = 0, j = strlen(s) – 1; i<j; i++ , j--)
    {
       c = s[i];
   s[i] = s[j];
   s[j]=c;
   }



Iata mai jos o implementare pentru functia atoi, care converteste un sir de caractere intr-un numar intreg:

Cod:
int atoi(char *s)
{
    int i, n, semn = 1;
    /* Ignora spatiile de la inceputul sirului */
    for(i=0; s[i]==' ' || s[i] == '\t' || s[i] == '\n'; i++)
    if (s[i] == '+' || s[i] == '-')
        /* verifica semnul */
         semn = (s[i] == '+' ? 1 : -1);
    /* calculeaza partea intreaga */
    for(n=0; s[i]>='0' && s[i] <= '9'; i++)
      n = 10 * n + s[i] – '0';
     return semn * n;
}


do-while

Instructiunea do-while este similara cu while, dar conditia de terminare este evaluata la sfarsitul ciclului si nu la inceputul acestuia ( cu alte cuvinte, instructiune se executa cel putin o data ! ):


Cod:
do
  instructiunea
while ( expresie );



break si continue

Am intalnit instructiunea break in paragrafele precedente. Asa cum am vazut, aceasta determina incheierea imediata a ciclului ( buclei ).
Instructiunea continue are un efect diametral opus lui break, in sensul ca face sa inceapa urmatoarea iteratie a ciclului. Pentru while si do-while aceasta reprezinta o noua evaluare a expresiei, iar pentru for inseamna reinitializarea ciclului, incepand cu reevaluarea expresiei e1.

Goto

Instructiunea goto efectueaza un salt fortat la un anumit punct din program, denumit eticheta ( label ). Deoarece C este considerat un limbaj structurat, nu este recomandata folosirea acestei instructiuni, practic fiind posibila intotdeauna evitarea ei. Ca exemplu, goto este folosita in cadrul nucleului Linux pentru a permite compilatorului sa genereze un cod mai eficient

Cod:
for(i = 0; i < 10; i++)
if( x – i < 0)
goto ok;
ok :
....


11. Pointeri


Prin definitie un pointer este o variabila care contine adresa unei alte variabile. Ponterii sunt foarte utilizati in C pentru ca simplifica implementarea unor numeroase probleme si conduc la generarea de cod-obiect mai compact si mai eficient.
Este posibila adresarea variabilei continute de pointer in mod indirect,. Presupunem ca avem variabila x de tip int, iar px un pointer de tipul int.
Operatorul unar “&” furnizeaza adresa la care se afla o variabila. Instructiunea:

Cod:
px = &x;


atribuie variabilei pc adresa la care este memorata variabila x. Vom spune ca px pointeaza pe x.
Operatorul unar “*” returneaza continutul variabilei memorate la adresa respectiva. In cazul nostru, instructiunea:

Cod:
x = *px;


atribuie variabilei x valoarea memorata la adresa px. Declaratia:

Citat:
int *px;


declara ca pointerul px memoreaza o valoare de tip int.
Initial continutul variabilei de tip pointer px este vid. Pentru a putea folosi aceasta variabila, trebuie mai intai alocat spatiul de memorie necesar, cu ajutorul functiei de biblioteca malloc(). Aceasta functie primeste ca argument dimensiunea spatiului de memorie care se doreste a fi alocat si returneaza adresa unde incepe zona de memorie alocata, in caz de succes, sau un pointer cu valoarea NULL ( practic 0 ) atunci cand alocarea nu a reusit. Trebuie mentionat aici faptul ca aceasta instructiune returneaza o variabila de tip void *, adica un pointer de tip general, void. Pentru a se evita generarea de atentionari de catre compilator, este necesara utilizarea operatorului cast, pentru modificarea acestui tip in tipul pointerului px, atunci cand se doreste ca px sa primeasca drept valoare adresa zonei alocate cu malloc():

Cod:
px = ( int *)malloc(2);



Operatiunea descrisa mai sus se numeste alocare dinamica.
Atunci cand nu se cunoaste marimea tipului de variabila ( cum ar fi in cazul unei structuri ) trebuie ubilizata instructiunea sizeof:

Cod:
double *d;
d = ( double *d)malloc(sizeof(double));



Eliberarea zonei de memorie ocupata de continutul unui pointer se face cu ajutorul functiei free(). Eliberarea acestor zone de memorie se face in mod automat la iesirea din program.

Cod:
free(d);


Pointerii rezolva si anumite inconveniente in folosirea parametrilor unei functii, deoarece acestia sunt transmisi prin valoare, nefiind posibila modificarea adreselor memorate prin intermediul lor de catre functie. Transmitand ca parametri variabile de tip pointer, adresele spre care pointeaza acestia nu pot fi schimbate, dar continutul lor poate fi modificat fara nici o restrictie. Functia din exemplul urmator interschimba valorile parametrilor primiti:

Cod:
void swap( int *px, int *py)
{
   int tmp;
    tmp = *px;
    *px = *py;
     *py = tmp;
}


12. Tablouri si pointerii


In C, exista o relatie foarte stransa intre pointeri si tablouri, dupa cum vom observa in cele ce urmeaza.
Un tablou poate fi utilizat ca si cum ar fi un pointer, iar un pointer poate fi indexat ca si cum ar fi un tablou. Valoarea unei variabile de tip tablou reprezinta adresa primului element al tabloului:

Cod:
int t[20];
int *pt;
int x;


In urma instructiunii:

Cod:
pt = &t[0];
pt = t;


pt va pointa la primul element al tabloului t. Cele doua atribuiri sunt echivalente. Instructiunea:

Cod:
x = *pt;


atribuie lui x valoarea primului element al tabloului, iar:

Cod:
x = * ( pt + 1 );



atribuie lui x valoarea celui de-al doilea element al tabloului. Daca pt este un pointer, el poate fi accesat cu ajutorul indicilor sub forma pt, ceea ce este echivalent cu * ( pt + i ).
Pentru a ilustra mai bine aceste notiuni de asa-zisa aritmetica a pointerilor, vom rescrie functia lungime_sir() care calculeaza lungimea unui sir de caractere:

Cod:
int lungime_sir(char *s)
{
    int n;
    for( n = 0; *s != '\0' ; s++)
    n++;
    return n;
}


Se observa incrementarea lui s, operatiune corecta cu un pointer, care nu afecteaza continutul acestuia, incrementand adresa s, aceasta memorand o copie locala in cadrul functiei a parametrului actual de la apelul functiei. De remarcat ca notatia char *s este echivalenta cu char s[]. Daca p este un pointer, atunci p++ desemneaza urmatorul “obiect” pointat, indiferent de tipul acestuia.
Pentru a evidentia usurinta programului cu pointeri, vom implementa o functie pentru copierea unui sir de caractere intr-un alt sir, furnizand mai intai valoarea fara pointeri, iar apoi varianta cu pointeri:


Cod:
void my_strcopy1(char s[], char t[])
{
    int i = 0;
    while(s[i] = t[i] != '\0')
     i++;
}
void my_strcopy2(char *s, char *t)
{
    while(*s++ = *t++)
    ;
}



13.Tablouri de pointeri

Pointerii pot fi utilizati si in tablouri de pointeri. Spre exemplu, un tablou de pointeri poate constitui un text, fiecare linie a acestuia fiind reprezentata ca un pointer la primul sau caracter. Vom prezenta in continuare un program de sortare lexicografica a acestor linii. Pointerii simplifica aceasta operatiuni, deoarece atunci corespunzatorii in tablou, nu si continutul liniilor de text:
functii.h:

Cod:
/* Compara cele doua siruri primite ca argumente
    returneaza zero daca sunt identice, un numar pozitiv daca s1>s2 si
    un numar negativ daca s1<s2 */

int strcmp(char *s1, char *s2);

/*   Returneaza lungimea sirului primit ca argumente */

int strlen(char *s);

/* Citeste de la tastatura linii de text, cel mult numarul specificat, pana cand intalneste EOF, memorandu-le in argumentul text returneaza numarul de linii introduse */

int readlines( char **text, int max);

/* Sorteaza liniile textului primit ca argument returneaza zero in caz de succes, -1 in caz de eroare */

int sort(char **text, int n);


functii.c

Cod:
#include <stdio.h>
int strcmp(char *s1, char *s2)
{
     for(; *s1 == *s2; s1++, s2++)
          if(*s1 == '\0')
               return 0;
      return( *s1 - *s2)
}
int strlen(char *s)
{
     int n;
     for(n = 0; *s != '\0'; s++)
          n++;
     return n;
}
int readlines(char **text, int max)
{
     int len, nlines = 0;
     char s[255];
     while(nlines < max && (len = strlen(gets(s))) > 0)
      {
          if((text[nlines++] = (char *)malloc(len)) == NULL)
          /* daca alocarea de memorie a esuat */
               return -1;
          strcpy(text[nlines++], s);
         }
     return nlines;
}
int sort(char **text, int n)
{
     int i, j, dist;
     char *tmp;
     for(dist = n / 2; dist > 0; dist /= 2)
          for(i = dist; i < n; i++)
               for(j = i – dist; j >= 0; j -= dist )
                {
                    if(strcmp(text[j], text[j + dist]) <= 0)
                         break;
                    tmp = text[j];
                    text[j] = text[j + dist];
                    text[j + dist ] = tmp;
                }
}
void printlines(char **text, int n)
{
     int i = 1;
     while(--n >= 0)
       printf(“%d: %s\n”, i, *text++, i++);
}


main.c

Cod:
#include <stdio.h>
#define MAX_LINES 1000
int main()
{
     char *text[MAX_LINES];
     int i, nlines;
     nlines = readlines(text, MAX_LINES);
     sort(text, nlines);
     printlines(text, nlines);
}


14.Pointeri la functii

In C, exista posibilitatea de a defini un pointer la o functie, care poate fi manipulat ca orice alta variabila. Pentru exemplificare, vom modifica programul de sortare a liniilor unui text, prezentat mai sus, pentru a putea sorta orice fel de date, nu doar siruri de caractere. Algoritmul de sortare va fi independent de operatiile de comparare si de inter-schimbare a liniilor de text, astfel incat, prin transmiterea acestor doua functii ca argumente, se va putea realiza sortarea pe diferite criterii:


Cod:
#include <stdio.h>
#define MAX_LINES 1000
int nrcmp(char *s1, char *s2)
{
     double n1, n2;
     n1 = atof(s1);
     n2 = atof(s2);
     if(n1 < n2 )
          return -1;
     else
          if(n1 > n2)
               return 1;
     return 0;
}
void schimba(char **s1, char **s2)
{
     char *tmp;
     tmp = *s1;
     *s1 = *s2;
     *s2 = tmp;
}
int sort(char **text, int n, int (*compara)(), (*shimba)())
{
    int i, j, dist;
     for(dist = n / 2; dist > 0; dist /= 2)
          for(i= dist; i < n; i++)
               for(j = i – dist; j >= 0; j -= dist)
               {
                    if((*compara)(text[j], text[j + dist]) <= 0
                         break;
                    (*schimba)(&text[j], &text[j + dist]);
                    }
}
int main(int argc, char **argv)
{
     char *text[MAX_LINES];
     int i, nlines, numeric = 0; /* 1 daca valorile sunt numerice ! */
     if(argc > 1 && argv[1][0] == '-' && argv[1][1] == 'n')
          numeric = 1;
     nlines = readlines(text, MAX_LINES);
     if(numeric)
          sort(text, nlines, nrcmp, schimba);
     else
          sort(text, nlines, strcmp, schimba);
     printlines(text, nlines);
     return 0;
}


Declaratia int (*compara)() desemneaza ca argumentul compara este un pointer la o functie care returneaza un int. Declaratia (*schimba)() indica un pointer la o functie care returneaza un void.

15. Recursivitate

In limbajul C, functiile pot fi apelate recursiv. Aceasca inseamna ca o functie se poate apela ea insasi. Atunci cand o functie se autoapeleaza, fiecare apel genereaza alte instante variabilelor declarate in interiorul functiei. Recursivitatea este utila in special pentru prelucrarea structurilor de date recursive, cum ar fi arborii sau grafurile.
Trebuie avut in vedere faptul ca utilizarea de functii recursive duce la consum ridicat de memorie, deoarece pentru fiecare apel de functie se aloca spatiu pentur stiva functiei precum si pentru variabilele declarate in interiorul ei.
Exemplul urmator implementeaza functia calcul, care decrementeaza un numar intreg pana cand acesta devine zero:


Cod:
#include <stdio.h>
void calcul(int *contor)
{
    if(*contor == 0)
        return;
    else
        {
            (*contor)--;
            calcul(contor);
        }
}
int main()
{
        int i;
        printf(“ Introduceti numarul de cicluri: “);
        scanf(“%d, &i);
        calcul(&i);
        printf(“ Valoarea lui i este: %d\n”, i);
        return 0;
}


16. Functia main()

S-a putut observa in multe exemple de pana acum apelul main(). Aceasta nu este altceva decat o functie speciala, care defineste programul principal. Apelul acestei functii incepe o data cu executia programului si inceteaza la apelul instructiunii return din cadrul functiei main() sau la incheierea programului.
In mod normal, functia main() trebuie sa returneze un numar intreg, reprezetand rezultatul executiei programului. In general, aceasta este zero pentru o executie normala.
Functia main() poate primi argumente de forma:

Cod:
int main(int argc, char **argv);


unde argc este numarul de parametri primiti de program la apelarea acestuia, iar argv este un tablou de siruri de caractere continand parametri. Primul element al tabloului, argv[0], reprezinta numele executabilului si asociaza programului, inclusiv calea completa in care se afla. Programul de mai jos va afisa toti parametrii in linia de comanda:

Cod:
main(int argc, char **argv)
{
    int i;
    for(i = 1; i < argc; i++)
        printf(“Parametrul %d: %s\n”, i, argv[i]);
}


Presupunand ca fisierul executabil corespunzator acestui program este denumit afisparam, apelul:

Cod:
afisparam param1 param2


va afisa la iesirea standard

Cod:
Parametrul 1: param1
Parametrul 2: param2


Structuri

O structura este o colectie de date de tipuri diferite. O structura se declara utilizand sintaxa:

Cod:
struct nume_structura{
          declaratii membri
          ...
}


E.g.
struct student {
char *nume;
char *prenume;
char *adresa;
char *telefon;
}
Pentru a declara o variabila de tip student si a o initializa:


Cod:
struct student s = { “Hoagio”, “hoagio”, “Bucuresti, str. Etc”, “0213022222” };


membrii structurii pot fi accesati prin constructii de forma:

Cod:
nume_structura.nume_membru


e.g:

Cod:
printf(“Nume si prenume: %s %s\n”, s.nume, s.prenume”);


In mod evident, pot fi declarati si pointeri la structuri. Pentru accesarea lor trebuie folosit in acest caz operatorul ->:

Cod:
sturct student *s;
s = (struct student *)malloc(sizeof(struct student));
s -> nume = (char *)malloc(10);
strcpy(s -> nume, “ Hoagio “);


O structura poate contine si membri de tip structura:

Cod:
struct data {
          int zi, luna, an;
};
struct student {
          char *nume;
          char *prenume;
          char *adresa;
          char *telefon;
          struct data data_nastere;
} s;


Ultima declaratie, cea a structurii student, contine si o definitie, cea a variabilei s de tip student. Membrii structurii data se acceseaza astfel:
s.data_nasterii.zi
Manipularea structurilor este supusa unui numar de restrictii. Astfel, structurile nu pot fi asignate sau copiate ca un intreg, elementele acestora trebuind copiate individual de catre programator. Este permisa utilizarea structurilor in cadrul argumentelor functiilor, precum si ca tip de data returnata de o functie:

Cod:
struct student citire_student();
int verificare_student(struct student *s);


Continutul unei variabile de tip structura poate fi initializata astfel:

Cod:
sturct student s = ( “ Hoagio “, “ regali”, “Bucuresti”, “0213022222”, { 31, 12, 1980 } };


17. Structuri de autoreferire

Autoreferirea desemneaya posibilitatea ca o structura sa aiba ca membru un pointer la o structura de acelasi tip cu ea:

Cod:
struct nod {
         int val;
         struct nod *stanga, *dreapta;
};
#include <stdio.h>
#include <string.h>
struct nod {
         char *linie;
         struct nod *stanga, *dreapta;
};
struct nod *inserare_linie(struct *rad, char *sir)
{
         struct nod *nou;
         if(rad == NULL)
         {
                  /* Alocam memorie pentru un nod */
                  if(nou = (struct nod *)malloc(sizeof(struct nod))) == NULL)
                           /* Daca alocarea de memorie a esuat */
                           return -1;
         /* Alocam memorie pentru un sir */
         if(nou -> linie = (char *)malloc(strlen(sir))) == NULL)
                  /* Daca alocarea a esuat */
                  return -1;
         strcpy(rad->linie, sir)
         rad->stanga = rad->dreapta = NULL;
         else
                  {
                    if(strcmp(sir, rad->linie) < 0)
                           rad->stanga = inserare_linie(rad->stanga, sir);
                  else
                           rad->dreapta = inserare_linie(rad->dreapta, sir);
           return rad;
}
void afiseaza_linie(struct nod *rad)
{
         if(rad->stanga)
                  afiseaza_linie(rad->stanga);
         printf(“%s\n”, rad->linie);
         if(rad->dreapta)
                  afiseaza_linie(rad->dreapta);
}
int main()
{
         int len;
         char s[256];
         struct nod *rad = NULL;
         while(len = strlen(fgets(s, 255, stdio))) > 0)
                  rad = inserare_linie(rad, s);
         afiseaza_linie(rad);
         return 0;
}


18. typedef

Intr-un mod similar cu macrodefinitiile, limbajul C permite crearea unor nume noi pentru diferite tipuri de date, denumite typedef. Astfel, declaratia:

Cod:
typedef char* SIR;


face ca numele de tip SIR sa fie sinonim pentru char *. Vom utiliza typedef pentru structura arborelui de mai sus:


Cod:
typedf struct nod{
         char *linie;
         struct nod *stanga, *dreapta;
} NOD_ARBORE;


Declaratiile typedef sunt utile in primul rand pentru tipuri de date care pot fi dependente de platforma, modificarile trebuind efectuate doar asupra declaratiei typedef, si in al doilea rand pentru a creste claritatea unui program atunci cand in cadrul acestuia intervin structuri mai complicate de date.

19. Bibliotecile Standard C

Operatiile I/O ( in/out ) sunt implementate in biblioteca standard C si sunt accesibile prin intermediul functiilor din cadrul acesteia. Pentru a putea folosi aceasta biblioteca, trebuie inclus fisierul-antet stdio.h printr-o directiva #include <stdio.h>. O serie de functii au ami fost utilizate in programele prezentate mai sus.

Iesirea formatata: printf

Sintaxa functiei printf este urmatoarea:

Cod:
printf(const char *format, arg1, arg2, ...)


printf converteste, si formateaza si tipareste la iesirea standard valorile argumentelor arg1, arg2, ..., conform formatului specificat prin primul argument. Acest format contine caractere obisnuite, care vor fi copiate la iesire, si caractere de control, care determina modul de conversie si tiparirea urmatoarelor argumente. Fiecare specificatie de conversie este introdusa prin caracterul “%” si incheiata printr-un caracter de conversie.
Intre “%” si caracterul de conversie pot fi:
un semn minus “-”, care semnifica alinierea la stanga a argumentului convertit;
un numar care specifica lungimea minima a sirului. Argumentul va fi aliniat la dreapta si va fi completat cu spatii albe pana la lungimea campului. Daca dimensiunea campului a fost prefixata cu un “0”, caracterul de completare va fi “0”.

Caracterele de conversie sunt urmatoarele:

d argumentul este convertit in zecimal;
o argumentul este convertit in octal fara semn;
x argumentul este convertit in hexazecimal fara semn;
u argumentul este convertit in zecimal fara semn;
c argumentul este considerat ca fiind un singur caracter;
s argumentul este un sir de caractere. Caracterele din cadrul argumentului corespunzator acestui format de conversie preluate pana la intalnirea caracterului \0 sau pana cand este atinsa lungimea specificata;
f argumentul este considerat in virgula mobila, implicit cu 6 cifre dupa virgula.
Asemanatoare cu functia printf sunt functiile:

sprintf(char *sir, const char *format, arg1, arg2. ...) trimite iesirea catre sirul de caractere specificat;
fprintf(FILE *fisier, const char *format, arg1, arg2, ...) trimite iesirea catre identificatorul de fisier specificat.

Intrarea formatata: scanf

scanf citeste caractere de la intrarea standard, le interpreteaza conform formatului specificat si le memoreaza in argumentele urmatoare, care practic sunt pointeri care indica adresele unde vor fi memorate datele convertite. Sintaxa functiei este urmatoarea:

Cod:
int scanf(const char *format, arg1, arg2, ...)


Formatul primului argument este identic cu cel descris la functia prinf. Functia scanf returneaza numarul de “obiecte” citite de la intrare. Important de remarcat este faptul ca spatiul de memorie alocat pentru pointerii trimisi ca argumente trebuie sa fie destul de mare pentru a putea memora datele citite.

Cod:
#include <stdio.h>
int main()
{
       int x, y;
       printf(“Introduceti cele doua valori: “);
       scanf(“%d %d”, &x, &y);
       printf(“%d + %d = %d\n”, x, y, x + y);
       return 0;
}



Asemanatoare cu functia scanf sunt functiile:

sscanf(char *sir, const char *format, arg1, arg2, ...) citeste date din sirul de caractere specificat;
fscan(FILE *fisier, const char *format, arg1, arg2, ...) citeste date din identificatorul de fisier specificat.

Functii pentru citirea si scrierea de caractere


int getchar() citeste un caracter de la intrarea standard;
int fgetc(FILE *f) citeste un caracter din fisierul specificat;
int putchar(int c) scrie un caracter la iesirea standard;
int fputc(int c, FILE *f) scrie un caracter in fisierul specificat.

Functii pentru citirea si scrierea de siruri de caracterele


char *gets(char *s) citeste un sir de caractere de la intrarea standard, memorandu-l in argumentul s. Nu face vreo verificare asurpa dimensiunii spatiului alocat sirului s;
char *fgets(char *s, int max, FILE *f) citeste din fisierul f un sir de caractere a carui lungime nu va depasi numarul maxim de caractere, specificat ca argument;
int puts(char *s) scrie un sir de caractere la iesirea standard;
int fputs(char *s, FILE *f) scrie un sir de caractere in fisierul f.

Lucrul cu fisiere

Inainte de a putea citi sau scrie intr-un fisier, acesta trebuie deschis cu ajutorul functiei fopen. Aceasta functie returneaza un pointer la o structura de tip FILE, care contine informatii despre fisierul deschis, utilizate intern de catre biblioteca:

Cod:
FILE *fopen(const char *nume_fisier, const char *mod)


Modul de deschidere a fisierului se alege in functie de modul in care se intentioneaza a fi utilizat:

r doar citire;
w scriere;
a adaugare.
Daca fisierul nu exista si este deschis pentru scriere sau adaugare, acesta va fi creat. Daca se deschide un fisier existent pentru scriere, continutul vechi va fi sters. Daca la deschidere apare o eroare, functia fopen va returna valoarea NULL.
Inchiderea unui fisier atunci cand nu mai este folositor se realizeaza cu ajutorul functiei fclose, care primeste ca argument pointerul la FILE care specifica fisierul respectiv. Continutul acestuia va fi eliberat.
In momentul inceperii executiei unui program C, sunt deschise in mod automat trei fisiere: intrarea standard ( stdin ), iesirea standard ( stdout ) si iesirea standard pentru erori ( stderr ). Acestea pot fi folosite ca orice alt fisier, dar nu pot fi deschise sau inchise ( ele sunt practic constante ).
Vom realiza in cele ce urmeaza un program care simuleaza comanda cat, pentru concatenarea a doua fisere:

Cod:
#include <errno.h>
#include <stdio.h>
void copie(FILE *f)
{
       int c;
       while((c = fget(f)) != EOF)
              putc(c, stdout);
}
int main(int argc, char **argv)
{
       FILE *f;
       if(argc == 1) /* nu ma primit argumente, utilizam intrarea standard */
         copie(stdin);
       else
         /* va fi afisat intai continutul fisierului precizat prin argv[1], apoi continutul fisierului argv[2] */
         while(--argc > 0)
              if((f = fopen(*++argv, “r”)) == NULL)
                     {
                       fprintf(stderr, “cat: nu pot deschide fisierul `%s'\n”, argv);
                       return -ENOENT; /* fisierul nu exista */
                     }
               else
                     {
                       copie(f);
                       fclose(f);
                     }
       return 0;
}


O sa incerc mai jos un program care extrage fisierele continute intr-o arhiva .tar:

Cod:
/* untar v1.0
Dezarhiveaza o arhiva tar furnizata ca parametru
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
int main(int argc, char *argv[1])
{
       FILE *in, *out;
       char buff[256];
       char *currdir, *name;
       char c;
       unsigned long i, currpos = 0;
       printf(“untar v1.0 – Dezarhivaza o arhiva tar\n\n”);
       if(argc != 2)
              {
               printf(“Numar invalid de parametri.\n”);
               printf(“Mod de utilizare: untar fisier\n”);
               return -1;
              }
       if((in = fopen(argv[1], “r”)) == NULL)
        {
          printf(“untar: nu pot deschide fisierul `%s'\n”);
          return -1;
         }
       while(!feof(in))
              {
                fseek(in, currpos, SEEK_SET);
                i = 0;
                /* determina numele fisierului sau directorului continut in arhiva */
                while((c = fgetc(in)) != '\0')
                     buff[i++] = c;
               if( i == 0)
                     break;
               if(buff[i-1] = '\0';
               currdir = buff;
               /* daca este un director, il creez */
              if(mkdir(currdir, 0755))
                     printf(“untar: nu pot crea direectorul `%s'\n”, currdir);
              else
                     printf(“%s/\n”, currdir);
              strcat(currdir, “/”);
       else
              {
                buff[i] = '\0';
                name = buff;
                printf(“%s\n”, name);
                /* daca este un fisier, il scriu pe disc */
               if((out = fopen(name, “w”)) = NULL)
                     {
                      printf(“untar: nu pot deschide fisierul `%s' pentru citire\n”, name);
                      fclose(in);
                      returin -1;
                     }
              currpos += 0x200;
              fseek(in, currpos, SEEK_SET);
              while((c = fgetc(in)) != '\0')
                     {
                       if(c == EOF)
                            {
                             printf(“untar: sfarsit neasteptat de fisier\n”);
                             fclose(in);
                     fclose(out);
                     return -1;
                     }
                  fputc(c, out);
              }
            fclose(out);
            currpos = ftell(in);
            while((currpos % 0x200) != 0)
              currpos++;
            currpos -= 0x200;
              }
       currpos += 0x200;
       }
       fclose(in);
       return 0;
}


In cazul in care alocarea de memorie a esuat, in functia inserare_linie(), ar trebui sa se intoarca NULL, nu (-1).

De asemenea, tot in inserare_linie():

Cod:
/* Alocam memorie pentru un sir */
         if(nou -> linie = (char *)malloc(strlen(sir))) == NULL)
     ......
         strcpy(rad->linie, sir)


Trebuie alocati (strlen(sir) + 1) bytes.
[i]to be continue...

_________________
Imagine
https://cosmen.wordpress.com


Sus
 Profil  
 
Afişează mesajele de la anteriorul:  Sortează după  
Scrie un subiect nou Răspunde la subiect  [ 1 mesaj ] 

Ora este UTC + 2 [ DST ]


Cine este conectat

Utilizatorii ce navighează pe acest forum: Niciun utilizator înregistrat şi 3 vizitatori


Nu puteţi scrie subiecte noi în acest forum
Nu puteţi răspunde subiectelor din acest forum
Nu puteţi modifica mesajele dumneavoastră în acest forum
Nu puteţi şterge mesajele dumneavoastră în acest forum
Nu puteţi publica ataşamente în acest forum

Căutare după:
Mergi la:  
cron
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
Translation/Traducere: phpBB România