Differences

This shows you the differences between two versions of the page.

Link to this comparison view

cpl:labs:07 [2016/11/14 23:27]
bogdan.nitulescu
cpl:labs:07 [2016/11/15 02:35] (current)
bogdan.nitulescu [Prototipuri de funcții]
Line 9: Line 9:
   * apelul prin valoare-rezultat   * apelul prin valoare-rezultat
   * apelul prin nume.   * apelul prin nume.
-<​note>​ Folosim termenul de argument sau **argument actual** pentru a ne referi la valoarea sau variabila ​care se pasează unei rutine și termenul de parametru sau **parametru formal** pentru a referi variabila căreia îi este asociată în rutina apelată </​note>​+<​note>​ Folosim termenul de argument sau **argument actual** pentru a ne referi la expresia ​care este trimisă unei rutine și termenul de parametru sau **parametru formal** pentru a referi variabila căreia ​această expresie ​îi este asociată în rutina apelată </​note>​
 ==== Apelul prin valoare ==== ==== Apelul prin valoare ====
-Trimite un argument ​punând la dispoziția procedurii apelate ​valoarea lui care se asociază parametrului formal corespunzător. În timpul execuției procedurii apelate, nu există nici o interacțiune cu variabilele apelantului. Excepția apare desigur în cazul în care argumentul este un pointer și procedura apelata ar putea folosi valoarea pointerului pentru a modifica valorile către care indică. Apelul prin valoare se implementează de obicei prin copierea valorii fiecărui argument ​în parametrul corespunzător ​la intrarea în procedura apelată. Aceasta modalitate poate fi foarte eficientă în cazul argumentelor care pot fi ținute în regiștri, dar foarte ineficientă pentru vectori deoarece presupune schimb de date intens cu memoria.+Fiecare ​argument ​este evaluat și valoarea lui este pusă la dispoziție procedurii apelate în parametrul ​formal ​corespunzător. ​
  
