|
Limbajele de programare: cod-masina, assembler, evoluate
Programele care convertesc un program scris de catre utilizator/programator intr-un limbaj de programare, in alt limbaj se numesc translatoare.
Limbajul in care este scris initial programul se numeste limbaj sursa, iar limbajul la care se face conversia se numeste limbaj tinta (limbaj destinatie).
In acelasi timp, programul scris in limbajul sursa se numeste program sursa, iar programul care poate fi executat de procesor, dupa ce este translatat corespunzator se numeste program obiect.
Daca ar exista un procesor care sa execute instructiunile continute de un program sursa, atunci nu ar mai fi necesara translatarea.
Cand limbajul sursa este o reprezentare simbolica pentru un limbaj masina (numeric), translatorul se numeste asamblor (assembler), iar limbajul sursa se numeste limbaj de asamblare.
Cand limbajul sursa este un limbaj de nivel inalt (C, C++, Java. etc) si limbajul destinatie este fie limbajul masina, fie o reprezentare simbolica pentru acest limbaj, translatorul se numeste compilator.
Limbajul de asamblare este un limbaj in care fiecare instructiune produce o singura instructiune masina, existand o corespondenta directa "unu la unu" intre instructiunile limbajului masina si cele ale limbajului de asamblare.
Limbajul cod-masina (pe scurt, limbaj masina) este o colectie de coduri exprimate in hexazecimal si este singurul sistem de coduri care poate fi citit si executat de procesor.
Limbajul de asamblare este uneori folosit de unii programatori pentru ca este mult mai usor de folosit deoarece este compus din nume simbolice si adrese simbolice si din o insiruire de cifre cum este limbajul masina.
Instructiunile limbajului de asamblare au patru parti:
- camp de etichete - folosite pentru a da nume simbolice adreselor de memorie si
unor instructiuni executabile, pentru a permite saltul la instructiuni;
- camp operatie - opcode - codul instructiunii sau o comanda pentru asamblor;
- camp de operanzi;
- camp de comentarii.
Compilatoarele si asambloarele translateaza in general cate o procedura a unui program la un moment dat; fiind mai multe proceduri intr-un program, toate procedurile trebuie "legate" impreuna (referintele la adresele de memorie trebuie sa fie puse in concordanta), acest lucru facandu-l un program numit editorul de legaturi.
Traducerea completa a programului sursa in program obiect necesita doua etape:
- compilarea sau asamblarea procedurilor sursa;
- editarea legaturilor pentru modulele obiect.
Prima etapa este realizata de compilator sau asamblor, iar cea de-a doua de catre editorul de legaturi.
Fiecare procedura este translatata separat pentru a se putea interveni si modifica daca va fi necesar, rezultand cate un modul pentru fiecare procedura.
Apoi, editorul de legaturi realizeaza legaturile pentru toate modulele, rezultand programul obiect, asa cum arata schema de mai jos:
Pentru a "lega" modulele, editorul de legaturi le aduce in memoria interna pentru a forma imaginea programului obiect, de a construi o imagine exacta a spatiului de adrese virtual al programului si de a pozitiona toate modulele obiect la locatiile corecte.
Editorul de legaturi uneste spatiile separate de adresa, deoarece fiecare modul obiect formeaza un spatiu separat de adresa; se compune tabela modulelor obiect, in care se specifica numele modulului, lungimea si adresa de start a fiecaruia.
Tabela modulelor, consuderand ca avem patru module notate A, B, C, D arata asa:
Modul lungime Adresa de start
A400 100
B 600 500
C 500 1100
D 300 1600
Modulele obiect contin sase parti (in general):
Prima parte contine numele modulului, anumite informatii necesare pentru editorul de
legaturi.
Partea a doua este o lista a simbolurilor definite de modul, pe care alte module le pot apela, impreuna cu valorile lor; programatorul trebuie sa declare in limbaj de asamblare care dintre simboluri sunt puncte de intrare (entry points).
Partea a treia contine o lista de simboluri care sunt utilizate in modul, dar care sunt definite in alte module, impreuna cu o lista a instructiunilor masina care folosesc aceste simboluri; programatorul in limbaj de programare indica ce simboluri sunt declarate ca simboluri esterne (external symbols).
Partea a patra este lista instructiunilor si a unor constante necesare prelucrarii; aceasta parte este singura care se incarca in memorie pentru a fi executata. Celelalte cinci parti sunt utilizate de editorul de legaturi si apoi eliminate inainte de inceperea executiei.
Partea a cincia este dictionarul de relocare, necesar pentru calcularea unor adrese.
Partea a sasea este o indicatie de sfarsit de modul, folosit si pentru a descoperi eventuale erori de citire a modulului.
Redam mai jos schema unui modul obiect:
Cele mai multe editoare de legaturi necesita aceste doua treceri, prezentate.
In prima trecere editorul de legaturi citeste toate modulele obiect si construieste o tabela a lungimilor si numelor modulelor, si o tabela de simboluri, constand din toate punctele de intrare si referintele externe.
In a doua trecere modulele obiect sunt citite, relocate si sunt editate legaturile in cate un modul la un moment dat.
3. Evolutia limbajelor de programare
Referirea la limbajele de programare prin "generatii" ale lor este motivata, printre altele, si de evidentierea cresterii puterii de prelucrare ale datelor.
Termenii de "prima" si "a doua" generatie de limbaje de programare nu au fost folosite pana nu s-a precizat asa-zisa generatie a treia.
Prima generatie (1GL) - inainte de 1940 - limbaje de programare au existat inainte sa apara computerele, constand din coduri (numerice si apoi alfa-numerice pentru limbajul de asamblare) care puteau fi "citite" cu ajutorul unor dispozitive mecanice, fiecare avand o anumita semnificatie.
Herman Hollerith a folosit pentru prima data (in 1890) cartele perforate pentru a reprezenta un sistem de coduri (au fost folosite de serviciul de strangere si evidenta a taxelor).
Primele calculatoare, din primele decade ale secolului XX, foloseau sistemul de numeratie zecimal pentru realizarea calculelor, datele fiind codificate prin pozitiile diferite ale unor repere mecanice (roti dintate, parghii,.).
Cand s-a descoperit ca logica poate fi reprezentata si prin numere (0 si 1) nu numai prin cuvinte (DA, NU), s-a trecut la folosirea sistemului binar in reprezentarea datelor si in codificarea actiunilor ce trebuiau desfasurate intr-o anumita ordine.
Programarea era in realitate o codificare.
In anii `40 au aparut primele dispozitive de calcul realizate cu relee electromagnetice si problema programarii a luat o alta turnura, aparand limbajele masina (serie de coduri care realizau, fiecare in parte, anumite activitati), prefigurandu-se cea de-a doua generatie de limbaje de programare.
Acum apare si conceptia revolutionara a lui von Neumann, care a considerat ca datele trebuie introduse in memorie (in forma binara), dar si programul, constituit din secvente de instructiuni reprezentate prin coduri in forma binara, trebuia introdus in memorie, in asa fel incat s-a definit structura von Neumann a calculatorului (aceasta "structura" fiind denumita de specialistii IBM cu termenul "arhitectura", ramas in vigoare si acum).
Printre limbajele dezvoltate in aceasta perioada, au fost:
1943 - ENIAC - masina de calcul, pentru care setul de instructiuni masina a fost pus
la punct in anul 1948;
1949 - 1954 - au aparut seturi de instructiuni mnemonice, incepand cu C-10 pentru
calculatorul BINAC (devenit UNIVAC).
Aceasta generatie a fost ulterior reconsiderata ca fiind generatia limbajelor cod masina, iar generatia a doua considerata generatia limbajelor de asamblare.
Limbajul cod masina este limbaj de "nivel scazut" (low level), cu un foarte scazut nivel de abstractizare (scriere a instructiunilor intr-un fel care sa nu aiba legatura directa cu calculatorul), fiind strict legat de modul de functionare al calculatorului.
Instructiunile in limbaj masina sunt direct executabile, deci nu este necesar compilatorul pentru a traduce aceste instructiuni, pe cand instructiunile scrise in limbajele evoluate (high level) nu pot fi executate decat daca sunt traduse de catre compilator si aduse intr-o forma care sa poata fi executabila (programul sursa - scris intr-un limbaj oarecare este compilat pentru a fi adus intr-o forma executabila, care se numeste program obiect).
Instructiunile erau introduse de la panoul central al calculatorului, prin manevrarea unor intrerupatoare.
Avantajul acestui mod de programare era ca programul era executat rapid, fiind preluat si executat direct de CPU.
Marele dezavantaj era ca acest limbaj era foarte greu de folosit, confuziile erau frecvente si erorile pe masura.
Daca trebuia introdusa o noua instructiune in secventa de instructiuni deja constituita, trebuiau mutate toate instructiunile de mai jos pentru a crea loc, ceea ce era foarte dificil.
A doua generatie (2GL) - anii `50, este generatia limbajelor de asamblare.
Termenul "asamblare" a fost introdus pentru a face distinctia intre generatia a treia si primele doua generatii.
Instructiunile se puteau scrie si citi de catre un programator, iar programul astfel obtinut trebuia tradus in instructiuni cod masina pentru a fi executabile.
Adresele de memorie erau reprezentate simbolic, astfel incat in zone diferite de memorie se introduceau instructiunile, datele de intrare si datele rezultate dupa prelucrare.
Acest limbaj este specific fiecarui procesor, deci nu putea rula decat pe calculatoare din aceeasi familie.
Limbajul de asamblare mai este folosit si acum pentru scrierea unor portiuni din programele pe care le contine kernelul (esenta sistemului de operare) sau pentru scrierea unor parti din sistemul de programe al compilatoarelor.
Generatia a treia (3GL) - este urmasa fireasca a generatiei a doua, de la care a preluat structurarea logica a softului, construind limbaje de programare care s-au despartit de hard si aveau o forma mai apropiata de limbajul uman.
Nivelul inalt (high level) al limbajelor indica distanta fata de caracteristicile calculatorului, munca programatorului fiind independenta de tipul procesorului.
Instructiunile compuneau programul respectiv (programul sursa) care trebuia sa fie tradus in limbajul cod masina pentru a fi executate.
Limbajele de programare aparute in aceasta perioada sunt:
- FORTRAN (1955),
- LISP (1958),
- COBOL (1958).
In aceeasi perioada a fost elaborat si limbajul ALGOL, care a impus noi concepte:
- blocuri complexe continand secvente de instructiuni si de date,
- fiecare bloc putea avea variabile, proceduri separate, invizibile pentru alte
blocuri,
- scrierea limbajului folosind notatii matematice, forma Backus-Naur (Backus-
Naur Form - BNF), pentru sintaxa, linie pastrata de celelalte limbaje
aparute ulterior.
Multe din programele scrise acum folosesc limbaje din a treia generatie, existand seturi de programe de asistenta, care ajuta programatorul sa scrie instructiunile.
Alte limbaje aparute in aceeasi perioada (aproximativ) sunt: SIMULA - 1962, CPL - 1963, BASIC - 1964, PL/I - 1964, BCPL - 1967 limbaj care a constituit baza dezvoltarii limbajului C.
In perioada 1967 ÷ 1978, a limbajelor de programare, s-au stabilit paradigmele fundamentale ale limbajelor de programare.
Astfel limbajul SIMULA, inventat in anul 1962 cu intentia de a surclasa limbajul ALGOL, a fost primul limbaj proiectat sa suporte programarea orientata obiect.
Generatia a patra (4GL) - (1970 ÷ 1990) o formeaza limbajele care tintesc dezvoltarea software-ului comercial, avand un grad superior de abstractizare, o mai mare putere de reprezentare si calcul (calculatoarele au devenit foarte performante sub aspectul hardului).
Se considera de catre istorici ai limbajelor de programare ca aceasta generatie a avut ca scop principal pregatirea generatiei a cincea, care a impus concepte de mai mare abstractizare.
Deoarece metodele de programare tipice generatiei III de limbaje erau greoaie si oricand supuse aparitiei erorilor, s-a cautat o metodologie care sa inlature sursa erorilor si sa faca din programare o treaba mai putin complicata si care sa necesite un efort mai mic.
S-au investit mari sume pentru proiectarea de limbaje sistemice, care incorporau constructii logice performante, interesul pentru programarea sistemelor (programarea la scara mare - programming in the large) prin folosirea modulelor sau a unitatilor de cod organizationale la scara mare fiind trendul acesyei perioade.
Miscarea RISC - Reduced Instructions Set Computers din arhitectura computerelor a dus la concluzia ca hardul RISC trebuie sa fie proiectat pentru compilatoare, viteza marita a acestor computere fiind baza dezvoltarii tehnologiei de compilare a limbajelor de foarte mare nivel.
Limbajele acestei generatii au fost comparate cu limbajele de programare specifice unui domeniu: DSL - Domain-Specific Programming.
In anii `70, se foloseau maximum 72 de coduri pentru datele de intrare, obtinute prin combinarile bitilor unui octet, suportul de informatie cel mai folost fiind cartela perforata.
Daca limbajele generatiei III urmareau codul procedural, in sensul ca limbajul definea functii care urmareau secventa de instructiuni - procedura, stabilind "ce trebuie facut", fara sa puna problema datelor care urmau sa fie prelucrate, limbajele generatiei IV erau non-procedurale.
Aceste limbaje puneau acccent pe "modul de a face" ceea ce trebuia facut, de aici aparand noi concepte care au schimbat viziunea asupra softului.
Au aparut limbaje noi de programare cu o alta viziune asupra ideei de prelucrare a datelor pe care limbajele din generatia III le mostenise.
Limbajul C, primul limbaj de programare sistemic, a fost dezvoltat de Laboratoarele Bell intre anii 1969 ÷ 1973, limbajul Smaltalk - 1972 este limbajul care pune bazele limbajului orientat obiect; limbajul Prolog - 1972, primul limbaj de programare logic.
In aceasta perioada s-au pus si bazele programarii structurate.
Toate limbajele mentionate au dus la aparitia unei multitudini de limbaje care au dezvoltat principiile stabilite in aceasta perioada.
Alte limbaje importante aparute in aceasta perioada: Logo - 1968, Pascal - 1970, C - 1972, Smaltalk - 1972, PROLOG - 1972, SQL - 1978.
In general aceste limbaje se grupeaza, dupa domeniul de folosire, astfel:
-de uz general: FoxPro, Clipper, DataFlex, IBM Rational EGL - Enterprise Generation
Language, Panther, Xbase++,..
-limbaje pentru secvente de baze de date: FOCUS, SQL, Progress 4GL, NATURAL,
Ingres 4GL,..
-generatoare de raport: Quest, RPG-II, LINC, ..
-analiza si manipularea datelor: DASL, IDL, LANSA, IGOR Pro, MATLAB, MARK-IV,
SQR, R, Ramis, .
-creatoare de GUI - Graphical User Interface: eDeveloper, Omnis Studio, OpenROAD, .
Generatia a cincea (5GL) - este compusa din limbaje de programare care se bazeaza pe folosirea unor constrangeri aplicate programelor si nu folosind algoritmi care descriu desfasurarea unui fenomen oarecare.
Scopul noilor limbaje de programare este rezolve problema data fara programator, prin reutilizarea si dezvoltarea celor existente.
Este urmarita si dezvoltarea conceptelor legate de inteligenta artificiala, logica fuzzy si retelele neuronale.
Nu au aparut noutati in limbajele imperative, dar s-au produs recombinari si maturari ale ideilor vechi.
Toate schimbarile realizate au fost orientate obiect: Object-Pascal, Virtual Basic, Java.
Limbajul Java a inceput sa joace un rol important in abordarea programarii sistemelor complexe.
Evolutia limbajelor de programare continua prin aplicarea lor in industrie si cercetare, trendurile curente incluzand:
- mecanisme pentru adaugarea verificarii securitatii si sigurantei limbajelor: control static
extins, control al fluxului de informatii,.
- metaprogramarea: accesul si folosirea sintaxei arborescente abstracte, tip arbore,
- integrarea cu bazele de date,
- dezvoltarea softului prin orientare - component, baza dezvoltarii ingineriei programarii - software engineering.
In 1990, generatia V de limbaje era considerata a fi noul val al dezvoltarii software, trecandu-se de la obiect-soft la component soft si aparand conceptul de ingineria programarii - software engineering.
Se considera ca singurul limbaj care nu va putea fi inlocuit este limbajul cod masina, toate celelalte ar putea fi inlocuite cu limbaje pentru dezvoltarea sistemelor
Instrumentele (tools) de lucru ale acestor limbaje constau din limbaje apartinand generatiei IV, carora li se incorporeaza sisteme de legaturi (wizards) tip RAD - Rational Aplication Development sau CASE - Computer Aided Software Engineering, elemente de asistenta grafica care usureaza construirea codurilor.
Exemple ale limbajelor celei de-a cincea generatie de limbaje: PROLOG, Mercury, ICAD - bazat pe limbajul LISP, C# (pronuntat Si - sharp), F#, Windows Power Shell,..
Chestionar
1. Cum definim limbajul de programare?
2. Care sunt componentele unui limbaj de programare?
3. Ce defineste semantica?
4. Ce a impus crearea de noi limbaje de programare?
5. Care este caracteristica esential limbajelor procedurale?
6. Din ce sunt compuse programele scrise in limbaje neprocedurale?
7. Cum se defineste compilatorul?
8. Ce motiveaza referirea la generatiile de limbaje de programare?