learn_C_deep_9 (comprendre le sens du retour du point de vue de l'assemblage, divers scénarios d'application de const)

mot-clé de retour

        Je ne sais pas si nous avons tous un doute : on télécharge un logiciel de jeu à grande échelle ( Glory of the King ), il faut plusieurs heures pour le télécharger, mais une fois qu'on perd le jeu d'affilée, quand on veut supprimer ce logiciel, il ne prend qu'une douzaine de secondes, pourquoi est-ce aujourd'hui, nous allons prendre ce doute et résoudre ce problème ensemble.

Dans l'ordinateur, libérer de l'espace signifie-t-il vraiment effacer toutes nos données ?

        Dans un ordinateur, libérer de l'espace ne signifie pas nécessairement effacer toutes les données qu'il contient. Pour libérer de l'espace, c'est-à-dire pour supprimer des fichiers, l'ordinateur n'effacera pas ou ne supprimera pas immédiatement le contenu des fichiers. En fait, le système d'exploitation de l'ordinateur marque généralement l'espace disque correspondant à ces fichiers comme réutilisable , puis l'écrase lorsque de nouvelles données doivent être stockées. Par conséquent, même si un fichier est supprimé, son contenu peut toujours exister sur le disque dur ou un autre périphérique de stockage et peut être récupéré tant qu'il n'a pas été écrasé.

         Résumé : lorsque vous supprimez un fichier, l'ordinateur n'a qu'à marquer simplement l'espace de stockage où se trouve le fichier comme disponible sans transfert de données réel, de sorte que la vitesse de suppression des données est plus rapide. 

Regardons un morceau de code

#include <stdio.h>
char* show()
{
	char str[] = "hello world";
	return str;
}
int main()
{
	char* s = show();
	printf("%s\n", s);
	return 0;
}

        Ce code implique principalement deux problèmes : le cycle de vie des variables locales et la sécurité de la mémoire.

Examinons d'abord la durée de vie des variables locales . Dans la fonction show(), la variable str est une variable locale définie dans le corps de la fonction. Le cycle de vie des variables locales se trouve uniquement dans le corps de la fonction et sera détruit une fois la fonction exécutée. Par conséquent, après l'exécution de l'instruction return str;, l'espace mémoire occupé par la variable str est libéré .

Ensuite, nous examinons la question de la sécurité de la mémoire . Dans la fonction show(), nous avons renvoyé l'adresse de la variable str comme valeur de retour à l'appelant. Puisque l'espace mémoire où se trouve la variable str a été libéré , l'espace mémoire pointé par le pointeur renvoyé s n'est plus garanti comme étant sûr. Dans la fonction main(), nous appelons la fonction printf() pour sortir la chaîne dans l'espace mémoire pointé par s. Parce que cet espace mémoire peut avoir été utilisé par d'autres programmes ou systèmes, cela provoquera des erreurs inconnues. Ceci est aussi souvent appelé le problème du "pointeur sauvage".

 Examinons de plus près le processus de publication

Résumé : L'instruction return dans ce code ne peut pas renvoyer un " pointeur  " pointant vers " la mémoire de la pile ", car cette mémoire est automatiquement détruite à la fin du corps de la fonction.

Est-ce étrange ici ? Ne venons-nous pas de dire que le cadre de la pile sera libéré après l'appel de la fonction, et que les données x à l'intérieur doivent être écrasées après la fonction printf, mais pourquoi pouvons-nous l'imprimer ici ? - Ici, nous allons introduire le mot-clé return

        L'instruction return est un mot clé en langage C qui termine l'exécution de la fonction en cours et renvoie une valeur ou aucune valeur. Dans la plupart des cas, l'instruction return est utilisée pour renvoyer le résultat de l'exécution d'une fonction à l'appelant.

        L'instruction return a de nombreuses utilisations et structures grammaticales différentes, dont les plus courantes sont :

```

c expression de retour ;

```

Parmi eux, expression peut être une constante, une variable, une expression ou une valeur de retour d'autres appels de fonction, et cette valeur deviendra la valeur de retour de la fonction et sera renvoyée à l'appelant.

        Notre code ci-dessus renvoie la valeur de X. Le mot-clé return dans le cadre de la pile de fonctions enregistre la valeur de x dans le registre et ramène la valeur de x à y dans la fonction principale via le registre. S'il s'agit de l'adresse de x, elle sera également renvoyée, mais la valeur qu'elle contient ne peut pas être imprimée. Le compilateur émettra un avertissement.

