Arhiva

Archive for octombrie, 2008

KeyGen PhotoShop Cs2 + Tutorial!

octombrie 31, 2008 125 comentarii

Am vazut ca multi va plangeti de faptu ca aveti photoshop  si nu aveti licenta nelimitata.

De acuma puteti avea la PhotoShop CS2 cu acest key gen.

Descarcare:   Link
Tutorial de folosire:

Prima data cand intrati in PhotoShop va loviti de acesta:

Dupa care intrati in KeyGen si dati copy la primu serial:

Dupa care va duceti in PhotoShop si adaugati serialul aici si dati activate online:

O sa va zica de o erroare, voi dati pe Phone Active:

Dupa accea copiati serialul de la Activation Number:

Si va duceti iara in KeyGen si ii dati paste la Activation Number, dupa care dati Generate Auth Code iar jos va apare un cod pe care ii dati copy:

Acuma introduceti serialul in casutele cele de jos libere:

Dupa accea dati Activate si va aparea asa: Asta inseamna ca e gata! :)

*INFO

Tutorialul a fost editat!

Capitolul 14: Instrumente soft

octombrie 31, 2008 Scrie un comentariu

Instrumente soft

Exista doua tipuri de instrumente soft:
- facilitati generale puse la dispozitie de catre sistemul de operare;
- facilitati specifice desemnate explicit pentru a ajuta programatorul.

Din moment ce comenzile sistemului de operare pot fi executate dintr-un program C, atunci programatorul poate folosi aceste comenzi ca instrumente de soft pentru indeplinirea anumitor sarcini. Cateva instrumente sunt disponibile intr-un sistem de operare, dar nu si in altul. De exemplu, “make” exista in UNIX, iar in MS-DOS este o trasatura ce se poate instala. Instrumentele de soft variaza cu timpul. Sistemele de depanare par a fi cele mai disponibile. Compilatorul C insusi poate fi considerat un instrument soft.
In acest capitol vom discuta cum executam o comanda din sistemul de operare dintr-un program C. Apoi vom discuta cateva instrumente soft importante, cum ar fi:
- compilatorul C
- “make”
- “touch”
- “grep”
- instrumente de depanare

14.1 Executarea comenzilor dintr-un program C

Functia “system()” (din biblioteca C) pune la dispozitie accesarea comenzilor sistemului de operare. Astfel, comenzile existente in
sistemul de operare pot fi apelate si din programe C.

Exemplu: Atat in MS-DOS, cat si in UNIX, exista comanda “date”. Daca intr-un program C, scriem comanda

system(“date”);
atunci va fi tiparita la ecran data curenta a sistemului.
Sirul trimis ca argument al functiei “system()” este tratat ca o comanda a sistemului de operare. Cand se executa instructiunea, controlul este trimis catre sistemul de operare, se executa comanda si apoi controlul este trimis inapoi catre program.

Exemplu: In UNIX, “vi” este o comanda folosita pentru editare. Presupunem ca suntem intr-un program si vrem sa editam un
fisier al carui nume se citteste de la tastatura. Putem scrie:
char comanda[MAXSTRING];
sprintf(comanda, “vi %s”, argv[1]);
printf(“Dam comanda vi, deschizand fisierul %s\n”, argv[1]);
system(comanda);

Un exemplu similar poate functiona si in MS-DOS inlocuind “vi” cu alt editor de texte.

Exemplu: Consideram ca vrem sa dam comanda MS-DOS “dir” si dorim afisarea doar cu litere mici. Atunci putem scrie programul:

#include
#include
#include
#define MAXSTRING 100

void main()
{
char comanda[MAXSTRING], *nume_fis_temp;
int c;
FILE *ifp;
nume_fis_temp = tmpnam(NULL);
sprintf(comanda, “dir > %s”, nume_fis_temp);
system(comanda);
ifp = fopen(nume_fis_temp, “r”);
while ((c = getc(ifp)) != EOF)
putchar(tolower(c));
remove(nume_fis_temp);
}
O varianta ceva mai “protejata” este:
#include
#include
#include
#define MAXSTRING 100

void main()
{
char comanda[MAXSTRING], *nume_fis_temp;
int c;
FILE *ifp;
nume_fis_temp = tmpnam(NULL);
sprintf(comanda, “dir > %s”, nume_fis_temp);
if (system(“dir *.*”) == 0)
{
system(comanda);
if ((ifp = fopen(nume_fis_temp, “r”)) != NULL)
while ((c = getc(ifp)) != EOF)
putchar(tolower(c));
remove(nume_fis_temp);
}
}
Atentie ! Se creeaza intai executabilul si apoi se ruleaza dupa ce s-a iesit din compilatorul C.

Observatii: Pentru programele de mai sus, facem precizarile:

- folosim functia “tmpnam()” pentru creearea unui nume de fisier temporar (de obicei “tmp1.$$$”); daca exista deja fisierul
“tmp1.$$$” in directorul curent, atunci se creeaza fisierul “tmp2.$$$”, s.a.m.d.);

- apelam functia “sistem()” pentru redirectarea iesirii comenzii “dir” in acel fisier temporar;
- apoi tiparim continutul fisierului la ecran schimband literele mari in mici;
- in final, stergem din memorie fisierul temporar folosind functia “remove()”.

14.2 Variabile de mediu

Variabilele de mediu sunt disponibile atat in UNIX, cat si in MS-DOS. Afisarea lor la ecran se poate face cu urmatorul program:
#include

void main(int argc, char *argv[], char *env[])
{
int i;
for (i = 0; env[i] != NULL; ++i)
printf(“%s\n”, env[i]);
}
Ambii parametri (argv si env) sunt de tip pointer catre pointer catre “char”. Deci, putem sa-i gandim ca siruri de pointeri catre “char” sau “vectori de siruri de caractere”. Sistemul memoreaza spatiu pentru ele. Ultimul element din fiecare astfel de sir este pointerul NULL. Evident programul de mai sus foloseste doar vectorul “env”.
Pe sistemele UNIX, programul va afisa:
PATH=/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/home/stefan/bin
HOME=/home/stefan
SHELL=/bin/bash
TERM=vt220
USER=stefan
. . . . .

La stanga semnului “=” sunt deci variabilele de mediu, iar la dreapta valorile lor, care trebuie gandite ca siruri de caractere.
Pe sistemele MS-DOS, programul va afisa:
PROMPT=$P$G
PATH=Z:.;Y:.;X:.;W:.;V:.;U:.;T:.;S:.;R:.;Q:.;P:.
COMSPEC=Y:COMMAND.COM
. . . . .

Ambele sisteme (UNIX si MS-DOS) pun la dispozitie comenzi pentru afisarea variabilelor de mediu. In UNIX, se pot folosi comenzile “env” sau “printenv” (pe unele sisteme si comanda “set”), iar in MS-DOS comanda “set”.
Prin conventie, variabilele de mediu sunt de obicei scrise cu litere mari. Intr-un program C, putem accesa variabilele de mediu prin al treilea argument al functiei “main()” sau putem folosi functia “getenv()” din biblioteca standard. Prototipul sau, care se gaseste in , este dat prin:
char *getenv(const char *name);
Daca sirul trimis ca argument este o variabila de mediu, atunci functia intoarce sirul (pointer catre “char”) pus la dispozitie de catre sistem ca valoare a variabilei. Daca sirul trimis ca argument nu este variabila de mediu, atunci se returneaza NULL.

Exemplu:

printf(“%s%s\n%s%s\n%s%s\n%s%s\n%s%s\n”,
“Nume utilizator: “, getenv(“USER”),
“Nume login: “, getenv(“LOGNAME”),
“Shell: “, getenv(“SHELL”),
“Env: “, getenv(“ENV”),
“Director Home: “, getenv(“HOME”));

In UNIX, anumite variabile de mediu, cum ar fi LOGNAME, SHELL, HOME, sunt puse la dispozitie de catre sistem (adica sunt nemodificabile). Pentru a initializa altele, scriem
setenv NAME “Abcd efgh”
in fisierul nostru “.login”.

14.3 Compilatorul C

Exista multe compilatoare de C si un sistem de operare poate pune la dispozitie un numar mare de astfel de compilatoare. Iata cateva posibilitati:

Comanda
Compilator C apelat
cc
Compilator C creat de Bell Laboratories
cc
Compilator C creat de Cray Research (UNICOS)
cc
Compilator C creat de Hewlett-Packard (HP-UX)
cc
Compilator C creat de Silicon Graphics (IRIX)
acc
Compilator C creat de Sun Microsystems (SunOS)
gcc
Compilator GNU C creat de Free Software Foundation
hc
Compilator High C creat de Metaware
occ
Compilator Oregon C creat de Oregon Sofware
qc
Compilator Quick C creat de Microsoft
tc
Compilator Turbo C, sistem integrat, creat de Borland
tcc
Compilator Turbo C, versiune linie comanda, Borland

