Diferență între revizuiri ale paginii „PC Laborator 9”

De la WikiLabs
Jump to navigationJump to search
 
(Nu s-au afișat 6 versiuni intermediare efectuate de același utilizator)
Linia 11: Linia 11:
 
Funcțiile sunt secțiuni ale unui program care se pot apela de mai multe ori, care pot modifica starea programului (prin modificarea variabilelor sau operații de IO - citire și scriere de date) și care pot întoarce un rezultat.
 
Funcțiile sunt secțiuni ale unui program care se pot apela de mai multe ori, care pot modifica starea programului (prin modificarea variabilelor sau operații de IO - citire și scriere de date) și care pot întoarce un rezultat.
  
== Definirea/ Declararea unei funcții ==
+
== Declarația unei funcții ==
  
 
O funcție în C este <span style="color: black; font-weight: bold">declarată</span> în următorul fel:
 
O funcție în C este <span style="color: black; font-weight: bold">declarată</span> în următorul fel:
Linia 75: Linia 75:
 
<ol>
 
<ol>
 
<li> O funcție nu este accesibilă/ apelabilă până nu este declarată în program.</li>
 
<li> O funcție nu este accesibilă/ apelabilă până nu este declarată în program.</li>
<li> Nu pot exista mai multe funcții cu același prototip.</li>
+
<li> Nu pot exista mai multe funcții cu același nume.</li>
 
<li> O funcție poate avea o singură definiție/implementare și oricâte declarații.</li>
 
<li> O funcție poate avea o singură definiție/implementare și oricâte declarații.</li>
 
<li> O funcție declarată și ne-implementată (ne-definită) poate fi apelată, și fișierul va compila cu succes, dar linker-ul nu va reuși să creeze fișierul executabil. Ca exemplu, încercați scrierea, compilarea și execuția acestui program:
 
<li> O funcție declarată și ne-implementată (ne-definită) poate fi apelată, și fișierul va compila cu succes, dar linker-ul nu va reuși să creeze fișierul executabil. Ca exemplu, încercați scrierea, compilarea și execuția acestui program:
Linia 234: Linia 234:
 
#Scrieți o funcție <code>sortare</code> care primește ca și argumente un întreg sort si un vector v. Funcția modifică elementele vectorului în funcție de întregul sort, astfel numere mai mari ca sort vor fi plasate la începutul vectorului.
 
#Scrieți o funcție <code>sortare</code> care primește ca și argumente un întreg sort si un vector v. Funcția modifică elementele vectorului în funcție de întregul sort, astfel numere mai mari ca sort vor fi plasate la începutul vectorului.
  
 +
<!--
 
= Exerciții Moodle =
 
= Exerciții Moodle =
 
<ol>
 
<ol>
Linia 255: Linia 256:
  
 
     <li>
 
     <li>