mot-clé const

        const est un mot clé du langage C. Sa fonction est de modifier la variable, indiquant que la valeur de la variable ne peut pas être modifiée directement . Cela signifie qu'une fois qu'une variable déclarée avec le mot-clé const reçoit une valeur pendant l'exécution du programme, elle ne peut plus être modifiée. Le mot-clé const peut être utilisé pour modifier des variables de types de données de base, des structures, des pointeurs et d'autres types.

        L'utilisation du mot clé const présente les avantages suivants :

1. La lisibilité du programme est meilleure. L'utilisation du mot-clé const peut indiquer clairement aux autres programmeurs que la variable est une constante et ne doit pas être modifiée.

2. Le programme est plus sûr L'utilisation du mot-clé const permet d'éviter de modifier accidentellement une variable qui devrait être une constante dans le programme, ce qui améliore la robustesse du programme.

3. Le compilateur peut utiliser le mot clé const pour optimiser le programme. Par exemple, dans certains cas, le compilateur peut incorporer directement la constante dans le code, ce qui améliore l'efficacité d'exécution du programme.

const les variables en lecture seule modifiées - - - ne peuvent pas être modifiées directement !

 Ne peut pas être modifié directement, mais peut être modifié indirectement - par adresse

 Conclusion : La variable modifiée par const n'est pas vraiment une constante non modifiable.

Les variables décorées const peuvent-elles faire partie d'une définition de tableau ?

const entier n = 100 ;

int arr[n] ;

        Ici vous pouvez voir un autre article que j'ai écrit, qui le présente.

Résumé : Une erreur est signalée directement sous vs2013 (standard C), mais sous gcc (extension GNU), c'est ok. Mais nous sommes tous conformes à la norme, cela ne peut pas être fait.

const ne peut être initialisé directement que lorsqu'il est défini et ne peut pas être assigné deux fois. Pourquoi?

        Le rôle du mot clé const est d'indiquer au compilateur que la variable est une constante et ne doit pas être modifiée. Par conséquent, une fois qu'une variable déclarée avec le mot-clé const est affectée d'une valeur lors de l'exécution du programme, elle ne peut pas être modifiée.

        Pour que le compilateur atteigne cet objectif, le mot clé const effectuera certaines optimisations sur la variable lors de la compilation, de sorte que la valeur de la variable ne puisse pas être modifiée lors de l'exécution du programme. Si la variable est autorisée à être affectée une deuxième fois pendant l'exécution du programme, le compilateur ne peut pas garantir que la valeur de la variable ne sera pas modifiée, ce qui est contraire au sens du mot-clé const.

        Par conséquent, const ne peut être initialisé directement que lorsqu'il est défini, et la raison pour laquelle il ne peut pas être assigné deux fois est d'assurer la robustesse et la sécurité du programme. Si vous avez vraiment besoin de modifier dynamiquement la valeur d'une variable pendant l'exécution du programme, vous devez utiliser des variables ordinaires au lieu de variables modifiées par const.

const pointeur modifié

Introduisez d'abord le concept de lvalue et rvalue

        En programmation informatique, lvalue et rvalue sont deux types d'expressions.

        La lvalue représente l'objet à affecter, qui peut apparaître à gauche de "=", ou dans n'importe quel opérande de l'expression. Les valeurs L peuvent apparaître dans plusieurs opérations et peuvent être modifiées.

        Une rvalue représente une valeur qui peut être affectée à une lvalue, et une rvalue peut apparaître dans n'importe quel opérande d'une expression, mais ne peut pas être modifiée. Une rvalue est généralement une valeur temporaire utilisée pour évaluer une expression et dont la valeur est ignorée lorsque l'expression a fini de s'exécuter.