In cele ce urmeaza, vom preciza modul de apel si optiunile acestora in UNIX. Multe dintre ele sunt valabile si in MS-DOS.
Daca avem un program complet intr-un singur fisier, sa zicem “pgm.c”, atunci comanda:
cc pgm.c va traduce codul C din “pgm.c” in cod obiect executabil si-l va scrie in fisierul “a.out” (In MS-DOS, fisierul executabil se numeste “pgm.exe”). Comanda “a.out” executa programul. Consideram acum comanda:
cc -o pgm pgm.c
Aceasta cauzeaza scrierea codului executabil direct in fisierul “pgm”, suprascriind-ul in cazul in care acesta exista deja (In MS-DOS optiunea similara este -e). Comanda “cc” lucreaza de fapt in trei faze:
- apelul preprocesorului
- apelul compilatorului
- apelul incarcatorului (editorului de legaturi)

Rolul incarcatorului este de a pune (lega) impreuna bucatile furnizate de compilator pentru a face fisierul executabil final. Optiunea -c se foloseste numai pentru compilare (pentru apelul preprocesorului si compilatorului), nu si a incarcatorului. Aceasta optiune este utila daca avem un program scris in mai multe fisiere. Consideram comanda
cc -c main.c fis1.c fis2.c
Daca nu sunt erori, fisierele obiect corespunzatoare vor fi create si vor avea extensia “.o” (In MS-DOS, ele au extensia “.obj”). Pentru creearea unui fisier executabil, putem compila anumite fisiere cu extensia “.c” si “.o” (combinate). Presupunem ca avem o eroare in “main.c”. Dupa corectarea ei, putem da comanda:
cc -o pgm main.c fis1.o fis2.o
Folosirea fisierului cu extensia “.o” in locul celui cu extensia “.c” reduce timpul de compilare. In plus fata de extensia “.c” si “.o”, putem folosi fisiere cu extesia “.s” care sunt create de asamblor sau de compilator cu optiunea “-S” (cand folosim biblioteci create de arhivator). Bibliotecile, de obicei, au extensia “.a” (In MS-DOS ele au extensia “.lib”).

Cateva optiuni folositoare pentru compilatorul C

-c – Doar compilare, geenereaza fisiere cu extensia “.o”
-g – Genereaza cod penttru depanator
-o – nume Pune codul executabil in fisierul “nume”
-p – Genereaza cod penttru profiler
-D – nume=def Pune la inceputul fiecaruii fisier cu extensia “.c” linia #define nume def
-E – Apeleaza preprocesoorul, dar nu si compilatorul
-I – dir (i mare) Cauta fisieerele “#include” din directorul “dir”
-M – Creaza un “makefile”
-MM – Creaza un “makefile”, ddar nu include toate dependentele din fisierele header standard
-O – Genereaza cod optimiizat
-S – Genereaza cod de assamblare in fisiere cu extensia “.s”

14.4 Creearea unei biblioteci

Multe sisteme de operare pun la dispozitie facilitati de creare si gestionare a bibliotecilor. In UNIX, acest lucru se face cu arhivatorul si se apeleaza cu comanda “ar”. In MS-DOS, acest lucru se realizeaza cu bibliotecarul si este o aplicatie ce se poate instala. Bibliotecarul Microsoft este “lib”, in timp ce bibliotecarul Turbo C Borland este “tlib”. Prin conventie, numele fisierelor din biblioteci au extensia “.a” in UNIX si “.lib” in MS-DOS. In cele ce urmeaza vom discuta situatia din UNIX, dar ideea generala se poate aplica oricarui bibliotecar.
In UNIX, arhivatorul “ar” poate fi folosit pentru combinarea unui grup de fisiere intr-unul singur numit “biblioteca”. Biblioteca C standard este un exemplu in acest sens. Pe multe sisteme UNIX, aceasta este fisierul “/lib/libc.a” sau poate exista in mai multe fisiere. Incercati comanda:
ar t /lib/libc.a
Cheia “t” este folosita pentru tiparirea numelor (sau titlurilor) fisierelor din biblioteca. Daca dorim numararea acestor titluri (pentru ca sunt foarte multe) putem da comanda:
ar t /lib/libc.a | wc

Observatie: Pe unele sisteme UNIX, biblioteca C nu este intitulata astfel. De exemplu, pe “fenrir”, puteti incerca alt exemplu de biblioteca: ar t /lib/libpwdb.a | wc

14.5 Folosirea lui “prof”

In UNIX, daca folosim optiunea “-p” pentru compilator, atunci se produce cod suplimentar, care poate lua locul in fisiere obiect sau fisiere executabile produse de compilator. Cand programul este apelat, codul suplimentar produce informatii care pot fi folosite pentru generarea “profilului” unei executii. Informatiile pentru “profile” sunt scrise automat in fisierul “mon.out”. Acest fisier nu poate fi citit de utilizatori. Pentru a obtine informatiile din “mon.out”, programatorul trebuie sa dea comanda prof pgm unde “pgm” este numele programului.

14.6 Cronometrarea executiei codului C

Multe sisteme de operare pun la dispozitie functii pentru folosirea ceasului intern. Accesul la ceasul masinii este posibil in ANSI C printr-un numar de functii a caror prototipuri sunt descrise in . Fisierul header contine de asemenea un numar de alte constructii, printre care si definitiile lui “clock_t” si “time_t”. De obicei, aceste definitii de tipuri sunt date prin:
typedef long clock_t;
typedef long time_t;

si aceste tipuri sunt folosite in prototipurile functiilor. Iata trei functii utile pentru cronometrarea timpului:
clock_t clock(void);
time_t time(time_t *p);
double difftime(time_t time1, time_t time0);

Cand un program este executat, sistemul de operare tine minte timpul procesorului ce este folosit. Cand este apelata functia “clock()”, valoarea returnata de sistem este cea mai buna aproximare a timpului folosit de program pana in acel punct. Unitatile (de masura) ceasului pot varia de la o masina la alta. Macro-ul
#define CLOCKS_PER_SEC 60 /* dependent de masina */
este pus la dispozitie in header-ul . Acesta poate fi folosit pentru conversia valorii returnate de “clock()” catre secunde.
Functia “time()” intoarce numarul de secunde care au trecut de la 1 ianuarie 1970 (sunt posibile si alte unitati, aceasta fiind una din ele). O folosire uzuala a acestei functii este:
srand(time(NULL));
Apelul se refera la generatorul de numere aleatoare. Daca trimitem doua valori produse de “time()” catre functia “difftime()”, atunci va fi returnata diferenta exprimata in secunde de tip “double”.

14.7 Programe de depanare

Un program de depanare permite programatorului sa urmareasca linie cu linie executia codului si de a verifica valorile unor variabile sau expresii. Acest lucru este extrem de folositor (mai ales cand un program nu functioneaza conform asteptarilor). Lumea programarii este plina de programe de depanare. De exemplu, in UNIX exista programul “dbx” (care insa nu este asa grozav). Programul C “fis.c” trebuie compilat cu optiunea “-g” (debugging), dupa care se lanseaza comanda “dbx fis.c”. Pana la comanda “quit”, toate comenzile sunt interne lui “dbx”. Vizualizarea lor se poate face cu “dbx help”.
In lumea MS-DOS, programele de depanare sunt in general incorporate. De exemplu, firmele Microsoft si Borland produc programe de depanare excelente.

14.8 Utilitarul “make”

Atat pentru programator, cat si pentru masina, este ineficient si costisitor sa pastram un program C mare intr-un singur fisier care necesita compilari repetate. O strategie mult mai buna este scrierea programului in mai multe fisiere cu extensia “.c” si compilarea lor separata. Utilitarul “make” poate fi folosit pentru a pastra “urmele” fisierelor sursa si de a produce acces usor la biblioteci si la fisierele header asociate. Aceasta facilitate este prezenta in UNIX, iar in MS-DOS este o proprietate ce se poate instala.

14.9 Utilitarul “touch”

Utilitarul “touch” este disponibil intotdeauna in UNIX si uneori disponibila sub MS-DOS (de obicei, este disponibila acolo unde este instalat “make”). Utilitarul “touch” este folosit pentru a actualiza data unui fisier. Acesta este util cand folosim “make” cand se
compara timpurile fisierelor care trebuie compilate.