<span style="font-size: 1rem;">Scrieți o funcție <code>columnSum</code> care să primească ca argumente dimensiunile unei matrice, valorile ei (numere întregi) și un număr întreg reprezentând o coloană. Funcția trebuie să întoarcă suma numerelor de pe coloana respectivă.&nbsp;<br></span><br>Scrieți apoi funcția <code>main</code> care să citească matricea de la tastatură și numere de coloana apoi să apeleze funcția <code>columnSum</code> și să afișeze suma calculată.<br><br><b>Date de intare<br></b><br>Pe prima linie se află n și m reprezentând numărul de linii și coloane a matricei. Pe următoarele n linii se află m valori întregi separate prin spațiu, reprezentând valorile matricei. Pe ultima linie se află un număr necunoscut de valori întregi reprezentând indecși de coloane, separate prin spațiu.<br><br><b>Date de ieșire<br></b>Pentru fiecare index de coloană de la intrare, se va afișa suma elementelor de pe coloana respectivă. Valorile sunt separate prin spații.<br><br><b style="color: red">Atenție: Valorile de pe ultima linie se vor citi până când funcția scanf/ fscanf întoarce valoarea EOF!</b>
+
<span>Scrieți o funcție <code>columnSum</code> care să primească ca argumente dimensiunile unei matrice, valorile ei (numere întregi) și un număr întreg reprezentând o coloană. Funcția trebuie să întoarcă suma numerelor de pe coloana respectivă.&nbsp;<br></span><br>Scrieți apoi funcția <code>main</code> care să citească matricea de la tastatură și numere de coloana apoi să apeleze funcția <code>columnSum</code> și să afișeze suma calculată.<br><br><b>Date de intare<br></b><br>Pe prima linie se află n și m reprezentând numărul de linii și coloane a matricei. Pe următoarele n linii se află m valori întregi separate prin spațiu, reprezentând valorile matricei. Pe ultima linie se află un număr necunoscut de valori întregi reprezentând indecși de coloane, separate prin spațiu.<br><br><b>Date de ieșire<br></b>Pentru fiecare index de coloană de la intrare, se va afișa suma elementelor de pe coloana respectivă. Valorile sunt separate prin spații.<br><br><b style="color: red">Atenție: Valorile de pe ultima linie se vor citi până când funcția scanf/ fscanf întoarce valoarea EOF!</b>
 
<p>Exemple:</p>
 
<p>Exemple:</p>
 
<pre>
 
<pre>
Linia 278: Linia 279:
 
     </li>
 
     </li>
 
     <li>
 
     <li>
 +
<p></p>Scrieți o funcție <code>isPalindrome</code> care să determine dacă un cuvânt este palindrom. Aceasta va lua ca argument un vector de caractere și va întoarce 1 dacă șirul este palindrom și 0 dacă nu. În funcția <code>main</code> se vor citi un număr necunoscut de cuvinte pentru care trebuie să se afișeze, pe linii succesive, <b>DA</b> sau <b>NU</b> în funcție de rezultatul apelului funcției <code>isPalindrome</code>. Toate cuvintele au maxim 60 de caractere.<br><p></p>
 +
<pre>
 +
Test      Input                          Result
 +
Example 1  car                            NU
 +
Example 2  Here pop the weasel            NU DA NU NU
 +
Example 3  papa milim vuiuv mumu          NU DA DA NU
 +
</pre>
 
     </li>
 
     </li>
 
     <li>
 
     <li>
 +
<p></p><span>Scrieți o funcție <code>dotProduct</code> care să primească ca argumente o lungime n și doi vectori de numere fracționare, ambii de lungime n. Funcția trebuie să întoarcă produsul scalar ai celor doi vectori.<br></span><br>Scrieți apoi funcția <code>main</code> care să citească cei doi vectori de la tastatură, apoi să apeleze funcția <code>dotProduct</code> și să afișeze produsul calculat.<br><br><b>Date de intare<br></b><br>Pe prima linie se află <b>n</b>, reprezentând lungimea vectorilor. Pe a doua și a treia linie se află câte <b>n</b> valori fracționare separate prin spațiu, reprezentând valorile celor doi vectori.&nbsp;<br><br><b>Date de ieșire<br></b>Se afișează rezultatul produsului scalar între cei doi vectori, cu trei zecimale.<br><br><p></p>
 +
<pre>
 +
Test      Input              Result
 +
Example 1  3                  20.000
 +
            1 2 3
 +
            2 3 4
 +
Example 2  2                  11.000
 +
            1 2
 +
            3 4               
 +
Example 3  5                  0.000
 +
            1 0 2 0 3
 +
            0 4 0 5 0
 +
</pre>
 
     </li>
 
     </li>
 
     <li>
 
     <li>
 +
<p></p><span>Scrieți o funcție <code>isPrime</code> care să primească ca argument un număr întreg fără semn. Funcția trebuie să întoarcă 1 dacă numărul este prim, 0 dacă nu.<br></span><br>Scrieți apoi funcția <code>main</code> care să citească de la tastatură un număr <b>n</b> întreg și să afișeze primele <b>n</b> numere prime.<br><br><b>Date de intare<br></b><br>La intare se dă un singur număr <b>n</b>.<br><br><b>Date de ieșire<br></b>La ieșire se vor afișa primele <b>n</b> numere prime, separate prin spațiu.<br><br><br><p></p>
 +
 +
