This is an old revision of the document!
Pe unele sisteme este posibil ca aceste pachete necesare să lipsească:
build-essential
manpages-dev
În cazul în care nu sunt instalate implicit pe sistemul vostru, pe un sistem Ubuntu se poate folosi comanda
student@sde:~$ sudo apt-get install build-essential manpages-dev
Pentru demo deschidem un terminal (folosim combinația de taste Alt+Ctrl+t
) și clonăm repository-ului oficial sde.
/home/student
Folosim comanda:
student@sde:~$ git clone https://github.com/upb-fils/sde
git pull
În directorul /home/student/sde/tp02
găsim fișierele necesare pentru laboratorul 2.
În cele ce urmează, vom consideră ca verbul a compila înseamnă a obține dintr-unul sau mai multe fișiere sursă un fișier executabil.
Mergem în directorul /home/student/sde/tp02/simple-gcc
unde găsim fișierul simple_hello.c
.
student@sde$ pwd /home/student/ student@sde$ cd sde/tp02/simple-gcc student@sde$ ls Makefile hello.c simple_hello.c utils.h errors.c help.c utils.c warnings.c student@sde$ gcc simple_hello.c student@sde$ ls Makefile errors.c help.c utils.c warnings.c a.out hello.c simple_hello.c utils.h student@sde$ ./a.out Hello world!
Anterior am folosit comanda gcc
căreia i-am dat un singur parametru ca intrare. A generat un binar numit a.out
. Mai jos putem vedea cum obținem un nume custom pentru binarul rezulat în urma comiplării fișierului simple_hello.c
.
student@sde$ ls Makefile errors.c help.c utils.c warnings.c a.out hello.c simple_hello.c utils.h student@sde$ gcc simple_hello.c -o hello student@sde$ ls Makefile errors.c hello.c simple_hello.c utils.h a.out hello help.c utils.c warnings.c student@sde$ ./hello Hello world! student@sde$ pwd /home/student/sde/tp02/simple-gcc/
Observați că anterior am folosit o cale relativă la directorul curent pentru a executa fișierul hello
, prin apelul ./hello
. Puteam folosi și o cale absolută: /home/student/sde/tp02/simple-gcc/hello
pentru executare.
.
(punct, dot): directorul curent.
Pentru a vedea ce tip de fișier binar/executabil este hello
, putem folosi comanda file
:
student@sde$ file ./hello hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=0x92a8cd0efe5b5dd5587b5965d8bc6fa27fac32af, not stripped
Comanda gcc simple_hello.c
a fost folosită pentru compilarea fișierului sursă simple_hello.c
. Rezultatul a fost obținerea fișierului executabil a.out
(nume implicit utilizat de gcc
). Dacă se dorește obținerea unui executabil cu un alt nume se poate folosi opțiunea -o
.
În mod similar se folosește g++
pentru compilarea unui program sursă C++.
Pentru compilarea de programe C, respectiv C++ folosim în linie de comandă compilatoarele gcc
, respectiv g++
. O invocare tipică este pentru compilarea unui program dintr-un singur fișier sursă, în cazul nostru simple_hello.c
.
Pornim de la programul simplu din fișierul simple_hello.c
pe care îl găsim în directorul ~/sde/tp02/simple-gcc
care tipărește la ieșirea standard un șir de caractere:
#include <stdio.h> int main(void) { printf("Hello world!\n"); return 0; }
Formatul general al unei comenzi de compilare cu gcc
:
gcc fisiere.c -o nume_executabil COMPILING_FLAGS LINK_BIBLIOTECI
COMPILING_FLAGS
sunt opțiuni ale gcc (precum -g
, -w
, -Wall
)LINK_BIBLIOTECI
ține de opțiuni precum -lm
sau -L
.
-o
trebuie să se găsească numele fișierului de ieșire. Acesta suprascrie fișierele pe care le primește ca argument.
Comanda poate fi, la fel de bine, structurată și astfel:
gcc COMPILING_FLAGS -o nume_executabil fisiere.c LINK_BIBLIOTECI
-o
nu punem fișiere sursă sau alte fișiere. Dacă vom face asta, fișierele vor fi suprascrise și vom pierde conținutul acestora.
Exemple concrete:
Intrați în directorul ~/sde/tp02/simple-gcc
gcc simple_hello.c -o simple_hello
gcc -Wall simple_hello.c -o simple_hello
gcc -Wall simple_hello.c -o math_hello -lm
gcc -Wall hello.c utils.c help.c -o hello
Paginile de ajutor ale GCC (man gcc
, info gcc
) oferă o listă cu toate opțiunile posibile ale GCC.
gcc -c simple_hello.c -o hello-obj.o
La acest pas, obținem programul în limbaj cod mașină
, cod care nu mai poate fi înțeles deloc de către oameni, fiind practic doar șiruri de biți 1/0. Acest tip de cod generat mai este cunoscut și sub numele de cod obiect
și poate fi executat direct de către procesor.
Fișierul intermediar produs are extensia .o
.
gcc hello-obj.o -o hello
Odată ce fișierul obiect este generat în etapa de asamblare, toate simbolurile (funcții, variabile globale etc) trebuie rezolvate
, adică, de exemplu pentru funcții, trebuie găsită implementarea efectivă (corpul funcției) care se poate afla în alt fișier obiect sau într-o bibliotecă a sistemului. De exemplu atunci când scriem printf(…)
, pentru a afișa un șir de caractere pe ecran, codul aferent funcției de bibliotecă printf()
trebuie efectiv copiat în programul nostru sau măcar făcută o legătură către el (de aici și numele de editare de legături). De asemenea, dacă programul nostru constă din mai multe fișiere .c
, fiecare dintre ele va produce câte un fișier obiect separat, dar în final noi vom obține un singur fișier și anume programul executabil. De legătura dintre fișierele obiect mai sus menționate se ocupă, bineînțeles, linkerul.
Se face apelând din Bash (Terminal) fișierul executabil, folosind calea către acesta:
student@sde$: pwd /home/student/sde/tp02/simple-gcc/ student@sde$: ./hello
student@sde$: /home/student/sde/tp02/simple-gcc/hello
Exemplu: Putem rula ls
folosind binarul din sistemul Linux. Încercați să executați comanda /bin/ls
Intrăm în directorul ~/sde/tp02/simple-gcc
Separăm compilarea fișierului help.c
de link-editare pentru a obține fișierului obiect help.o
. Pentru aceasta vom folosi comanda:
student@sde$: gcc -c help.c student@sde$: ls errors.c hello.c help.c help.o Makefile utils.c utils.h warnings.c simple_hello.c
Observați crearea fișierului help.o
. Procedăm similar pentru utils.c
și hello.c
:
student@sde$: gcc -c utils.c student@sde$: gcc -c hello.c student@sde$: ls errors.c hello.c hello.o help.c help.o Makefile simple_hello.c utils.c utils.h utils.o warnings.c
Pentru a obține fișierul binar hello
, care execută codul din corpul funcției main
al fișierului sursă hello.c
este nevoie să punem cap la cap toate cele 3 fișiere obiect, să le link-edităm:
student@sde$: gcc utils.o help.o hello.o -o hello student@sde$: ls errors.c hello hello.c hello.o help.c help.o Makefile simple_hello.c utils.c utils.h utils.o warnings.c student@sde$: file hello hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=54b686de038a29fed397f604403961f9d1086f76, not stripped student@sde$: ./hello 30 craaaaap
Make este un utilitar care permite automatizarea și eficientizarea sarcinilor. În mod particular este folosit pentru automatizarea compilării programelor. După cum s-a precizat, pentru obținerea unui executabil provenind din mai multe surse este ineficientă compilarea de fiecare dată a fiecărui fișier și apoi link-editarea. Se compilează fiecare fișier separat, iar la o modificare se va recompila doar fișierul modificat.
Utilitarul make folosește un fișier de configurare denumit Makefile
. Un astfel de fișier conține reguli și comenzi de automatizare.
|
student@sde$ make gcc -Wall hello.c -o hello student@sde$ ./hello Hello, World! |
student@sde$ make clean rm -f hello student@sde$ make all gcc -Wall hello.c -o hello |
Makefile
. Liniile care conțin comenzi de compilare sunt indentate folosind TAB
, nu space
.
main.o: main.c < TAB >gcc -Wall -c main.c
Exemplul prezentat mai sus conține două reguli: all
și clean
. La rularea comenzii make
se execută prima regulă din Makefile (în cazul de față all
, nu contează în mod special denumirea). Comanda executată este gcc -Wall hello.c -o hello
. Se poate preciza explicit ce regulă să se execute prin transmiterea ca argument comenzii make
. (comanda make clean
pentru a șterge executabilul hello
și comanda make all
pentru a obține din nou acel executabil).
Intrați în directorul ~/sde/tp02/simple-gcc
. Amintiți-vă cele 4 comenzi gcc pe care le-am dat pentru a obține 3 fișiere obiect din sursele utils.c
, hello.c
și help.c
și cea de a patra comanda pentru a link-edita cele 3 obiecte spre obținerea binarului.
Putem automatiza toți acești pași putem folosi fișierul Makefile
:
student@sde$: make gcc -c utils.c gcc -c hello.c gcc -c help.c gcc utils.o help.o hello.o -o hello student@sde$: ls errors.c hello hello.c hello.o help.c help.o Makefile simple_hello.c utils.c utils.h utils.o warnings.c student@sde$: file hello hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=54b686de038a29fed397f604403961f9d1086f76, not stripped student@sde$: ./hello 30 craaaaap
Urmăriți dependențele între reguli din fișierul ~/sde/tp02/simple-gcc/Makefile
:
build: utils.o hello.o help.o gcc utils.o help.o hello.o -o hello all: gcc simple_hello.c -o simple utils.o: utils.c gcc -c utils.c hello.o: hello.c gcc -c hello.c help.o: help.c gcc -c help.c clean: rm -f *.o hello
Alocarea de memorie se face folosind functiile malloc
, calloc
si realloca
. Toate aceste functii intorc in felul urmator:
Functiile de alocare sunt urmatoarele:
// TODO add functions here
In cazul in care apare o eroare, acesta poate fi afisata folosind functie perror
. Acesta functie primeste un singur parametru, un text care este afisat inaintea mesajului de eroare. De exemplu, daca o alocare nu reuseste, putem afla de ce in felul urmator:
int main () { int *s = (int*)malloc (sizeof (int)); if (s != NULL) { // execute some code free (s); } else { perror ("malloc"); abort (); } return 0; }
Dupa ce memoria alocata nu mai este necesara, acesta trebuie dealocata folosind functia free
. Aceasta primeste un parametru o adresa de memorie intoarsa in prealabil de malloc
sau o functie similara.
Mai multe exemple puteti gasi la Pointeri. Abordarea lucrului cu tablouri folosind pointeri. si Alocarea dinamică a memoriei. Aplicaţii folosind tablouri şi matrice..
In limbajul C textele (string) sunt memorate in siruri de tip char
. Sirul are doua propietati importante:
Lungimea maxima a sirului este stabilita la alocare, in doua feluri:
char text[100]; // 100 characters // or char *text = (char*)malloc (100*sizeof(text));
Lungimea textului se tabileste dinamic dupa urmatoarea conventie: dupa toate caracterele utile din text se pune un caracter cu codul 0.
todo poza
Functiile utile pentru texte sunt:
// TODO add string functions
Observati n-ul. Prima categorie se bazeaza pe i deea ca orice text va avea sigur caracterul 0 in capat. Daca acesta nu este prezent, functionarea nu este corecta si poate duce la probleme de securitate. Functiile care incep cu strn… mai au un parametru suplimentar, o dimensiune maxima a text-ului. Astfel functiile se vor opri fie la caracterul 0 fie dupa maxim n caractere.
Acestea din urma sunt considerate sigure.
Mai multe exemple puteti gasi la Prelucrarea şirurilor de caractere. Funcţii. Aplicaţii..
/home/student
. Dacă nu ați clonat deja la secțiunea demo repository-ului oficial sde, atunci clonați-l acum folosind coamnda:
student@sde:~$ git clone https://github.com/upb-fils/sde
Daca ati clonat deja directrul, intrati in el si rulati:
git pull
În directorul ~/sde/tp02
găsiți fișierele necesare pentru rezolvarea acestui laborator.
Pentru acest laborator putem porni la lucru cu mașina cu interfață grafică, aceasta se află în /mnt/unfrozen
. Drept urmare importăm fișierul /mnt/unfrozen/sde_2016_2017_gnome.ova
Pentru a rezolva următoarea serie de exerciții mergeți în directorul /home/student/sde/tp02/simple-gcc
. Pentru aceasta
folosim următoarea comandă cd ~/sde/tp02/simple-gcc
. În directorul simple-gcc
găsiți fișierul sursă simple_hello.c
. Compilați-l, folosind gcc
, într-un fișier executabil denumit hello
. Pentru aceasta folosim următoarea comandă:
student@sde$ gcc simple_hello.c -o hello
Rulați executabilul proaspăt obținut.
student@sde$ ./hello Hello, World
Repetați procesul de mai sus, dar de data aceasta obțineți un executabil cu numele salut
. Acum îl rulăm și observăm că am obținut exact același lucru ca mai sus. Cele două fișiere par identice, dar ca să ne asigurăm folosim comanda cmp
.
cmp
folosind comanda:
man cmp
Dacă în urma rulării comenzii cmp
cu parametrii corespunzător nu se va afișa nimic înseamnă că cele două fișiere sunt identice.
Observăm că cele două fișiere sunt într-adevăr identice, acest lucru datorându-se faptului că procesul de compilare este unul determinist (o bucată de cod sursă C se va traduce mereu în exact aceleași instrucțiuni în limbaj de asamblare și apoi în cod mașină - dacă se păstrează același grad de optimizare).
În același director ca mai sus, /home/student/sde/tp02/simple-gcc
, găsim fișierul warnings.c
. Compilați-l folosind următoarea comandă:
student@sde$ gcc warnings.c -o warnings student@sde$ ./warnings a + b = 5
Observăm că fișierul a fost compilat și rulat cu succes. Repetați comanda de mai sus, dar folosiți de această dată flagul -Wall
pentru comanda gcc
, ca mai jos:
student@sde$ gcc -Wall warnings.c -o warnings warnings.c: In function ‘main’: warnings.c:8: warning: unused variable ‘c’
Vedem totuși că de data aceasta a fost identificată o problemă cu fișierul warnings.c
și anume că variabila c
a fost declarată, inițializată, dar nefolosită. Acest fapt nu afectează comportamentul programului nostru, dar, în general, e bine să le evităm pe cât posibil.
Inspectați fișierul sursă warnings.c
și corectați warningul. Rulați din nou comanda gcc -Wall warnings.c -o warnings
până când nu mai primiți niciun warning la compilare.
Intrați în directorul /home/student/sde/tp02/tema-pc
. Dorim să compilăm tema la programare folosind fișierul Makefile
. Rulați comanda make
.
Rulați încă o dată comanda make
. S-a mai executat vreo comandă?
Schimbați valoarea macro-ului MIN_VAL
în fișierul utils.h
. Rulați încă o dată comanda make
. De ce nu se actualizează fișierul executabil? Modificați fișierul Makefile
pentru ca obținerea fișierelor obiect (cu extensia .o
) să țină cont și de fișierele header (cu extensia .h
) de care acestea depind.
În același director ca mai sus, /home/student/sde/tp02/simple-gcc
, găsim fișierul errors.c
. Compilați-l folosind următoarea comandă:
student@sde$ gcc -Wall errors.c -o errors errors.c: In function ‘main’: errors.c:7: error: expected ‘;’ before ‘return’ student@sde$ ls errors ls: cannot access errors: No such file or directory
Observăm că de această dată nu a mai fost obținut niciun fișier executabil, întrucât în fișierul errors.c
au fost detectate erori de sintaxă. Cea identificată în cazul nostru este la linia 7 și anume că înaintea instrucțiunii return
lipsește caracterul ;
. Modificați fișierul errors.c
astfel încât acesta să fie compilat și rulat cu succes.
Mergeți în directorul /home/student/sde/tp02/tema-pc
, unde găsim 4 fișiere. Pentru acest exercițiu ignorați fișierul Makefile
, ne interesează numai tema.c
, utils.c
și utils.h
.
Inspectăm fișierul tema.c
folosind un editor sau comanda cat tema.c
și vedem că folosește două funcții (vect_gt
și vect_lt
) care nu apar definite nicăieri. Observăm totuși că este inclus fișierul utils.h
, iar dacă ne uităm în acesta vom vedea că cele două funcții sunt declarate totuși acolo.
Încercăm să compilăm fișierul tema.c
așa cum am învățat până acum:
student@sde$ gcc tema.c /tmp/cc7oBohY.o: In function `main': tema.c:(.text+0xac): undefined reference to `vect_gt' tema.c:(.text+0xdb): undefined reference to `vect_lt' collect2: ld returned 1 exit status
Din păcate, procesul de compilare eșuează în etapa de linking, chiar dacă noi avem cele două funcții declarate în fișierul utils.h
, iar acesta este inclus în fișierul principal tema.c
.
Compilați fișierul tema.c
numai până la codul obiect (până la etapa de linkare), folosind flagul -c
. Comanda necesară pentru acest lucru este:
student@sde$ gcc -Wall -c tema.c
Repetați același lucru pentru fișierul utils.c
și obțineți fișierul obiect utils.o
. Putem obține acum fără probleme executabilul nostru, dacă linkăm cele două fișiere obiect. Facem asta cu comanda:
student@sde$ gcc tema.o utils.o -o tema student@sde$ ./tema Values: 10 -20 30 9 7 8 11 5 -2 100 Values greater than 5: 7 Values less than 3: 2
student@sde$ gcc -Wall tema.c utils.c -o tema
Deschideți în editorul preferat fișierul tema.c
și realizați o modificare minoră, de exemplu modificați valoarea lui N
din 9 în 5. Compilați din nou fișierul tema.c
pentru a obține fișierul obiect tema.o
. La fel ca mai sus, linkați fișierele obiect tema.o
și utils.o
pentru a obține executabilul tema
, după care rulați-l. Observăm că nu a mai fost nevoie de recompilarea fișierului utils.c
, am folosit fișierul obiect obținut anterior.
In directorul /home/student/sde/tp02/text
gasiti 2 fisiere.
Modificati fisierul text.c
astfel incat:
1. sa va memorati numele in variabila name
si sa afisati numele memorat in variabila (folosind printf)
2. sa va memorati numele in variabila namePointer
, sa afisati numele si sa eliberati memoria folosita
3. sa va memorati numele in variabila namePointer
(folosind alta metoda decat cea de la punctul 2), sa afisati numele si sa eliberati memoria folosita
Adaugati codul sursa necesar in text-sort.c
pentru a memora 5 texte si a le afisa in ordine.
Mergeți în directorul /home/student/sde/tp02/phone
, unde găsim 3 fișiere.
Adaugati codul necesar in fisierul phone.c
pentru:
1. a aloca o structura de tip Phone
.
2. a introduce date in structure (folositi datele telefonului vostru)
3. a afisa structura pe ecran
4. a elibera memoria folosita
Adaugati codul necesar in fisierul phone-pointer.c
pentru:
1. a aloca o structura de tip Phone
.
2. a introduce date in structure (folositi datele telefonului vostru)
3. a afisa structura pe ecran
4. a elibera memoria folosita
Adaugati codul necesar in fisierul phone-array.c
pentru:
1. a aloca un sir de 5 structuri de tip Phone
.
2. a introduce date in sirul de structuri (folositi datele telefonului vostru si al colegilor)
3. a afisa structurile pe ecran
4. a elibera memoria folosita de structuri
E bine ca atunci când scriem cod să fie cât mai organizat, aerisit, cât mai ușor de înțeles de către altcineva. În directorul /home/student/sde/tp02/ugly
găsiți fișierul ugly.c
care este scris intenționat într-un mod foarte confuz și alambicat. Citiți articolul de la acest link după care modificați fișierul ugly.c
conform principiilor prezentate, păstrând însă exact aceeași funcționalitate.