Exemplu: Daca punem data curenta la un fisier “aaa.h” folosind comanda:

touch aaa.h
atunci fisierul “aaa.h” are data cea mai recenta decat toate fisierele “.h”, “.c”, “.o”. Acum, dand comanda “make” toate fisierele cu extensia “.c” vor fi recompilate si fisierele obiect linkeditate pentru a crea noul fisier executabil.

14.10 Alte instrumente soft utile

Sistemul de operare pune la dispozitie multe instrumente soft pentru programatori. Iata o lista cu cateva instrumente soft ce se gasesc in UNIX (unele chiar si in MS-DOS):

Comanda
Observatii
cb
Folosit pentru transformarea codului C in “pretty print”
diff
Tipareste liniile care difera in doua fisiere
grep
Cauta un “pattern” intr-unul sau mai multe fisiere
indent
Alt “pretty printer” cu mai multe optiuni
wc
Numara liniiile, cuvintele si caracterele dintr-un fisier (sau mai multe)

Utilitarul “cb” citeste din “stdin” si scrie in “stdout”. Utilitarul “indent” este mai puternic. Poate fi gasit pe versiunile UNIX
Berkeley si Sun.

Exemplu: cb < pgm.c

Utilitarele “diff”, “grep” si “wc” pot fi folosite de oricine, nu numai de programatori. Cu toate ca sunt utilitare UNIX, ele sunt de obicei disponibile si in MS-DOS (in special “grep”, foarte folositor programatorilor).
In final, sa mentionam ca C poate fi folosit in conjunctie si cu alte instrumente de nivel inalt (unele dintre ele limbaje “adevarate”):

Utilitar
Observatii
awk
Limbaj de procesare si scanare a pattern-urilor
csh
Acest “shell” (ca si “sh”, “ksh”) este programabil
lex
Genereaza cod C pentru analiza lexicala
sed
Editor de texte care preia comenzile sale dintr-un fisier
yacc
“Yet another compiler-compiler”, folosit la generarea de cod C

O importanta deosebita o au “lex” si “yacc” (cu versiunile “pclex” si “pcyacc” pentru MS-DOS). Versiuni mai recente, cum ar fi, “flex” sau “bison”, sunt disponibile de la Free Software Foundation, Inc. Ele lucreaza atat sub UNIX, cat si sub MS-DOS.

Categories: Tutoriale C++

Capitolul 13: Intrari/iesiri si fisiere

octombrie 31, 2008 Scrie un comentariu

Intrari/iesiri si fisiere

In acest capitol vom explica folosirea unor functii de intrare/iesire (printre care si “printf()” si “scanf()”). De asemenea vom arata modurile de deschidere a fisierelor (pentru procesarea lor) si cum se foloseste un pointer catre fisier.

13.1 Functia de iesire “printf()”

Are doua proprietati importante care permit o folosire flexibila la un nivel inalt:
- poate fi tiparita o lista de argumente de lungime arbitrara;
- afisarea este controlata de specificari de conversie simple (sau formate).

Functia “printf()” primeste sirul de caractere din fisierul standard de iesire (stdout), care este normal conectat la ecran. Lista de argumente a lui “printf()” are doua parti:
sirul_de_control si celelalte_argumente

Exemplu: In cazul apelului:

printf(“Produsul %d %s $%f\n”, 2, “costa”, 3.77);
avem
sirul_de_control: “Produsul %d %s $%f\n”

celelalte_argumente: 2, “costa”, 3.77
Expresiile din ‘celelalte_argumente’ sunt evaluate si convertite conform cu formatele din sirul de control si apoi plasate in sirul de iesire. Caracterele din sirul de control care nu fac parte dintr-un format sunt plasate direct in sirul de iesire. Simbolul % introduce o
specificare de conversie sau format. O specificare de conversie este un sir care incepe cu % si se termina cu un caracter de conversie.
Tabelul de mai jos reprezinta caracterele de conversie (si modul lor de afisare) pentru functia “printf()”:

Caracter conversie
Cum sunt afisate de argumentele corespunzatoare ?
c
ca un caracter
d, i
ca un intreg zecimal
u
ca un intreg zecimal fara semn
o
ca un intreg octal fara semn
x, X
ca un intreg hexazecimal fara semn
e
ca un numar in virgula mobila; (ex: 7.123000e+00)
E
ca un numar in virgula mobila; (ex: 7.123000E+00)
f
ca un numar in virgula mobila; (ex: 7.123000)
g
in formatul cel mai scurt dintre “e” sau “f”
G
in formatul cel mai scurt dintre “E” sau “f”
s
ca un sir
p
argumentul corespunzator este un pointer catre void; valoarea sa se va tipari ca un intreg hexazecimal
n
argumentul corespunzator este un pointer catre un intreg; argumentul nu este convertit
%
cu formatul %% se va afisa un singur % catre sirul de iesire; nu avem argumente ce trebuie convertite

Exemplu: Iata un exemplu de folosire a formatului “%n”:

#include
void main()
{
int * pi;
printf(“Mai multe caractere %n.\n”, pi);
printf(“Nr.caractere = %d”, *pi);
}

Pe ecran se va afisa numarul de caractere afisate pana la aparitia formatului “%n” (in cadrul instructiunii de afisare “printf()” curente), adica 20. Functia “printf()” intoarce un “int” ce reprezinta numarul de caractere tiparite dupa inlocuirea corespunzatoare a specificatorilor de conversie.

Exemplu:

#include
void main()
{
int * pi;
int a = printf(“Mai mult de %d caractere %n.\n”, 10, pi);
printf(“Numarul de caractere intors de functia printf() este %d\n”, a);
printf(“Nr.caractere = %d”, *pi);
}

In specificarile de conversie pot fi incluse informatii de formatare explicite. Daca ele nu apar, atunci sunt folosite cele implicite. De exemplu, formatul %f cu argumentul 3.77 va afisa 3.770000. Numarul este afisat pe 6 caractere la dreapta punctului zecimal (implicit). Intre % (care specifica inceputul unei specificari de conversie) si caracterul de conversie de la sfarsit, pot apare in ordine:
- zero sau mai multe caractere (flag) care modifica intelesul specificarii de conversie;
- un intreg pozitiv optional care specifica minimul lungimii campului a argumentului convertit.
Locul unde un argument este tiparit se numeste “campul sau”, iar numarul de spatii folosit pentru afisarea sa se numeste “lungimea campului”. Daca argumentul convertit are mai putine caractere decat lungimea campului specificat, atunci acesta se va completa cu spatii (la stanga sau dreapta). Daca argumentul convertit are mai multe caractere decat lungimea campului specificat, atunci lungimea campului se va mari cu cat este necesar. Daca intregul care defineste lungimea campului incepe cu zero si argumentul ce se tipareste este ajustat dreapta in campul sau, atunci pentru umplerea sa se vor folosi zerouri in loc de spatii;
- o “precizie” optionala, care este specificata de un punct urmat de un intreg nenegativ. Pentru conversii d, i, o, u, x si X aceasta specifica numarul minim de cifre ce trebuie afisate. Pentru conversii e, E si f aceasta specifica numarul de cifre de la dreapta punctului zecimal. Pentru conversii g si G aceasta specifica numarul maxim de cifre semnificative. Pentru conversie cu s, aceasta specifica numarul maxim de caractere ce trebuie tiparite dintr-un sir;
- un h sau l optional, care este un modificator “short” sau “long”, respectiv. Daca h este urmat de un d, i, o, u, x sau X, atunci aceasta este o specificare de conversie relativ la “short int” sau “unsigned short int”. Daca un h este urmat de n, atunci argumentul corespunzator este un pointer catre “short int” sau “unsigned short int”. Daca l este urmat de d, i, o, u, x sau X, atunci specificarea de conversie se aplica unui argument “long int” sau “unsigned long int”. Daca l este urmat de caracterul n, atunci argumentul corespunzator este un pointer catre “long int” sau “unsigned long int”;
- L optional, care este un modificator “lung”. Daca L este urmat de e, E, f, g sau G, specificarea de conversie se aplica unui argument “long double”.

