Une vue de bas niveau des appels et des retours de fonction

Introduction

Depuis que j'ai appris le langage C, j'ai utilisé des fonctions pendant si longtemps, mais pourquoi utilisons-nous des fonctions ?

En fait, il s'agit de réutiliser un morceau de code. À l'aide de fonctions, nous pouvons saisir différentes valeurs et obtenir le résultat en retournant la valeur.

Notions associées :

En langage assembleur, les instructions "call" et "ret" sont utilisées pour changer l'ordre d'exécution en changeant la valeur du PC (compteur de programme).
Dans ce processus, le mouvement des données est également impliqué, comme les paramètres et les valeurs
de retour. cours, à l'intérieur du programme, et quelques variables locales

Deuxièmement, la réalisation des paramètres et des variables locales

2.1 Variables locales

Question : Les variables peuvent-elles avoir le même nom ?

Parfois oui, selon la situation

Pourquoi?

Parce que le compilateur mappera des variables portant le même nom à des adresses différentes.

Exemple

#include<stdio.h>
int first;
int second;//定义了一个全局的变量second
void callee(int first){
    
    
    int second;//定义了一个局部的变量second
    second=1;
    first=2;
    printf("callee:first=%d,second=%d\n",first,second);
    printf("address of second:%#x\n\n",&second);
}
int main(){
    
    
    first=1;
    second=2;
    callee(first);
    printf("caller:first=%d,second=%d\n",first,second);
    printf("address of second:%#x\n",&second);
    return 0;
}

résultat de l'opération :

callee:first=2,second=1
address of second:0x61fed8

caller:first=1,second=2
address of second:0x405068

A travers cet exemple, on trouve :

1. Les variables occupent de la mémoire
2. Le compilateur atteint la portée en mappant des variables du même nom à des adresses différentes.

2.2 Paramètres—paramètres formels et réels

Réfléchissez d'abord à une question, les paramètres formels et les paramètres réels partagent-ils la même adresse en mémoire ? Autrement dit, l'adresse du paramètre formel et le paramètre réel sont-ils les mêmes ?

La réponse est non, les adresses des paramètres formels et réels sont différentes.

Vous pouvez le tester avec un petit exemple :

#include<stdio.h>
void callee(int formal_parameter){
    
    
    printf("address of second:%#x\n\n",&formal_parameter);
}
int main(){
    
    
    int actual_parameter=1;
    printf("address of second:%#x\n",&actual_parameter);
    callee(actual_parameter);//传入的实参的值
    return 0;
}

résultat de l'opération :

address of second:0x61fefc
address of second:0x61fee0

Autrement dit, le compilateur réaffectera une adresse au paramètre formel de la fonction.
Alors pourquoi est-il conçu de cette façon :

Cela permet au paramètre réel de ne pas être perturbé, c'est-à-dire que peu importe comment je modifie la valeur de mon paramètre formel, mon paramètre réel ne changera pas, protégeant le paramètre réel.

Passer par valeur
- cela permet au paramètre réel de ne pas être perturbé
Par référence
- le langage C ne prend pas en charge l'appel par référence, mais ses opérateurs & et * produisent facilement le même effet. Pour le contenu connexe, vous pouvez afficher le transfert de paramètre de pointeur dans le langage C

2.3 Comment le compilateur implémente les variables et paramètres locaux

Prenons un exemple récursif

#include<stdio.h>
void callee(int n){
    
    
    printf("Function calls:%d(0x%08x)\n",n,&n);
    if (n==0) {
    
    
        printf("Function returns:%d(0x%08x)\n",n,&n);
        return;
    }
    callee(n-1);
    printf("Function returns:%d(0x%08x)\n",n,&n);
}
int main(int argc,char *argv[]){
    
    
    int n=5;
    callee(n);
    return 0;
}

résultat de l'opération :

Function calls:5(0x0061fee0)
Function calls:4(0x0061fec0)
Function calls:3(0x0061fea0)
Function calls:2(0x0061fe80)
Function calls:1(0x0061fe60)
Function calls:0(0x0061fe40)
Function returns:0(0x0061fe40)
Function returns:1(0x0061fe60)
Function returns:2(0x0061fe80)
Function returns:3(0x0061fea0)
Function returns:4(0x0061fec0)
Function returns:5(0x0061fee0)

insérez la description de l'image ici

A chaque appel, une variable locale n doit être créée. Réfléchissez à une question, comment le compilateur sait-il combien de fois cette fonction doit être appelée ?

Le compilateur ne peut pas déterminer tant que le programme n'a pas fini de s'exécuter, il ne peut donc être alloué dynamiquement qu'au moment de l'exécution.

L'inconvénient est que les performances du programme seront réduites lors de son exécution.

Comment le compilateur l'implémente-t-il ?

Comme le compilateur ne sait pas combien de variables il doit allouer dynamiquement, il réserve beaucoup d'espace pour l'expansion.

empiler:

La pile est une zone spéciale de la mémoire de stockage (utilisée pour stocker les variables temporaires créées par chaque fonction).
Au fur et à mesure que les fonctions poussent et sortent des variables, la pile s'agrandit et se rétrécit en conséquence.

Acho que você gosta

Origin blog.csdn.net/weixin_47138646/article/details/122199224
Recomendado
Clasificación