<pre>
 +
Test      Input              Result
 +
Example 1  2                  2 3
 +
Example 2  3                  2 3 5
 +
Example 3  10                2 3 5 7 11 13 17 19 23 29
 +
</pre>
 
     </li>
 
     </li>
 
</ol>
 
</ol>
 +
 +
=== Bonus ===
 +
<p></p><span style="font-size: 1rem;"></span>Scrieți o funcție <code>compareStrings</code> care să compare doi vectori de caractere. Funcția primește doi vectori de caractere și are un rezultat întreg. Funcția ce trebuie realizată întoarce:<ul><li>număr pozitiv dacă primul șir e mai mare</li><li>număr negativ dacă primul sir e mai mic</li><li>zero dacă sunt egale</li></ul><p>Nu se permite utilizarea funcției <code>strcmp</code>.</p><p>Scrieți apoi o funcție <code>main</code> care să citească două șiruri de caractere (cuvinte) și să afișeze <b>MAI MIC</b> dacă primul șir e mai mic decât al doilea, <b>EGAL</b> dacă sunt identice și <b>MAI MARE</b> dacă primul șir este mai mare decât al doilea. Cuvintele au maxim 255 de caractere fiecare.</p><br><p></p>
 +
 +
<pre>
 +
Test      Input              Result
 +
Example 1  andrei bogdan      MAI MIC
 +
Example 2  vasile vasile      EGAL
 +
Example 3  ghita Ghita        MAI MARE
 +
Example 4  ana anamaria      MAI MIC
 +
</pre>
 +
 +
-->

Versiunea curentă din 27 noiembrie 2017 21:45

Obiective

În urma acestui laborator, studentul va fi capabil:

  • să înțeleagă conceptul de funcție;
  • să definească funcții, sub formă de prototip și implementare;
  • să apeleze funcții;
  • să utilizeze corect cuvintele cheie void și return.

Funcții

Funcțiile sunt secțiuni ale unui program care se pot apela de mai multe ori, care pot modifica starea programului (prin modificarea variabilelor sau operații de IO - citire și scriere de date) și care pot întoarce un rezultat.

Declarația unei funcții

O funcție în C este declarată în următorul fel:

tip_returnat nume_functie(lista_tip_argumente);

unde:

  • tip_returnat definește tipul de date din care face parte valoarea returnată de funcție; dacă funcția nu întoarce nici o valoare (este folosit exclusiv pentru modificarea stării programului sau pentru IO), atunci se mai numește și procedură, iar tipul returnat este void;
  • nume_functie reprezintă numele funcției care este folosit ulterior pentru apelul ei, și care respectă aceleași reguli ca orice identificator generic din C: poate conține exclusiv cifre, litere mici și mari, caracterul underscore (_), și nu poate începe cu cifră;
  • lista_tip_argumente reprezintă o listă de tipuri de date, separate prin virgulă, din care fac parte argumentele funcției; această listă poate fi goală (dacă funcția nu are argumente).


O declarație de funcție se numește și prototip.

Exemple de declarații de funcții

float max(float, float);
int inc(int);
void print(char[]);
int getAge();

Implementarea unei funcții

Implementarea (definirea) unei funcții în C se face în felul următor:

tip_returnat nume_functie(lista_argumente) {
    statement_1
    statement_2
    ...
    statement_n
}