-Un exemplu de limbaj ​în care se folosește apelul ​prin valoare ​este C, în care de faptacesta ​este singurul mecanism de pasare a parametrilor. ​Dar un parametru poate fi și adresa unui obiect ​și se obține efectul apelului prin referință. +În timpul execuției procedurii, nu există nicio interacțiune cu variabilele apelantului. Excepția apare în cazul în care argumentul este un pointer ​și procedura apelată ar putea folosi valoarea pointerului pentru a modifica valorile către care indică acesta.  
-În limbaje precum Javaargumentele ​de tipuri primare ​sunt trimise astfelDe asemeneape obiecte ​de tip IntegerFloatetc, se realizează unboxing înainte de trimiteredeci efectul va fi același.+ 
 +Apelul ​prin valoare ​se implementează de obicei prin copierea valorii fiecărui argument ​în parametrul corespunzător la intrarea în procedura apelată. Aceasta modalitate poate fi foarte eficientă în cazul argumentelor ​care pot fi ținute în regiștri, dar foarte ineficientă pentru tipuri ​de date compuse - vectoristructuri, obiecte - deoarece presupune un transfer de date semnificativ în memorie. 
 + 
 +Apelul prin valoare ​este singurul mecanism de pasare a parametrilor ​în limbajul CUn parametru poate fi însă ​și adresa unui obiectse obține ​astfel ​efectul apelului prin referință. 
 + 
 +În C++la transferul unui obiect prin valoare se va crea o copie a acelui obiect, si se va apela constructorul ​de copiere, daca acesta exista. 
 + 
 +<​code>​class C; 
 +void f(C a);            // parametrul a se transmite prin valoare 
 +void g() { C x; f(x); } // se apeleaza constructorul C::C(const C&) ce copiaza x in a 
 +</​code>​ 
 + 
 +==== Apelul prin referință ==== 
 +Realizează o asociere între argumentul actual și parametrul corespunzator. Procedura apelată va avea acces total la argument pe toată perioada execuției sale, putând să schimbe parametrul actual sau să îl transmită mai departe altor proceduri.  
 + 
 +Ca implementare tipică, la intrarea în procedura apelată se determină adresa argumentului și aceasta se pune la dispoziția procedurii ca mijloc de accesare a argumentului. Acest mecanism este foarte eficient când vectori ​sunt transmiși ca parametri, dar poate fi ineficient în cazul parametrilor de mărime mică care ar putea fi transmiși prin regiștri 
 + 
 +Apelul prin referință poate cauza un //alias// - o situație în care aceeași locație de memorie poate fi accesată folosind două nume simbolice diferite. Un exemplu (C#) : 
 +<code c">​void f(ref int iref int j) { i++; j++; } 
 +void g() { int x = 1; f(x,x); /* x == 3 */ }</​code>​ 
 + 
 +Pot apărea probleme în cazul în care o constantă este transmisă prin referință;​ procedura apelată ar putea modifica acea constantă, ceea ce este ilegal. Soluția uzuală este copierea constantelor în noi locații ​de memorie și transmiterea acestor adrese când se face apelul prin referință.  
 + 
 +Apelul prin referința este implementat implicit în Fortransau explicit via o sintaxă specială în C++C# sau PHP. Același efect se poate obține în C și C++ prin transmiterea adresei ca valoare. 
 + 
 +<​note>​ 
 +În JavaJavaScript sau Python, parametrii sunt transmiși prin valoare, la fel ca și în C. Acest lucru este valabil atât pentru primitive cât și pentru obiecte. 
 + 
 +Afirmația **obiectele ​se transmit prin referință** este greșită. Deoarece variabilele din Java conțin referințe către obiecte și nu obiectele în sinecorect este **referința obiectelor este transmisă prin valoare**. 
 +</​note>​ 
 + 
 +Codul urmator C++ folosește apelul prin referință pentru a interschimba valorile variabilelor a și bCodul similar Java, însă, folosește apelul prin valoare și lasă variabilele neschimbate. 
 + 
 +**C++** 
 +<code c++>void swap(Obj &a, Obj &b) { Obj t=a; a=b; b=t; } 
 +void f() { Obj a,b; swap(a,b); } </​code>​ 
 + 
 +**Java** 
 +<code java>​void swap(Obj a, Obj b) { Obj t=a; a=b; b=t; } 
 +void f() { Obj a,b; swap(a,b); } </​code>​
  
 ==== Apelul prin rezultat ==== ==== Apelul prin rezultat ====
-Este similar celui prin valoare, cu deosebirea că aici se întorc valorile de la apelat la apelant. La intrarea în procedura apelată nu se întamplă nimic, iar la întoarcere valoarea unui parametru apelat prin rezultat este pusă la dispoziția apelantului prin copierea valorii lui în argumentul asociat. Acest tip de apel apare în cazul parametrilor de tip out din Ada+Este similar celui prin valoare, cu deosebirea că aici se întorc valorile de la apelat la apelant. La intrarea în procedura apelată nu se întamplă nimic, iar la întoarcere valoarea unui parametru apelat prin rezultat este pusă la dispoziția apelantului prin copierea valorii lui în argumentul asociat. Acest tip de apel apare în cazul parametrilor de tip out din Ada sau C#:
-Exemplu in C#:+
 <code c"> <code c">
 void a(out int result) void a(out int result)
Line 29: Line 67:
  
 ==== Apelul prin valoare-rezultat ==== ==== Apelul prin valoare-rezultat ====