Il existe également des lvalues ​​et des rvalues ​​​​pour les variables de pointeur.

        Dans le langage C, un pointeur est une variable spéciale qui stocke une adresse mémoire et peut être utilisée pour accéder aux données stockées à cette adresse. Lors de la définition d'une variable de pointeur, nous pouvons utiliser le mot clé const pour déterminer si le pointeur et les données pointées par le pointeur peuvent être modifiés.

        1. const int* p ;

        Le const agit ici sur les données pointées par le pointeur, indiquant que les données pointées par p ne peuvent pas être modifiées. En d'autres termes, nous pouvons lire ces données constantes via le pointeur p, mais nous ne pouvons pas modifier ces données via le pointeur p. Par exemple : p lui-même peut être modifié (comme p++), mais la variable de type int pointée par p ne peut pas être modifiée (comme *p=10 est illégal).

        2. int const* p ;

        Cette définition est équivalente à la définition ci-dessus, la position du mot-clé const est différente mais la signification est la même.

        3. int* const p ;

        Le const agit ici sur le pointeur lui-même, indiquant que le pointeur p lui-même ne peut pas être modifié. En d'autres termes, on ne peut pas le faire pointer vers d'autres adresses en changeant la valeur du pointeur p, mais on peut modifier les données stockées dans cette adresse grâce au pointeur p. Par exemple : p lui-même ne peut pas être modifié (comme p++ est illégal), mais la variable de type int pointée par p peut être modifiée (comme *p=10 est légal).

        4. const int* const p ;

        Il y a deux mots clés const dans cette définition, un pour le pointeur lui-même et un pour les données pointées par le pointeur. Indique que le pointeur p lui-même et les données pointées par p sont non modifiables, c'est-à-dire que le pointeur p ne peut pointer que vers une certaine adresse, et les données stockées à cette adresse ne peuvent pas être modifiées. Par exemple : p lui-même ne peut pas être modifié (comme p++ est illégal), et la variable de type int pointée par p est également non modifiable (comme *p=10 est illégal).

        const int* p1 = &a;

        entier* q1 = p1 ;

Ici, le pointeur p1 de type const int* est affecté au pointeur q1 de type int*, ce qui n'est pas sûr. Étant donné que p1 pointe vers une variable de type int constante non modifiable, si la variable pointée par p1 est modifiée via le pointeur q1, un comportement indéfini se produira. La manière correcte consiste à transtyper le type pointeur en un type non const, à savoir : int* q1 = (int*)p1;

        int* const p2 = &b;

        entier* q2 = p2 ;

Ici, il est sûr d'affecter le pointeur p2 de type int* const au pointeur q2 de type int*. Parce que p2 pointe vers une variable de type int modifiable, et que p2 lui-même n'est pas modifiable. De plus, il est sûr d'affecter un pointeur de type const à un pointeur de type non const. Il n'y a donc aucun problème avec ce code et aucune modification n'est nécessaire.

Paramètres des fonctions modifiées const

         En langage C, on peut aussi utiliser le mot clé const pour modifier les paramètres de la fonction, ce qui signifie que la fonction ne modifiera pas la valeur du paramètre modifié. Les paramètres d'une fonction peuvent être divisés en paramètres formels et en paramètres réels. Les paramètres formels sont les variables définies dans la fonction et les paramètres réels sont les valeurs transmises à la fonction lorsque la fonction est appelée. Lorsque le mot clé const est utilisé pour modifier un paramètre formel, cela signifie que la valeur de ce paramètre formel ne peut pas être modifiée dans la fonction. Si la fonction tente de modifier le paramètre modifié par const, le compilateur signalera une erreur.

        Voici un exemple d'utilisation de const pour décorer des paramètres de fonction :

void print_array(const int* arr, int n)
{
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

int main(void)
{
    int arr[] = { 1, 2, 3, 4, 5 };
    print_array(arr, 5);
    return 0;
}

        Dans cet exemple, le premier paramètre de la fonction print_array est de type const int*, ce qui signifie que le pointeur pointe sur une mémoire non modifiable, et la valeur correspondant à cette mémoire ne peut pas être modifiée dans la fonction. Le deuxième paramètre est un type int ordinaire, indiquant la longueur du tableau. Dans la fonction, nous avons utilisé une boucle for pour parcourir le tableau et imprimé chaque élément du tableau à l'aide de la fonction printf. Comme nous avons déclaré le premier paramètre de type const int*, la valeur pointée par ce pointeur ne peut pas être modifiée dans la fonction. Si la fonction tente de modifier la valeur pointée par ce pointeur, le compilateur signalera une erreur.

        Cela permet de protéger les valeurs du tableau contre les modifications accidentelles, améliorant ainsi la robustesse du programme.

La fonction forme-t-elle une variable temporaire lors du passage des paramètres ?

        En langage C, les paramètres de fonction sont passés par valeur ou par adresse. Lorsque nous appelons une fonction, la valeur du paramètre réel est copiée et transmise à la fonction, tandis que le paramètre formel défini dans la fonction est une nouvelle variable. Au cours de ce processus, une variable temporaire est en effet générée pour stocker la valeur du paramètre réel.

Bye Bye! ! !

Je suppose que tu aimes

Origine blog.csdn.net/qq_64446981/article/details/130543352
conseillé
Classement