Caracterele “flag” sunt:
- semnul “-” va implica alinierea spre stanga a argumentului convertit in campul sau. Daca nu gasim nici un semn “-”, atunci alinierea argumentului convertit se face la dreapta campului.
- semnul “+” va implica afisarea semnului “+” in cazul numerelor pozitive (functioneaza pentru d, i, e, E, f, g si G). Toate numerele negative incep cu semnul minus.
- un spatiu, care semnifica ca un numar pozitiv (ce provine dintr-o conversie unsigned) are la inceput un spatiu;
- un “#”, care semnifica ca rezultatul trebuie convertit la o forma alternativa ce depinde de caracterul de conversie. Daca caracterul de conversie este “o”, atunci “#” cauzeaza afisarea unui zero in fata numarului octal. Intr-o conversie x sau X, “#” cauzeaza 0x, respectiv 0X, in fata numarului hexazecimal. In conversiile g si G, acesta cauzeaza tiparirea unor zerouri la coada (pana la un anumit numar de zecimale, de obicei 5). In conversiile e, E, f, g sau G, aceasta cauzeaza tiparirea punctului zecimal, chiar si cu precizia 0. Pentru alte conversii, comportarea lui “#” este nedefinita.
- un “0″ (zero), care inseamna ca in loc de spatii sunt folosite zerouri. Cu caracterele de conversie d, i, o, u, x, X, e, E, f, g si G, aceasta poate produce zerouri nesemnificative (in fata numarului).

Intr-un format, lungimea campului sau precizia (sau ambele), pot fi specificate folosind *, in loc de un intreg, lucru care indica ca o valoare se obtine dintr-o lista de argumente.

Exemplu:

int m, n;
double x = 333.7777777;
. . . . . . . . . . .
/* citeste m si n (sau le calculam cumva) */
. . . . . . . . . . .
printf(“x = %*.*f\n”, m, n, x);

Daca argumentul corespunzator lungimii campului are lungime negativa, atunci se considera “-” ca fiind un “flag” urmat de o valoare pozitiva. Daca argumentul corespunzator preciziei are valoare negativa, atunci acesta se considera ca lipseste.
Tabelul de mai jos contine formate de caractere si siruri (folosim ghilimele pentru delimitarea vizuala a campului, ele nefiind afisate).

Declaratii si initializari

char c = ‘A’, s[] = “Luna rosie!”;

Format corespunzator
Argumentul
Cum este afisat in campul sau ?
Observatii
%c
c
“A”
Lungimea campului 1 implicit
%2c
c
” A”
Lungimea campului 2, aliniat dreapta
%-3c
c
“A ”
Lungimea campului 3, aliniat stanga
%s
s
“Luna rosie!”
Lungimea campului 11 implicit
%3s
s
“Luna rosie!”
Spatii suplimentare
%.6s
s
“Luna r”
Precizia 6
%-11.8s
s
“Luna ros”
Precizie 8, aliniere stanga

13.2 Functia de intrare “scanf()”

Are doua proprietati importante care permit o folosire flexibila la un nivel inalt:
- poate citi o lista de argumente de lungime arbitrara;
- intrarea este controlata de specificari de conversie simple (sau formate).

Functia “scanf()” citeste caractere din fisierul standard “stdin”. Lista de argumente a functiei “scanf()” are doua parti:
sir_de_control si celelalte_argumente

Exemplu: Fie declaratiile si apelul functiei “scanf()”:

char a, b, c, s[100];
int n;
double x;
scanf(“%c%c%c%d%s%lf”, &a, &b, &c, &n, s, &x);

Avem:
sir_de_control: “%c%c%c%d%s%lf”
celelalte_argumente: &a, &b, &c, &n, s, &x
Celelalte argumente urmate de un sir de control consta dintr-o lista separata prin virgule de expresii pointer sau adrese (reamintim ca “s” este insusi o adresa).

Directive in sirul de control

Sirul de control din “scanf()” este compus din trei tipuri de “directive”:
- caractere ordinare
- spatii goale
- specificari de conversie

Caractere ordinare

Caracterele din sirul de control (diferite de spatiile goale si caracterele din specificarile de conversie) sunt numite “caractere ordinare”. Caracterele ordinare trebuie sa se regaseasca (potriveasca) cu cele din sirul de la intrare.

Exemplu:

float suma;
scanf(“$%f”, &suma);

Caracterul $ este ordinar. Deci trebuie sa intalnim $ in sirul de la intrare. Daca are loc o potrivire cu succes, atunci spatiile goale (daca exista) se vor sari, si caracterele care urmeaza se vor potrivi la o valoare (in virgula mobila). Valoarea convertita va fi plasata in memorie la adresa variabilei “suma”.

Caractere “spatii goale”

Caracterele spatii goale din sirul de control care nu fac parte dintr-o specificare de conversie se potrivesc cu orice spatiu liber din sirul de intrare.

Exemplu:

char c1, c2, c3;
scanf(” %c %c %c”, &c1, &&c2, &c3);

Daca sirul de la intrare contine literele “a”, “b”, si “c”, atunci “c1″, “c2″ si “c3″ vor avea valorile “a”, “b”, “c” (a nu se citi ghilimelele). O directiva spatiu liber implica ca spatiile goale (daca exista) sa fie ignorate din sirul de intrare.

Exemplu: Urmatoarele instructiuni sunt echivalente:

scanf(” %c %c %c”, &c1, &c2, &c3);
scanf(“\t%c \t %c\n%c”, &c1, &c2, &c3);

Intre % si caracterul de conversie poate fi:
- caracterul * optional, care indica o suprascriere, urmata de un intreg optional care defineste lungimea maxima a sirului de intrare (care va fi deci ignorat), urmat optional de h, l, L care modifica caracterul de conversie;
- modificatorul h, care poate precede caracterele de conversie d, i, o, u, x sau X. Acesta precizeaza ca valoarea convertita trebuie memorata ca un “short int” sau “unsigned short int”;
- modificatorul l, care poate precede caracterele de conversie d, i, o, u, x, X sau e, E, f, g, G. In primul caz, acesta precizeaza ca valoarea trebuie memorata ca un “long int” sau “unsigned long int”.
In cel de-al doilea caz, acesta precizeaza ca valoarea convertita trebuie memorata ca un “double”;
- modificatorul L, care poate precede caracterele de conversie e, E, f,g sau G. Acesta precizeaza ca valoarea convertita trebuie memorata ca un “long double”.
Caracterele din sirul de intrare sunt convertite la valori in concordanta cu specificarile de conversie din sirul de control si plasate la adresa data prin expresia pointer corespunzatoare din lista de argumente. Cu exceptia unei intrari caracter, un camp de scanare consta dintr-un numar contiguu de caractere diferite de spatiu (conforme cu conversia specificata). Campul de scanare se termina cand se gaseste un caracter neadecvat, sau s-a depasit lungimea scanarii (daca ea este precizata), sau s-a ajuns la EOF (depinde care vine primul).
Specificarea %s sare spatiile goale si apoi citeste caractere diferite de spatiu pana cand se gaseste spatiu sau EOF (depinde care vine primul). In schimb, specificarea %5s sare spatiile goale, apoi citeste caracterele diferite de spatiu, dar cel mult 5 caractere sau pana la EOF (depinde care vine primul). Cand se citeste un sir de caractere, se presupune ca in memorie este deja rezervat suficient spatiu pentru memorarea sa (cu tot cu santinela ).
Formatul %nc (unde “n” este o constanta intreaga) foloseste la citirea urmatoarelor n caractere, inclusiv spatii goale (se presupune ca s-a rezervat suficient spatiu in memorie pentru pastrarea lor, iar caracterul nu se mai adauga).

Numere in virgula mobila din sirul de intrare

Numerele in virgula mobila din sirul de intrare sunt formatate cu un semn optional (+ sau -) urmat de un sir de cifre cu un punct zecimal optional, urmat de parte exponentiala optionala. Partea exponentiala consta din e sau E, urmate de un semn optional (+ sau -), urmat de un sir de cifre.

Exemple:

77
+7.7e1
770.0E-1
+0.003

Nu uitati: Sirul de intrare nu este cod C (se aplica reguli diferite).

Folosirea multimii de scanare

O specificare de conversie de forma %[sir] indica ca un sir special poate fi citit. Multimea de caractere dintre parantezele patrate se numeste “multime de scanare”. Daca primul caracter din multimea de scanare nu este caracterul circumflex “^”, atunci sirul trebuie sa fie construit numai din caractere ce apartin multimii de scanare.

Exemple:

1. Formatul %[abc] va citi orice sir care contine literele “a”, “b” si “c” si se va opri daca orice alt caracter va apare in sirul de intraare, inclusiv un spatiu (ex. scanf(“%[abc]“, m)).
2. In contrast, formatul %[^abc] va citi orice ce se va termina cu “a”, “b” sau “c”, dar nu si spatiu.
3. Fie codul
char m[30];
scanf(“%29[AB \t\n]“, m);