-Este caracterizat de reuniunea proprietăților celor două tipuri de apel: prin valoare și prin rezultat. La intrarea în procedura apelată valoarea argumentului se copiază în parametru, iar la ieșire valoarea parametrului se copiază înapoi în argument. Este implementat în Ada pentru parametri de tip inout.+Este caracterizat de reuniunea proprietăților celor două tipuri de apel: prin valoare și prin rezultat. La intrarea în procedura apelată valoarea argumentului se copiază în parametru, iar la ieșire valoarea parametrului se copiază înapoi în argument. Este implementat în Fortran sau Ada pentru parametri de tip inout, și apare și în cazul apelurilor de procedură pe un alt computer (//RPC - remote procedure calls//). Diferenta față de apelul prin referință este că nu mai apar situații de alias între argumente.
  
-==== Apelul prin referinta ==== 
-Realizează o asociere între argumentul actual și parametrul corespunzator. La intrarea în procedura apelată se determină adresa argumentului și aceasta se pune la dispoziția procedurii ca mijloc de accesare a argumentului. Procedura apelată va avea acces total la argument pe toată perioada execuției sale, putând să schimbe parametrul actual și să îl transmită altor rutine pe care le apelează. Acest mecanism este foarte eficient când vectori sunt tramsmiși ca parametri, dar poate fi ineficient în cazul parametrilor de mărime mică care ar putea fi transmiși prin regiștri. Pot apărea probleme în cazul în care o constantă este transmisă prin acest mecanism. 
- 
-Dacă compilatorul implementează o constantă ca pe o locație partajată de memorie în care se stochează valoarea constantei și care se accesează la fiecare folosire a constantei într-o procedură și dacă constanta se transmite altei proceduri prin referință,​ procedura apelata ar putea modifica conținutul locației alterând astfel valoarea constantei, necesară continuării execuției procedurii apelante după revenirea din cealaltă procedură. Soluția uzuală este copierea constantelor în noi locații de memorie și transmiterea acestor adrese când se face apelul prin referință. Aceasta modalitate de apel prin referința este implementată în Fortran, dar după cum am spus, acest efect se poate realiza și în C și C++ prin transmiterea adresei ca valoare. 
- 
-<​note>​ 
-În Java, parametrii sunt transmiși prin valoare (la fel ca și în C). Acest lucru este valabil atât pentru primitive cât și pentru obiecte. 
- 
-Afirmația **obiectele se transmit prin referință** este greșită. Obiectele sunt transmise tot prin valoare, însă acestea reprezintă o referință către zona de memorie în care sunt stocate datele. Corect este **referința obiectelor este transmisă prin valoare**. 
-</​note>​ 
 ==== Apelul prin nume ==== ==== Apelul prin nume ====
-Este cel mai complex ​mecanism de pasare ​a argumentelor, atât din punct de vedere conceptual, cât și în ceea ce privește implementarea și îl amintim doar din motive istorice deoarece ALGOL 60 este singurul limbaj care oferă acest tip de mecanismSe poate spune că este similar cu apelul ​prin referință ​deoarece permite accesul procedurii apelate la argumentul transmis. Diferența vine din faptul că adresa argumentului ​este calculată la fiecare acces la acesta ​și nu doar o singură dată la intrarea în procedură. Astfel, dacă argumentul este a[i], și valoarea lui i se schimbă între două folosiri ale argumentului,​ la cele două folosiri se vor accesa de fapt două elemente diferite ale vectorului.+Este un mecanism de evaluare întârziată ​a argumentelor ​unei funcții (//lazy evaluation//​)Este similar cu transmiterea ​prin referință, însă argumentele actuale sunt evaluate de fiecare dată când parametrul formal ​este folosit ​și nu doar o singură dată la intrarea în procedură. 
 + 
 +Astfel, dacă argumentul este a[i], și valoarea lui i se schimbă între două folosiri ale argumentului,​ la cele două folosiri se vor accesa de fapt două elemente diferite ale vectorului.
  
 +Mecanismul este rar în limbajele moderne, dar a fost folosit istoric (ALGOL 60). Un exemplu:
 <code pascal> <code pascal>
 begin begin
Line 65: Line 96:
 </​code>​ </​code>​
 În exemplul de mai sus în care i și a[i] sunt transmiși de către programul principal procedurii f, prima folosire a parametrului x aduce valoarea lui a[1], pe cand cea de-a doua folosire seteaza valoarea a[2]. Apelul outinteger() va avea ca rezultat afișarea valorilor „5 5 2”. Dacă s-ar fi folosit apelul prin referința, atunci s-ar fi afișat „5 5 8”. În exemplul de mai sus în care i și a[i] sunt transmiși de către programul principal procedurii f, prima folosire a parametrului x aduce valoarea lui a[1], pe cand cea de-a doua folosire seteaza valoarea a[2]. Apelul outinteger() va avea ca rezultat afișarea valorilor „5 5 2”. Dacă s-ar fi folosit apelul prin referința, atunci s-ar fi afișat „5 5 8”.
 +
 +O variantă a apelului prin nume este folosită în unele limbaje funcționale,​ în care evaluarea unui argument se face o singură dată, dar la prima folosire a acestuia, și nu la intrarea în funcție.
 +
  
 ===== Convenții de apel ===== ===== Convenții de apel =====
Line 130: Line 164:
  
 {{ :​cpl:​labs:​laborator-05-stack-full.png?​480 |}} {{ :​cpl:​labs:​laborator-05-stack-full.png?​480 |}}
 +
 +==== Exemplu: funcție minimală ====
 +<code c>int add(int a, int b)
 +{
 +      return a + b;
 +}</​code>​
 +
 +== gcc - x86 (CISC) ==
 +
 +<code asm>
 +_add:                     ; Identificatorii C sunt prefixati de caracterul _
 +
 +                          ; La intrarea in functie, stiva contine:
 +                          ;     | b
 +                          ;     | a
 +                          ; esp | Adresa de intoarcere
 +                          ​
 +  pushl   ​%ebp ​           ; Prin conventie, registrul ebp este frame pointer
 +  movl    %esp, %ebp      ; catre inceputul cadrului de stiva al unei functii.
 +                          ; Stiva acum:
 +                          ; ebp + 12 | b
 +                          ; ebp +  8 | a
 +                          ; ebp +  4 | adresa de intoarcere
 +                          ; ebp      | ebp din functia anterioara
 +                          ​
 +                          ; Deoarece stiva creste in jos, parametrii se gasesc la 
 +                          ; deplasamente pozitive (ebp+8) in timp ce variabilele locale ​
 +                          ; se gasesc la deplasamente negative (ebp-8).
 +  ​
 +  movl    12(%ebp), %eax  ; eax = b [ebp + 12]
 +  movl    8(%ebp), %edx   ; edx = a [ebp + 8 ]
 +                          ; Registrii eax si edx sunt volatili, deci pot fi folositi de 
 +                          ; catre functie fara restrictii.
 +                          ​
 +  addl    %edx, %eax      ; eax += edx
 +                          ; Valoarea intoarsa de functie este plasata in registrul eax.
 +                          ​
 +  popl    %ebp
 +  ret
 +</​code>​
 +
 +== gcc – ARM (RISC) ==
 +<code asm>
 +add:
 +   ​add ​    r0, r0, r1
 +   ​bx ​     lr
 +</​code>​
 +
 +Functia nu foloseste stiva. Atat parametrii, cat si adresa de intoarcere sunt transmisi prin registrii procesorului:​
 +
 +| R0 | a |
 +| R1 | b |
 +| LR | Adresa de intoarcere |
 +
 + 
 +Valoarea intoarsa de functie trebuie plasata in R0. Codul generat este astfel echivalent cu:
 +
 +  * R0 = R0 + R1
 +  * Salt la LR.
 +
 +== JVM (masina virtuala bazata pe stiva) ==
 +
 +<​code>​
 +public static int add(int, int);
 +   ​Stack=2,​ Locals=2, Args_size=2
 +   ​0: ​  ​iload_0
 +   ​1: ​  ​iload_1
 +   ​2: ​  iadd
 +   ​3: ​  ​ireturn
 +</​code>​
 +
 +Masina virtuala Java nu are registri si foloseste stiva pentru toate operatiile.
 +
 +Functia are 2 argumente, ce sunt copiate in zona de variabile locale, si foloseste 2 locatii de stiva.
 +
 +Variabilele locale sunt copiate pe stiva:
 +<​code>​
 +  push local[0]; --> a
 +  push local[1]; --> b
 +</​code>​
 +
 +Instructiunea de adunare inlocuieste doua valori din varful stivei cu suma lor. Codul echivalent intr-o masina cu registri:
 +
 +<​code>​
 +  pop a
 +  pop b
 +  c = a + b
 +  push c
 +</​code>​
 +
 +Functia intoarce valoarea ramasa în vârful stivei.
 +==== Exemplu: evaluarea unei expresii ====
 +<code c>int add_mul(int a, int b, int c, int d)
 +{
 +      return a * b + c * d;
 +}</​code>​
 +
 +== gcc - x86 ==
 +<code asm>
 +_add_mul:
 +  pushl   %ebp
 +  movl    %esp, %ebp
 +
 +; Stiva, dupa executia codului de initializare:​
 +;    +20  d
 +;    +16  c
 +;    +12  b
 +;    +8   a
 +;    +4   ​Adresa de intoarcere
 +; ebp+0   ebp din functia anterioara
 +
 +; Compilatorul descompune calculul expresiei in instructiuni simple
 +; Apoi mapeaza variabilele temporare pe registrii hardware ai procesorului
 +
 +  movl    8(%ebp), %edx  ;   T1 = a       ; edx = a
 +  movl    12(%ebp), %eax ;   T2 = b       ; eax = b
 +  imull   %edx, %eax     ; ​  T3 = T1 * T2 ; eax = edx * eax
 +  movl    16(%ebp), %ecx ;   T4 = c       ; ecx = c
 +  movl    20(%ebp), %edx ;   T5 = d       ; edx = d
 +  imull   %ecx, %edx     ; ​  T6 = T4 * T5 ; edx = ecx * edx
 +  addl    %edx, %eax     ; ​  T7 = T3 + T6 ; eax = eax + edx
 +
 +  popl    %ebp
 +  ret
 +</​code>​
 +
 +== JVM ==
 +
 +^ public static int add_mul(int,​ int, int, int); ^  Stiva  ^
 +| Code: | |
 +| Stack=3, Locals=4, Args_size=4 | ::: |
 +| 0:   ​iload_0 |  a  |
 +| 1:   ​iload_1 |  a  |
 +| ::: |  b  |
 +| 2:   imul |  a * b  |
 +| 3:   ​iload_2 |  a * b  |
 +| ::: |  c  |
 +| 4:   ​iload_3 |  a * b  |
 +| ::: |  c  |
 +| ::: |  d  |
 +| 5:   imul |  a * b  |
 +| ::: |  c * d  |
 +| 6:   iadd |  a * b + c * d  |
 +| 7:   ​ireturn | |
 +
 +Functia intoarce valoarea ramasa pe stiva.
 +
 +==== Exemplu: apeluri de funcții ====
 +<code c>int add_mul2(int a, int b, int c, int d)
 +{
 +      return mul(a,b) + mul(c,d);
 +}</​code>​
 +
 +== gcc - x86 ==
 +<code asm>
 +_add_mul2:
 +        pushl   %ebp
 +        movl    %esp, %ebp
 +        pushl   %ebx
 +        subl    $20, %esp
 +; Conform conventiei de apel, registrul ebx nu trebuie modificat de catre functii. ​
 +; Pentru ca add_mul2() il foloseste, el este salvat pe stiva la intrarea in functie
 +; si restaurat la iesire.
 +;
 +; Stiva, dupa executia codului de initializare:​
 +;
 +; ebp+20 ​ d
 +; ebp+16 ​ c
 +; ebp+12 ​ b
 +; ebp+8   a
 +; ebp+4   ​Adresa de intoarcere
 +; ebp+0   ebp din functia anterioara
 +;         ebx din functia anterioara
 +;         ​Spatiu pentru variabilele locale.
 +; esp+4   Al doilea parametru pentru mul(int,​int)
 +; esp+0   ​Primul parametru pentru mul(int,​int)
 +
 +        movl    12(%ebp), %eax
 +        movl    %eax, 4(%esp)
 +        movl    8(%ebp), %eax
 +        movl    %eax, (%esp)
 +        call    _mul
 +; Codul de nivel inalt: ebx = mul ( a, b )
 +; Parametrii pentru mul ( a, b ) sunt plasati in spatiul prealocat.
 +
 +        movl    %eax, %ebx
 +; Rezultatul functiei este salvat in ebx. Se foloseste ebx, deoarece, conform
 +; conventiei, este un registru nonvolatil, prin urmare nu va fi distrus de urmatorul
 +; apel al functiei mul().
 +
 +        movl    20(%ebp), %eax
 +        movl    %eax, 4(%esp)
 +        movl    16(%ebp), %eax
 +        movl    %eax, (%esp)
 +        call    _mul
 +; eax = mul ( c,d )
 +; De remarcat ca parametrii unei functii apartin cadrului de stiva al functiei ​
 +; anterioare, care o apeleaza. ​
 +; In cazul nostru, stiva la apelul lui mul(c,d) :
 +;
 +;                           d
 +;                           c
 +;                           b
 +;                           a
 +; Cadrul add_mul2() --> ​    ​Adresa de intoarcere din add_mul2()
 +;                           ebp din functia anterioara
 +;                           ebx din functia anterioara
 +;                           ​Spatiu pentru variabilele locale.
 +;                           d
 +;                           c
 +; Cadrul mul()      --> ​    ​Adresa de intoarcere din mul()
 +;                           ...
 +
 +        addl    %eax, %ebx
 +        movl    %ebx, %eax
 +; Calculul valorii intoarse de add_mul2():
 +;  ebx += eax
 +;  eax = ebx
 +
 +        addl    $20, %esp
 +        popl    %ebx
 +        popl    %ebp
 +        ret
 +</​code>​
  
 ===== Prototipuri de funcții ===== ===== Prototipuri de funcții =====
-Un prototip de funcție reprezintă o declarare a funcției care omite corpul funcției, dar specifică numele, tipul parametrilor și tipul întors. În timp ce definiția unei funcții specifică ​ce face o funcție, prototipul ​funcției ​poate fi descris ca fiind interfața funcției. Acest lucru este pus în evidență în IDE-urile care au opțiuni diferite “Go to declaration” și “Go to definition”.+Un prototip de funcție reprezintă o declarare a funcției care omite corpul funcției, dar specifică numele, tipul parametrilor și tipul întors. În timp ce definiția unei funcții specifică ​implementarea unei funcții, prototipul poate fi descris ca fiind interfața funcției. Acest lucru este pus în evidență în IDE-urile care au opțiuni diferite “Go to declaration” și “Go to definition”.
  
-Prototipurile ​sunt foarte importante, pentru că ele sunt folosite de compilator pentru a determina caracteristicile unei funcții ce urmează a fi apelata pentru a i se putea transmite parametrii ​sau a se putea primi valoarea întoarsă ​de funcție. De asemeneaprototipul funcției este folosit și în funcția apelată pentru primirea parametrilor de la funcția apelantă, precum și pentru transmiterea valorii întoarse de funcție.+Prototipurile sunt folosite de compilator pentru a determina caracteristicile unei funcții ce urmează a fi apelatapentru a i se transmite parametrii ​și a se primi valoarea întoarsă, ​fără a avea acces la codul sursă al funcției.
  
-Dacă prototipul funcției vizibil în locul sau locurile ​în care funcția este apelata diferă de prototipul functiei din locul în care funcția este definita, atunci se pot ajunge la apeluri de funcție eronate, datorate incompatibilității între numărul și tipul parametrilor din locul apelării funcției și din funcția propriu-zisă.+Dacă prototipul funcției vizibil în locul în care funcția este apelata diferă de prototipul functiei din locul în care funcția este definita, atunci se pot ajunge la apeluri de funcție eronate, datorate incompatibilității între numărul și tipul parametrilor din locul apelării funcției și din funcția propriu-zisă.
  
-Din punct de vedere al compilatorului, dacă o funcție nu este declarată, ea va avea automat atribuit prototipul implicit al funcțiilor:​+În limbajul C, dacă o funcție nu este declarată, ea va avea automat atribuit prototipul implicit al funcțiilor:​
  
 <code c> <code c>
Line 153: Line 411:
 int func(typeof_x,​ typeof_y, typeof_z); int func(typeof_x,​ typeof_y, typeof_z);
 </​code>​ </​code>​
-**ATENȚIE**:​ dacă tipurile argumentelor sunt mai mici decât un ''​int'',​ compilatorul de C va face upcast parametrului la int și apoi va chema funcția. Dacă implementarea funcției se aștepta să îi fi fost trimiși mai puțini bytes se poate corupe memoria. Exemplu: 
-<code c> 
-char c = '​a';​ 
-short s = 1; 
-f(c, s); 
- 
-// semnătură implicită generată: ​ 
-int f(int, int); 
-</​code>​ 
-Presupunând că parametrii sunt trimiși pe o stivă adresabilă la nivel de octet și că ''​sizeof(char) = 1'',​ ''​sizeof(short) = 2''​ și ''​sizeof(int) = 4''​ avem următoarea diferență între ce așteptă funcția ''​f''​ să primească ca parametri și ce argumente au fost pasate pe stivă. 
-| parametri trimiși la apel | c0 | c1 | c2 | c3 | s0 | s1 | s2 | s3 | 
-| parametri așteptați de implementare | c1 | s0 | s1 | 
- 
- 
- 
-în general compilatoarele fac verificări de compatibilitate între prototipurile unei aceleiași funcții, dar există cazuri cand aceste verificări nu sunt posibile (de exemplu când se folosesc funcții din biblioteci). 
  
 Un exemplu de functionare eronată a unui apel de funcție din motive de incompatibilitate între prototipurile unei funcții: Un exemplu de functionare eronată a unui apel de funcție din motive de incompatibilitate între prototipurile unei funcții:
Line 193: Line 435:
   * în locul de unde se face apelul, nu se transmite niciun parametru, și se aşteaptă să se întoarcă o valoare de tip int,   * în locul de unde se face apelul, nu se transmite niciun parametru, și se aşteaptă să se întoarcă o valoare de tip int,
   * în funcția apelată, se așteapta 2 parametri care vor fi citiți eronat din locațiile corespunzătoare,​ iar valoarea întoarsă va fi un long long.    * în funcția apelată, se așteapta 2 parametri care vor fi citiți eronat din locațiile corespunzătoare,​ iar valoarea întoarsă va fi un long long. 
- 
-Cea mai frecventă eroare din punct de vedere al incompatibilității între prototipuri este aceea a nedeclarării prototipurilor în cazul unor funcții de bibliotecă,​ fapt ce duce la folosirea prototipului implicit și la imposibilitatea verificării compatibilității între prototipuri. 
  
 Este important de observat, totuși, ca majoritatea limbajelor moderne (inclusiv C++, versus C) nu acceptă tipuri si prototipuri implicite. Este important de observat, totuși, ca majoritatea limbajelor moderne (inclusiv C++, versus C) nu acceptă tipuri si prototipuri implicite.
cpl/labs/07.1479158860.txt.gz · Last modified: 2016/11/14 23:27 by bogdan.nitulescu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0