unde:

  • tip_returnat reprezintă tipul returnat al funcției (vezi definiția unei funcții);
  • nume_funcție reprezintă numele funcției (vezi definiția unei funcții);
  • lista_argumente reprezintă o listă de definiții de variabile (de forma "tip_data nume_variabilă") care sunt argumentele funcției; această listă poate fi goală;
  • statement_... reprezintă una sau mai multe instructiuni (statement) C care implementează comportamentul funcției și care se execută de fiecare dată când se apelează funcția; corpul unei funcții poate fi gol (poate să nu conțină nici un statement), dar atunci apelul ei nu are nici un efect; dacă funcția nu este de tip void, atunci obligatoriu ultima instrucțiune executată din funcție trebuie să fie return, care setează valoarea întoarsă de funcție.

Exemple de implementări (definiri) de funcții

float max(float a, float b) {
    if (a >= b) return a;
    return b;
}

int inc(int value) {
    return value + 1;
}

void print(char string[]) {
    printf("%s", string);
}
 
int getAge() {
    return 20;
}

Observații

  1. O funcție nu este accesibilă/ apelabilă până nu este declarată în program.
  2. Nu pot exista mai multe funcții cu același nume.
  3. O funcție poate avea o singură definiție/implementare și oricâte declarații.
  4. O funcție declarată și ne-implementată (ne-definită) poate fi apelată, și fișierul va compila cu succes, dar linker-ul nu va reuși să creeze fișierul executabil. Ca exemplu, încercați scrierea, compilarea și execuția acestui program:
    #include <stdio.h>
    
    int inc(int);
    
    int main() {
        int n;
        printf("n = ");
        scanf("%d", &n);
        printf("n + 1 = %d\n", inc(n));
        return 0;
    }
    
  5. Implementarea unei funcții ține loc implicit și de declarație, dacă aceasta nu există deja.
  6. Cuvântul cheie return oprește execuția funcției, indiferent dacă mai există sau nu instrucțiuni după return.
  7. Cuvântul cheie return poate fi folosit și în funcții void, dar urmat doar de punct și virgulă (;) și are ca efect ieșirea imediată din funcție. Ca exemplu, încercați scrierea, compilarea și execuția acestui program:
    #include <stdio.h>
    
    void printIfNotZero(int value) {
        if (value == 0) return;
    
        printf("S-a apelat functia printIfNotZero cu valoarea %d.\n", value);
    }
    
    int main() {
        printIfNotZero(1);
        printIfNotZero(0);
        printIfNotZero(2);
        printIfNotZero(3);
        printIfNotZero(0);
        return 0;
    }
    
  8. În C, argumentele unei funcții sunt "pass-by-value", în sensul că ele conțin valorile variabilelor cu care se apelează funcția, nu se identifică cu aceste variabile. Altfel spus, modificarea unui argument al funcției nu se propagă în afara funcției. Ca exemplu, încercați scrierea, compilarea și execuția acestui program:
    #include <stdio.h>
    
    void inc(int value) {
        printf("In 'inc', la intrare, value este %d\n", value);
        value = value + 1;
        printf("In 'inc', la iesire, value este %d\n", value);
    }
    
    int main() {
        int n = 3;
        printf("Inainte de 'inc', n este egal cu %d\n", n);
        inc(n);
        printf("După 'inc', n este egal cu %d\n", n);
        return 0;
    }
    

Programe din mai multe fișiere

Programele complexe pot fi sparte în mai multe fișiere. În general, prin convenție, într-un fișier se scriu funcții care au legătură între ele, în sensul că sunt folosite pentru aceeași funcționalitate (de exemplu, funcții pentru criptare, sau funcții pentru IO, sau funcții pentru operații matematice, etc.). Pentru că aceste funcții pot teoretic fi folosite în multiple alte funcții din alte fișiere, sistemul cel mai des întâlnit pentru separarea acestor funcții este următorul:

  • prototipurile funcțiilor se scriu într-un fișier cu extensia .h (header), iar acest fișier header este inclus acolo unde aceste funcții sunt apelate; aceste fișiere header trebuie obligatoriu să aibă gardă de dublă incluziune.
  • implementările funcțiilor se scriu într-un fișier sursă .c, care include header-ul de mai sus, și care este compilat separat într-un object file.
  • toate fișierele obiect se link-editează apoi într-un fișier executabil.

