Aceasta e o versiune anterioară a paginii.
Laboratorul de Structuri de Date și Algoritmi are drept scop aprofundarea conceptelor prezentate la curs. Un laborator va prezenta un anumit set de concepte și va conține următoarele activități:
Pentru o desfășurare cât mai bună a laboratorului și pentru o înțelegere cât mai bună a conceptelor vă recomandăm să parcurgeți conținutul laboratorului de acasă.
În cadrul acestui laborator vom lucra pe platforma Linux, mai precis vom lucra într-o mașină virtuală în care este instalat Lubuntu. Lubuntu este o distribuție de Linux care are la bază Ubuntu dar față de aceasta este mai lightweight.
Pe stațiile de lucru din laborator a fost instalat VMware Player și adăugată mașina virtuală cu numele SDA-AB. Porniți mașina virtuală.
Credențialele sunt următoarele:
Pentru a putea lucra de acasă la rezolvarea temelor, în cazul în care nu doriți să aveți instalată o distribuție de Linux, puteți găsi mașina virtuală de la laborator de aici Descărcați fișierul SDA-AB_VM.zip și dezarhivați-l. Fișierul are dimensiunea de 2.3GB (5.4GB dezarhivat).
Pentru a putea deschide mașina virtuală avem nevoie de VMware Workstation Player. Aceste este gratuit și poate fi descărcat de aici. Alegeți versiunea corespunzătoare sistemului de operare folosit.
După dezarhivarea mașinii virtuale și instalarea VMware Player, deschideți VMware Player → File → Open a Virtual Machine → Mergeți in directorul unde ați salvat mașina virtuală, SDA-AB, și selectați fișierul SDA-AB.vmx după care porniți mașina.
În mașina virtulă este deja instalat toolchain-ul GNU. Acesta este o suită de tool-uri necesare dezvoltării programelor folosind C/C++ și conține:
Pentru exemplificare vom folosi un program simplu care tipărește la ieșirea standard (stdout) un șir de caractere.
#include <stdio.h> int main() { printf("SDA - Hello, World!\n"); return 0; }
GCC folosește pentru compilarea de programare C/C++ comanda gcc
, respectiv comanda g++
.
student@sda-ab-vm:~/Documents$ ls hello.c student@sda-ab-vm:~/Documents$ gcc hello.c student@sda-ab-vm:~/Documents$ ls a.out hello.c student@sda-ab-vm:~/Documents$ ./a.out SDA - Hello, World!
Prin urmare, comanda gcc hello.c
a fost folosită pentru compilarea fișierului sursă 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
.
student@sda-ab-vm:~/Documents$ gcc hello.c -o hello student@sda-ab-vm:~/Documents$ ls hello hello.c student@sda-ab-vm:~/Documents$ ./hello SDA - Hello, World!
Observăm că de data aceasta fișierul executabil rezultat se numește hello
.
În mod similar se poate folosi g++
pentru compilarea unui program sursă C++.
De cele mai multe ori nu vom lucra cu un singur fișier sursă ci vom încerca să modularizăm codul, spărgându-l în mai multe fișiere sursă, fiecare implementând o anumită funcționalitate.
Fișierele header .h
nu implementează funcții ci conține doar definiții de funcții. Implementarea efectivă a acestora se realizează în fișierul asociat .c
pentru C, sau .cpp
pentru C++.
Să considerăm următorul scenariu: dorim să realizăm un modul care implementează funcțiile de adunare și inmulțire.
Creăm fișierul header cu numele my_math.h
.
#ifndef MY_MATH_H #define MY_MATH_H int sum(int a, int b); int multiply(int a, int b); #endif
Prin folosirea #ifndef MY_MATH_H
… #endif
ne asigurăm că acest fișier header nu este inclus de mai multe ori în același fișier sursă. Cu alte cuvinte, în cazul în care simbolul MY_MATH_H
este deja definit de la prima includere, a doua operație de includere nu va avea niciun efect. Această tehnică se numește Include guard și folosește directivele de preprocesoare #ifndef
, #define
, #endif
.
Apoi, creăm fișierul sursa cu numele my_math.c
.
#include "my_math.h" int sum(int a, int b) { return a + b; } int multiply(int a, int b) { return a * b; }
Creăm și un fișier cu numele main.c
în care vom utiliza funcțiile implementate de modulul nostru.
#include <stdio.h> #include "my_math.h" int main() { int a = 10; int b = 20; printf("a + b = %d\n", sum(a, b)); printf("a * b = %d\n", multiply(a, b)); return 0; }
Observăm modul în care am inclus header-ul my_math.h față de header-ul stdio.h. Atunci când ne dorim să includem un header din biblioteca standard folosim #include < ... .h>
, iar atunci când dorim să includem un header creat de noi folosim #include ".... .h>"
.
Programul nostru are acum mai multe fișiere sursă .c
. Compilarea din mai multe astfel de fișiere se face prin specificarea tuturor fișierelor sursă .c
ca argumente utilitarului gcc
.
student@sda-ab-vm:~/Documents$ ls main.c my_math.c my_math.h student@sda-ab-vm:~/Documents$ gcc main.c my_math.c -o main student@sda-ab-vm:~/Documents$ ls main main.c my_math.c my_math.h student@sda-ab-vm:~/Documents$ ./main a + b = 30 a * b = 200
.h
nu trebuie date ca argumente utilitarului gcc
. În etapa de compilare conținutul unui fișier .h
este copiat în interiorul fișierului sursă care îl include.
Make este un utilitar din GNU Toolchain care permite automatizarea și eficientizarea sarcinilor. În mod particular este folosit pentru automatizarea compilării programelor. În cazul în care avem un proiect care are un număr foarte mare de fișiere sursă, compilarea întregului proiect de la zero poate dura foarte mult. Prin folosirea utilitarului make putem automatiza compilarea fiecărui fișier separat .c
într-un fișier obiect (binar) .o
, apoi unirea tuturor fișierelor obiect într-un singur fișier executabil. La o modificare se va recompila doar fișierul sursă modificat, rezultând un nou fișier obiect .o
. Astfel, procesul de compilare este mult mai rapid.