Aceasta va produce citirea in vectorul de caractere “m” a unui sir de cel mult 29 caractere. Sirul consta din literele A, B,
spatiu, tab, newline. La sfarsit, se va scrie .
4. Programatorii de obicei gandesc o linie ca un sir de caractere, inclusiv spatii si taburi, care se termina cu un newline.
Un mod (elegant) de a citi o linie in memorie este folosirea unei multimi de scanare potrivita:
char linie[256];
while (scanf(” %[^\n]“, linie) == 1)
printf(“%s\n”, linie);

Valoarea returnata de “scanf()”

Cand “scanf()” este apelata, poate apare o greseala la citire. De exemplu, daca nu sunt caractere in sirul de intrare, atunci “scanf()” va intoarce -1 (EOF). Daca apare o nepotrivire intre formatele din “scanf()” si sirul de la intrare, atunci “scanf()” va intoarce numarul de conversii cu succes pana in acel moment. Numarul este zero daca nu apar conversii. Daca “scanf()” reuseste cu succes, atunci este returnat numarul de conversii cu succes. La fel, acest numar poate fi zero.

Exemplu:

char c, *sir_control, s[7], m[18];
int a, contor;
sir_control = “%d , %*s %% %c %[abc] %*s %5s %s”;
contor = scanf(sir_control, &a, &c, s, m, &m[5]);

Consideram ca avem la intrare sirul:
23 , ignora % C abacus citeste_aceasta**
Atunci:
“23″ este plasat in memorie la adresa lui “a”
“,” se potriveste
“ignora” este un sir ignorat
“%” se potriveste

“C” este plasat in memorie la adresa lui “c”
“abac” este plasat in s[0],…,s[4]=”
“us” este un sir ignorat
“cites” este plasat in m[0],…,m[5]=”
“te_aceasta**” este plasat in m[5],…,m[18]=”

Din moment ce au avut loc 5 conversii cu succes, rezulta ca functia “scanf()” va intoarce valoarea 5.

Exemplu: Tabelul de mai jos contine mai multe exemple de directive de control pentru functia “scanf()”:

Directive in sirul de control
Tipul argumentului corespunzator
Continutul sirului de intrare
Observatii
ab%2c
char *
abacus
ab se potriveste, ac se converteste
%3hd
short *
-7733
-77 se converteste
%41i
long *
+0×66
+0×6 se converteste
-%2u
unsigned *
-123
- se potriveste, 12 se converteste
+ %lu
unsigned long *
+-123
+ se potriveste, -123 se converteste
+ %lu
unsigned long *
+ -123
+ se potriveste, -123 se converteste
+ %lu
unsigned long *
+- 123
+ se potriveste, eroare, (- nu se converteste)
%3e
float *
+7e-2
+7e se converteste
%4f
float *
7e+22
7e+22 se converteste
%51f
double *
-1.2345
-1.23 se converteste
%4Lf
long double *
12345
1234 se converteste
%p
void * *
dependent poate citi ceea ce printf(),de sistem cu %p scrie la iesire

13.3 Functiile “sprintf()” si “sscanf()”

Functiile “sprintf()” si “sscanf()” sunt versiuni ce folosesc siruri ale functiei “printf()” si “scanf()”, respectiv. Prototipurile lor,
care se gasesc in “stdio.h”, sunt:
int sprintf(char *s, const char *format, …);
int sscanf(const char *s, const char *format, …);

Punctele … indica compilatorului faptul ca functia poate avea un numar variabil de argumente. O instructiune de forma:
sprintf(sir, sir_de_control, alte_argumente);
scrie rezultatul in sirul de caractere “sir”. Intr-o maniera similara, o instructiune de forma:
sscanf(sir, sir_de_control, alte_argumente);
citeste rezultatul din sirul de caractere “sir”.

Exemplu:

char * sir_intrare = “1 2 3 ab”;
char sir_iesire[100], temp[100];
int a, b, c;
sscanf(sir_intrare, “%d%d%d%s”, &a, &b, &c, &temp);
sprintf(sir_iesire, “%s %s %d%d%d\n”, temp, temp, a, b, c);
printf(“%s”, sir_iesire);

Atunci se va afisa la ecran:
ab ab 123
Atentie ! Este responsabilitatea programatorului sa rezerve spatiu suficient pentru memorarea lui “sir_iesire” din “sprintf()”.

13.4 Functiile “fprintf()” si “fscanf()”

Functiile “fprintf()” si “fscanf()” sunt versiunile pentru fisiere a functiilor “printf()” si “scanf()”. Fisierul “stdio.h” contine un numar de constructii referitoare la fisiere. In acest fisier exista si tipul structura FILE a caror membrii descriu starea curenta a unui fisier. Tot in acest fisier, sunt definiti trei pointeri la fisier. Este vorba despre “stdin”, “stdout” si “stderr”.

Denumirea in C
Numele complet
Observatii
stdin
standard input file
conectat la tastatura
stdout
standard output file
conectat la ecran
stderr
standard error file
conectat la ecran

In fisierul “stdio.h” exista prototipurile pentru functiile “fprintf()” si “fscanf()”:
int fprintf(FILE *ofp, const char *format, …);
int fscanf(FILE *ifp, const char *format, …);
(“ofp” – outfile pointer, iar “ifp” – infile pointer)

Punctele … spun compilatorului ca functia ia un numar variabil de argumente. O instructiune de forma:
fprintf(pointer_catre_fisier, sir_de_control, alte_argumente);
va scrie in fisierul spre care pointeaza “pointer_catre_fisier”. In particular,
fprintf(stdout, …); este echivalent cu printf(…);
Intr-o maniera similara, o instructiune de forma:
fscanf(pointer_catre_fisier, sir_de_control, alte_argumente);
va citi din fisierul spre care pointeaza “pointer_catre_fisier”. In particular,
fscanf(stdin, …); este echivalent cu scanf(…);

13.5 Accesarea fisierelor

Fisierele au cateva proprietati importante:
- au un nume
- trebuie inchise si deschise
- poate fi scris in ele sau citit din ele sau adaugat la ele
- cand sunt deschise avem acces la ele de la inceput la sfarsitul lor

Abstract, un fisier poate fi gandit ca un sir de caractere. Dupa ce un fisier a fost deschis, sirul poate fi accesat folosind functii din biblioteca standard.

Exemplu:

#include
void main()
{
int suma = 0, val;
FILE *ifp, *ofp;
ifp = fopen(“fis_in”, “r”); /* deschis pentru citire */
ofp = fopen(“fis_out”, “w”); /* deschis pentru scriere */
. . . . . .
}

In acest exemplu, am deschis fisierul “fis_in” pentru citire si “fis_out” pentru scriere. Din momentul deschiderii fisierului, pointerul catre fisier poate fi folosit exclusiv pentru referirea la intregul fisier. Daca, de exemplu, presupunem ca fisierul “fis_in” are numere intregi, atunci iata o modalitate de a face suma lor:
while (fscanf(ifp, “%d”, &val) == 1)
sum += val;
fprintf(ofp, “Suma lor este %d.\n”, suma);

Ca si “scanf()”, functia “fscanf()” intoarce numarul de conversii cu succes. Dupa ce terminam de exploatat fisierele, putem sa le inchidem:
fclose(ifp);
fclose(ofp);

Un apel de functie de forma “fopen(nume_fisier, mod)” deschide fisierul respectiv intr-un mod particular si returneaza un pointer catre fisier. Sunt mai multe posibilitati pentru modul de accesare a fisierului:

Mod de acces
Semnificatie
r
deschide fisier text pentru citire
w
deschide fisier text pentru scriere
a
deschide fisier text pentru adaugare
rb
deschide fisier binar pentru citire
wb
deschide fisier binar pentru scriere
ab
deschide fisier binar pentru adaugare

Fiecare dintre aceste moduri se poate termina cu “a+”. Asta inseamna ca fisierul poate fi deschis si pentru citire si pentru scriere.

Mod de acces
Semnificatie
r+
deschide fisier text pentru citire si scriere
w+
deschide fisier text pentru scriere si citire

Deschiderea pentru citire a unui fisier care nu exista, sau care nu poate fi citit, va esua si functia “fopen()” va intoarce pointerul NULL. Deschiderea unui fisier pentru scriere va avea ca efect crearea unui fisier (daca acesta nu exista) sau se va suprascrie peste unul existent. Deschiderea unui fisier pentru adaugare va avea ca efect crearea unui fisier (daca acesta nu exista) sau se va scrie la sfarsitul sau (daca acesta exista).
Daca scriem modul “r+” sau “w+” atunci fisierul se considera ca a fost deschis pentru citire si scriere. Cu toate acestea citirile nu pot fi urmate de scrieri decat daca s-a ajuns la EOF sau au intervenit apeluri ale functiilor “fseek()”, “fsetpos()” sau “rewind()”. De asemeni, scrierile nu pot fi urmate de citiri decat daca s-a ajuns la EOF sau au intervenit apeluri ale functiilor “fflush()”, “fseek()”, “fsetpos()” sau “rewind()”.

