Differences

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

Link to this comparison view

cpl:labs:07 [2016/11/15 01:19]
bogdan.nitulescu [Apelul prin referință]
cpl:labs:07 [2016/11/15 02:35] (current)
bogdan.nitulescu [Prototipuri de funcții]
Line 164: 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 187: 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 227: 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.1479165579.txt.gz · Last modified: 2016/11/15 01:19 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