Exemplu

  • custom_math.h
    #ifndef CUSTOM_MATH_H
    #define CUSTOM_MATH_H
    
    long long factorial (int);
    
    #endif
    
  • custom_math.c
    #include <custom_math.h>
    long long factorial (int n) {
        long long result = 1;
        int i;
        if (n < 0) return 0;
        for (i = 2; i < n; i++) {
            result *= i;
        }
        return result;
    }
    
  • main.c
    #include <stdio.h>
    #include <custom_math.h>
    int main() {
        int n;
        printf("n = ");
        scanf("%d", &n);
        printf("%d! = %d\n", n, factorial(n));
        return 0;
    }
    

Compilarea unui executabil din mai multe fișiere

Pentru a obține executabilul pentru exemplul de mai sus, se compilează fiecare fișier sursă într-un object file:

gcc -c custom_math.c -o custom_math.o
gcc -c main.c -o main.o

Apoi, se link-editează cele două surse împreună într-un executabil:

gcc custom_math.o main.o -o main

Makefile pentru compilarea unui executabil din mai multe fișiere

C_FILES=custom_math.c main.c
OBJ_FILES=$(C_FILES:.c=.o)

main: $(OBJ_FILES)
	gcc $^ -o $@

%.o: %.c
	gcc -c $^ -o $@

.PHONY: clear
clear:
	rm -rf main $(OBJ_FILES)

Observații:

  1. C_FILES definește o variabilă care conține o listă cu fișierele sursă ce se doresc compilate.
  2. OBJ_FILES reprezintă tot lista cu fișierele sursă, dar în care extensia .c se transformă în .o (astfel, se obține lista cu fișierele obiect necesare pentru compilare).
  3. Executabilul main depinde de toate fișierele obiect și se generează prin apelul lui gcc.
  4. Regula %.o: %.c este o regulă template care spune că dacă este necesară generarea unui fișier obiect, acesta depinde de un fișier cu același nume dar cu extensia .c, și se generează folosind tot un apel la gcc.
  5. Pentru a compila și alte surse în acest Makefile, tot ce este necesar este să le adăugați la lista C_FILES.

Exerciții

  1. Scrieți un program care să implementeze trei funcții:
    • readTempCelsius care să citească și să întoarcă un număr în virgulă reprezentând o temperatură în grade Celsius;
    • celsius2fahrenheit care să convertească un argument din grade Celsius în grade Fahrenheit;
    • main care să folosească celelalte două funcții pentru a converti o temperatură citită, din grade Celsius în Fahrenheit.
  2. Scrieți o funcție care să calculeze determinantul unei matrici de 3x3 sau mai simplu de 2x2. Testați funcția integrând-o într-un program.
  3. Scrieți o funcție care să primească ca argumente o matrice de numere întregi, dimensiunile ei și un număr întreg reprezentând o coloană, și să întoarcă suma numerelor de pe coloana respectivă. Funcția va afișa un mesaj și va întoarce 0 daca coloana nu există în matrice. Testați funcția integrând-o într-un program.
  4. Scrieți o funcție care să determine dacă un cuvânt este palindrom. Scrieți apoi un program care să citească cuvinte de la tastatură până când cuvântul introdus are lungimea 0. Pentru fiecare cuvânt introdus se va specifica dacă este sau nu palindrom.
  5. Scrieți o funcție care să compare doi vectori de caractere. Funcția primește doi vectori de caractere și are un rezultat întreg. HINT: asemenea funcției strcmp. Funcția ce trebuie realizată întoarce:
    • număr pozitiv dacă primul șir e mai mare
    • număr negativ dacă primul sir e mai mic
    • zero dacă sunt egale
  6. Scrieți o funcție sortare care primește ca și argumente un întreg sort si un vector v. Funcția modifică elementele vectorului în funcție de întregul sort, astfel numere mai mari ca sort vor fi plasate la începutul vectorului.