13.6 Accesarea aleatoare a unui fisier

In plus fata de accesarea unui caracter unul dupa altul intr-un fisier (acces secvential), noi putem accesa caractere in locuri diferite (acces aleator). In biblioteca C, functiile “fseek()” si “ftell()” sunt folosite pentru accesarea aleatoare a unui fisier. O expresie de forma:
ftell(pointer_catre_fisier)
returneaza valoarea curenta a indicatorului de pozitie in fisier. Valoarea reprezinta numarul de octeti pornind de la inceputul fisierului, numarand de la 0. Cand un caracter este citit dintr-un fisier, sistemul incrementeaza indicatorul de pozitie cu 1. Tehnic vorbind, indicatorul de pozitie in fisier este un membru a structurii catre care pointeaza “pointer_catre_fisier”. Pointerul catre fisier nu pointeaza catre caractere individuale din fisier (aceasta este o eroare de conceptie pe care o fac programatorii incepatori).
Functia “fseek()” are trei argumente:
- pointer catre fisier
- offset (intreg)
- un intreg care arata locul fata de care se calculeaza offset-ul

O instructiune de forma
fseek(pointer_catre_fisier, offset, mod);
seteaza indicatorul de pozitie la o valoare care reprezinta “offset” octeti pornind de la “mod”. Valoarea lui “mod” poate fi 0, 1 sau 2, insemnand ca ne referim la inceputul fisierului, pozitia curenta sau sfarsitul fisierului, respectiv.
Atentie ! Functiile “fseek()” si “ftell()” sunt garantate sa lucreze numai pentru fisiere binare. In MS-DOS, daca dorim sa lucram cu aceste functii, trebuie sa deschidem acest fisier in acces binar. In UNIX, din moment ce exista doar un singur mecanism de lucru cu fisierele, orice mod de deschidere pentru fisier este bun.

13.7 Stil de programare

Un stil bun de programare va verifica daca functia “fopen()” lucreaza asa cum ne asteptam (in orice program serios, acest lucru trebuie facut). Iata cum ar trebui sa se faca deschiderea fisierului “fis1″ in acces de citire:
if ((ifp = fopen(“fis1″, “r”)) == NULL)
{
printf(“\nNu putem deschide fisierul fis1. Pa!\n\n”);
exit(1);
}

Categories: Tutoriale C++

Capitolul 12: Recursie

octombrie 31, 2008 Scrie un comentariu

Recursie

O functie este recursiva daca se autoapeleaza, direct sau indirect. In C toate functiile se pot defini recursiv.

Exemplu:

#include
void numara(int n);

void main()
{
numara(10);
}

void numara(int n)
{
if (n)
{
printf(“%d ! “, n);
numara(n – 1);
}
else
printf(“Gata !\n”);
}

Dupa executia acestui program, pe ecran se va tipari
10 ! 9 ! 8 ! 7 ! 6 ! 5 ! 4 ! 3 ! 2 ! 1 !
Gata !

Acest program s-ar fi putut realiza si iterativ (folosind o instructiune de tip while).

Exemplu: Suma primelor n numere naturale.

int suma(int n)
{
if (n <= 1)
return n;
else
return (n + suma(n – 1));
}

De obicei, functiile recursive urmeaza un “pattern” standard:
- exista un caz de baza (sau mai multe);
- caz recursiv general (in care, in general, un intreg este trimis ca argument al apelului recursiv);

Recursia este un procedeu foarte puternic de rezolvare a problemelor. Secretul este identificarea cazului general.
Pentru exemplul precedent, cand se trimite n catre functia “suma()”, recursia activeaza n copii ale functiei inaintea intoarcerii pas cu pas catre primul apel recursiv (se mai spune ca in momentul apelului recursiv, variabilele locale “ingheata”, ele “dezghetandu-se” la intoarcerea din recursie). Multe functii recursive se pot scrie intr-o forma iterativa (folosind structuri de tip “while”, se mai spune “derecursivare”). Recursia se recomanda cand problema se poate rezolva foarte usor folosind recursie si cand nu se cere o eficienta sporita in timpul executiei programului. Uneori, se recomanda recursia finala (adica dupa apelul recursiv nu mai sunt alte instructiuni si nu exista variabile locale).

Exemplu: Citeste o linie si o afiseaza in ordine inversa, apoi lasa doua randuri goale.

#include
void tipareste(void);

void main()
{
printf(“Introduceti o linie: “);
tipareste();
printf(“\n\n”);
}

void tipareste(void)
{
char c;
if ((c = getchar()) != ‘\n’)
tipareste();
putchar(c);
}
Iata o rulare in executie:
Introduceti o linie: iepurasu usa rupei
iepur asu usarupei
Observati in exemplul precedent ca la fiecare apel recursiv, se memoreaza in stiva caracterul “c” legat la o valoare, care se va afisa la intoarcerea din recursie. Deci practic, sunt “n” copii ale lui “c”, unde “n” reprezinta lungimea liniei.

Exemplu:

Putem complica putin exemplul precedent, in sensul ca afisam aceleasi cuvinte, dar in ordine inversa.
#include
#include

#define MAXWORD 100

void tipareste_cuvinte(void);
void citeste_cuvant(char *);

void main()
{
printf(“Introduceti o linie: “);
tipareste_cuvinte();
printf(“\n\n”);
}

void tipareste_cuvinte(void)
{
char w[MAXWORD];

citeste_cuvant(w);
if (w[0] != ‘\n’)
tipareste_cuvant();
printf(“%s “, w);
}

void citeste_cuvant(char *s)
{
static char c = ”;

if (c == ‘\n’)
*s++ = c;
else
while (!isspace(c = getchar()))
*s++ = c;
*s = ”;
}
Daca, la executie, utilizatorul scrie:
Introduceti o linie: noi invatam C
atunci pe ecran, va apare:
C invatam noi
Variabila “c” avand clasa de memorare “static”, rezulta ca valoarea ei se pastreaza de la un apel la altul. De altfel, initializarea lui “c” se face o singura data (cand se intra prima data in aceasta functie). Daca “c” ar fi fost de tip “auto”, atunci chiar daca aveam la sfarsitul sirului ‘\n’, la urmatorul apel, acesta nu ar fi fost cunoscut, deci practic nu mai aveam conditie de oprire.

Exemplu:

In acest exemplu, vom desena “pattern-uri” pe ecran folosind functii recursive.
#include
#define SYMBOL ‘*’
#define OFFSET 0
#define LENGTH 19

void display(char, int, int);
void draw(char, int);

void main()
{
display(SYMBOL, OFFSET, LENGTH);
}

void display(char c, int m, int n)
{
if (n > 0)
{
draw(‘ ‘, m);
draw(c, n);
putchar(‘\n’);
display(c, m + 2, n – 4);
}
}

void draw(char c, int k)
{
if (k > 0)
{
putchar(c);
draw(c, k – 1);
}
}
Functia “main()” contine apelul functiei “display()”, care apeleaza “draw()”, care la randul ei apeleaza “display()”. Deci functia
“display()” este recursiva. Functia “draw()” tipareste k copii ale caracterului “c”. Pe ecran se va afisa:
* * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * *
* * * * * * * * * * *
* * * * * * *
* * *

12.1 Manipularea sirurilor folosind recursia

Un sir consta dintr-un numar de caractere consecutive, terminate prin caracterul ”. De fapt, putem gandi un sir ca fiind sirul nul (care consta doar din caracterul ”) sau un caracter urmat de un sir. Aceasta definitie a sirului este o structura de date recursiva.

Exemplu: O definitie recursiva a lungimii unui sir.

int r_strlen(char *s)
{
if (*s == ”)
return 0;
else
return (1 + r_strlen(s + 1));
}

Eleganta acestei formulari recursive este “platita” de o pierdere in timpul executiei. Daca sirul are lungimea k, calcularea lungimii sale necesita k + 1 apeluri recursive (un compilator optimizat poate evita aceasta pierdere).

12.2 Metodologia “divide-et-impera”

Recursia se foloseste in foarte multe cazuri pentru codificarea algoritmilor “divide-et-impera”. Un astfel de algoritm imparte problema in subprobleme, rezolvand fiecare subproblema prin recursie, apoi recombina solutiile partiale pentru a obtine intreaga solutie.
Vom considera un exemplu cunoscut, si anume, determinarea minimului si maximului elementelor unui sir de intregi (publicat pentru prima data de catre Ira Pohl, “A Sorting Problem and Its Complexity”, Communications of the ACM, 15, nr. 6, 1972) considerat cel mai bun algoritm pentru aceasta problema. Criteriul pentru “cel mai bun” a fost numarul de comparatii necesare. Prezentam mai jos o functie C care rezolva aceasta problema (considerand dimensiunea sirului putere a lui 2).
void minmax(int a[], int n, int *min_ptr, int *max_ptr)
{
int min1, max1, min2, max2;

if (n == 2)
if (a[0] < a[1])
{
*min_ptr = a[0];
*max_ptr = a[1];
}
else
{
*min_ptr = a[1];
*max_ptr = a[0];
}
else
{
minmax(a, n/2, &min1, &max1);
minmax(a + n/2, n/2, &min2, &max2);
if (min1 < min2)
*min_ptr = min1;
else
*min_ptr = min2;
if (max1 < max2)
*max_ptr = max2;
else
*max_ptr = max1;
}
}

Categories: Tutoriale C++

Capitolul 11: Directive preprocesor si metodologie de programare

octombrie 31, 2008 Scrie un comentariu

Directive preprocesor si metodologie de programare

11.1 Folosirea lui #include

Am discutat deja folosirea directivelor de preprocesare
#include
#include

O alta forma pentru #include este
#include “nume_fisier”
Preprocesorul va inlocui aceasta linie cu o copie a fisierului precizat. Mai intai cautarea se face in directorul curent, apoi
in alte locuri dependente de sistem. Daca directiva este de forma
#include
atunci preprocesorul va cauta in alte locuri (deci nu in directorul curent). De exemplu, sub UNIX, fisierele header standard (cum ar fi “stdio.h”, “stdlib.h”) se gasesc de obicei in directorul
/usr/include
Sub MS-DOS, aceste fisiere se gasesc in directorul
/include

11.2 Folosirea lui #define

Directivele de preprocesare declarate cu “#define” au doua forme:
- #define identificator sir_atomi
- #define identificator(id,…,id) sir_atomi

O definitie lunga (care nu dorim sa o scriem pe aceeasi linie poate fi continuata pe linia urmatoare punand un \ (backslash) la sfarsitul liniei curente). In primul caz, compilatorul va inlocui fiecare aparitie a “identificatorului” prin “sir_atomi” in restul fisierului (de la pozitia curenta in jos) cu exceptia celor care sunt incadrate intre ghilimele sau apostroafe.

Exemple:

#define NR_SEC_PE_ZI (60 * 60 * 24)
#define PI 3.141592653
#define C 299792.458 /* viteza luminii in km/sec */
#define EOF (-1) /* valoarea uzuala pt sfarsit de fisier */
#define MAXINT 2147483647 /* numarul intreg maxim pe 4 octeti */
#define DIMENS 250 /* dimensiunea unui sir */
#define EPSILON 1.0e-9 /* limita numerica */

Deci, folosirea lui “#define” mareste claritatea si portabilitatea unui program.

11.3 Sintaxa “dulce”

Se foloseste pentru evitarea unor greseli frecvente sau ca un moft.

Exemplu:

#define EQ ==

Aceasta declaratie ajuta programatorul sa nu mai confunde = cu ==.

Exemplu:

#define do /* spatiu */

De exemplu, acum putem simula instructiunea “while” din C ca un “while do” din Pascal sau Algol.
De exemplu, daca avem definitiile de sintaxa “dulce” de mai sus, putem spune ca instructiunile
while (i EQ 1) do
{
. . . . .
}
si
while (i == 1)
{
. . . . .
}

sunt echivalente.

11.4 Macrouri cu argumente

Revenim la forma a doua a macrourilor cu argumente:
#define identificator(id,…,id) sir_atomi

Exemplu:

#define SQ(x) ((x) * (x))

Identificatorul x din #define este un parametru care va fi substituit in textul ce urmeaza. Substitutia se face fara considerarea
corectitudinii sintactice. De exemplu,
SQ(7 + w) este echivalent cu ((7 + w) * (7 + w))
Intr-o maniera similara,
SQ(SQ(*p)) este echivalent cu ((((*p) * (*p))) * (((*p) * (*p))))
Observati deci ca folosirea parantezelor (de exemplu, (x)) are o importanta deosebita, altfel nu s-ar respecta ordinea de evaluare.

11.5. Unde este greseala ?

#define SQ(x) ((x) * (x));

Macrourile sunt folosite de obicei pentru a inlocui apelurile functiilor cu cod liniar (scurte si fara variabile suplimentare).

Exemplu: Macroul de mai jos defineste minimul a doua valori:

#define min(x, y) (((x) < (y)) ? (x) : (y))
Dupa aceasta definitie, o expresie de forma
m = min(u, v)
se poate expanda de catre preprocesor la
m = (((u) < (v)) ? (u) : (v))
Folosind aceasta definitie, putem defini minimul a patru valori, astfel
#define min4(a, b, c, d) min(min(a,b), min(c, d))
O macro-definitie poate folosi functii si macrouri in corpul lor.

Exemple:

#define SQ(x) ((x) * (x))
#define CUB(x) (SQ(x) * (x))
#define F_POW(x) sqrt(sqrt(CUB(x)))

O directiva de preprocesare de forma
#undef identificator va anula definitia precedenta a identificatorului.

11.6 Definitii de tipuri si macrouri din

C pune la dispozitie facilitatea “typedef” pentru a asocia (redenumi) un tip cu unul specific.

Exemplu: typedef char uppercase;

Declaratia de mai sus face tipul “uppercase” sinonim cu “char”. De exemplu, declaratiile de mai jos sunt valide:
uppercase c, u[100];
Fisierul header contine cateva definitii de tip:
typedef int ptrdiff_t; /* tip intors de diferenta pointerilor */
typedef short wchar_t; /* tip caracter mare */
typedef unsigned size_t; /* tipul sizeof */

Tipul “ptrdiff_t” spune care este tipul returnat de o expresie implicata in diferenta a doi pointeri. In MS-DOS, acesta depinde de modelul de memorie ales (tiny, short, large, far, huge), pe cand in UNIX, tipul folosit este “int”.
Tipul “wchar_t” se foloseste pentru acele caractere care nu se pot reprezenta pe un octet (char -> int).
Reamintim ca operatorul “sizeof” este folosit pentru determinarea lungimii unui tip sau a unei expresii. De exemplu, “sizeof(double) = 8″. Tipul “size_t” este returnat de operatorul “sizeof”.
Un macrou definit in este
#define NULL 0

11.7 Sortare folosind “qsort()”

Daca avem o multime relativ mica de elemente, atunci putem sa folosim sortare cu bule sau metoda sortarii prin selectie directa (care sunt de ordinul O(n^2)). Daca insa avem multe elemente, atunci este convenabil sa folosim metoda sortarii rapide (“quick sort”). Prototipul functiei “qsort()” se gaseste in . Acesta este
void qsort(void *array, size_t n_els, size_t el_size, int compare(const void *, const void *));
Argumentele acestei functii au rolul:
array – sirul care va fi sortat;
n_els – numarul de elemente ale sirului;
el_size – numarul de octeti necesar memorarii unui element;

compare – functia de comparare, ce se declara ca fiind int compare(const void *, const void *)
Functia de comparare are ca argumente doi pointeri catre void. Aceasta returneaza un intreg care este mai mic, egal sau mai mare decat zero dupa cum primul argument este mai mic, egal sau mai mare decat al doilea argument.

Exemplu:

Vom scrie un program ce foloseste “qsort()”. Initializam un vector, il tiparim, il sortam cu “qsort()”, apoi il tiparim din nou.
#include
#include
#include
#define N 11 /* dimensiunea sirului */

int cmp(const void *vp, const void *vq); /* functia de comparare */
void init(double *a, int n);
void tipareste_sir(double *a, int n);
void main()
{
double a[N];
init(a, N);
tipareste_sir(a, N);
qsort(a, N, sizeof(double), cmp);
tipareste_sir(a, N);
}
int cmp(const void *vp, const void *vq)
{
const double *p = (const double *)vp;
const double *q = (const double *)vq;
double diff = *p – *q;
return ((diff >= 0.0) ? ((diff > 0.0) ? -1 : 0) : +1);
}
void init(double *a, int n)
{
int i;
srand(time(NULL)); /* vezi rand() */
for (i = 0; i < n; ++i)
a[i] = (rand() % 1001) / 10.0;
}
void tipareste_sir(double *a, int n)
{
int i;
for (i = 0; i < n; ++i)
{
if (i % 6 == 0)
putchar(‘\n’);
printf(“%12.1f”, a[i]);
}
putchar(‘\n’);
}

11.8 Un exemplu de utilizare a macrourilor cu argumente

Vom relua problema de mai sus, dar vom folosi macrouri cu argumente. Vom scrie programul in doua fisiere, un fisier header “sort.h” si un fisier “sort.c”. Fisierul header va contine directive de precompilare (#include, #define), precum si prototipuri pentru functiile noastre. Fisierul “sort.h” este:
#include
#include
#include
#include
#define M 32
#define N 11
#define parte_fractionara(x) (x – (int) x)
#define caracter_aleator() (rand() % 26 + ‘a’)
#define real_aleator() (rand() % 100 / 10.0)
#define INIT(array, sz, type) \
if (strcmp(type, “char”) == 0) \
for (i = 0; i < sz; ++i) \
array[i] = caracter_aleator(); \
else \
for (i = 0; i < sz; ++i) \
array[i] = real_aleator();

#define PRINT(array, sz, sir_control) \
for (i = 0; i < sz; ++i) \
printf(sir_control, array[i]); \
putchar(‘\n’)

int compara_partea_fractionara(const void *, const void *);
int lexico(const void *, const void *);

Acum, vom scrie restul codului pentru programul nostru, si anume fisierul “sort.c”.
#include “sort.h”

void main()
{
char a[M];
float b[N];
int i;
srand(time(NULL));
INIT(a, M, “char”);
PRINT(a, M, “%-2c”);
qsort(a, M, sizeof(char), lexico);
PRINT(a, M, “%-2c”);
printf(“—\n”);
INIT(b, N, “float”);
PRINT(b, N, “%-6.1f”);
qsort(b, N, sizeof(float), compara_partea_fractionara);
PRINT(b, N, “%-6.1f”);
}

int compara_partea_fractionara(const void *vp, const void *vq)
{
const float *p = (const float *)vp, *q = (const float *)vq;
float x;
x = parte_fractionara(*p) – parte_fractionara(*q);
return((x < 0.0) ? -1 : (x == 0.0) ? 0 : +1);
}

int lexico(const void *vp, const void *vq)
{
const char *p = (const char *)vp, *q = (const char *)vq;
return(*p – *q);
}

11.9 Compilare conditionata

Preprocesorul are directive pentru compilare conditionata. Acestea pot fi folosite pentru dezvoltarea programelor si pentru scrierea codului mai portabil de la o masina la alta. Fiecare directiva de forma
#if expresie_integrala_constanta

#ifdef identificator
#ifndef identificator implica compilarea conditionata a codului care urmeaza pana la directiva de precompilare #endif
Pentru compilarea codului de mai sus, in cazul lui #if trebuie ca expresia constanta sa fie diferita de zero (true), in cazul lui
#ifdef sau #ifdefined numele identificatorului trebuie sa fie definit anterior intr-o linie #define, fara interventia directivei
#undef identificator
In cazul lui #ifndef, numele identificatorului trebuie sa nu fie curent definit.
Expresia constanta integrala folosita intr-o directiva de precompilare nu poate contine operatorul “sizeof” sau un cast. Poate
insa, folosi operatorul de precompilare “defined” (valabil in ANSI C, dar nu si C traditional). Expresia defined identificator
este echivalenta cu defined(identificator).
Acesta se evalueaza la 1 daca identificatorul este definit, si 0 in caz contrar.

Exemplu:

#if defined(HP9000) || defined(SUN4) && !defined(VAX)
. . . . . /* cod dependent de masina */
#endif

Uneori “printf()” este utila in scopuri de depanare. Presupunem ca la inceputul unui fisier am scris
#define DEBUG 1
si in unele zone ale programului am scris
#if DEBUG
printf(“debug: a = %d\n”, a);
#endif

Daca dupa ce ne-am convins ca este bine ce se intampla si vrem sa nu mai vizualizam valoarea lui “a” in acest moment, atunci schimbam DEBUG in 0 (de exemplu).
O alta varianta ar fi sa nu initializam DEBUG. Scriem deci la inceputul fisierului
#define DEBUG
Putem folosi #ifdef si #if si scriem:
#ifdef DEBUG
. . . . .
#endif

11.10 Macrouri predefinite

In ANSI C sunt 5 macrouri predefinite. Nu pot fi redefinite de catre programator. Ele au la inceput si sfarsit cate doua simboluri “underscore”.

Macro predefinit
Valoare
__DATE__
Un sir ce contine data curenta
__FILE__
Un sir ce contine numele fisierului
__LINE__
Un intreg reprezentand numarul liniei curente
__STDC__
Daca implementarea=ANSI C, atunci acesta
reprezinta un numar diferit de zero
__TIME__
Un sir ce contine timpul curent

11.11 Operatorii # si ##

Operatorii de preprocesare # si ## sunt valabili in ANSI C, dar nu si in C traditional. Operatorul unar # cauzeaza transformarea in sir a unui parametru formal dintr-o macro-definitie.
#define mesaj_pentru(a, b) \
printf(#a ” si ” #b “: Te iubim !\n”)

void main() { mesaj_pentru(Carolina, Nicoleta); }
La apelul acestui macrou, fiecare parametru al acestuia este inlocuit cu argumentul corespunzator, iar # cauzeaza ca argumentele sa fie puse intre ghilimele. Altfel spus, dupa preprocesare, in memorie se obtine:
void main() { printf(“Carolina” ” si ” “Nicoleta” “: Te iubim !\n”); }
Deoarece sirurile constante separate prin spatiu se concateneaza, instructiunea de mai sus este echivalenta cu:
void main() { printf(“Carolina si Nicoleta: Te iubim !\n”); }
Operatorul binar ## este folosit la impartirea in tokenuri lexicale.
Exemplu:

#define X(i) x ## i
X(1) = X(2) = X(3);
va deveni dupa preprocesare
x1 = x2 = x3;

11.12 Macroul “assert()”

ANSI C pune la dispozitie macroul “assert()” din biblioteca standard “assert.h”. Acest macrou poate fi folosit cand vrem sa ne asiguram ca o expresie are o anumita valoare. Vrem sa scriem o functie ale carei argumente satisfaca niste conditii.

Exemplu:

#include
void f(char *p, int n)
{
. . . . .
assert(P != NULL);
assert(n > 0 && n < 5);
. . . . .
}

Daca vreo asertiune esueaza, atunci sistemul va tipari un mesaj si va opri executia programului. Iata o implementare posibila a lui “assert()”.
#if defined(NDEBUG)
#define assert(ignore) ((void) 0) /* ignorare */
#else
#define assert(expr)
if (!(expr)) \
{ \
printf(“\n%s%s\n%s%s\n%s%d\n\n, \
“Assertion failed: “, #expr, \
“in file “, __FILE__, \
“al line “, __LINE__); \
}
#endif

De remarcat ca daca NDEBUG este definit, atunci sunt ignorate toate asertiunile. Aceasta permite programatorului in timpul scrierii programului sa verifice pas cu pas executia programului. Functia “abort()” se gaseste in biblioteca standard.

11.13 Folosirea lui #error si #pragma

ANSI C contine si directivele de preprocesare #error si #pragma.

Exemplu:

#if A_SIZE < B_SIZE
#error “tipuri incompatibile”
#endif

Daca in timpul compilarii va apare o eroare prezenta intr-o directiva #error, atunci se va afisa mesajul respectiv.
Directiva #pragma se foloseste pentru folosire specifica implementarii. Ea are forma generala:
#pragma atomi_lexicali
Aceasta cauzeaza o comportare ce depinde de fiecare compilator C in parte.

11.14 Numerele liniilor unui program

O directiva de preprocesare de forma
#line constanta_integrala “nume_fisier”
va determina compilatorul sa renumeroteze liniile textului sursa astfel incat urmatoarea linie sa aiba valoarea specificata si numele fisierului sursa curent este “nume_fisier”. Daca nu se precizeaza “nume_fisier”, atunci se va face doar numerotarea liniilor. Bineinteles, numerele asociate liniilor sunt ascunse pentru programator si apar numai la mesaje de eroare sau avertismente.

Categories: Tutoriale C++
Follow

Get every new post delivered to your Inbox.