La scope en C est-elle liée uniquement à la compilation, car nous soaps que nous pouvons accéder à n’importe quelle mémoire au moment de l’exécution?

J’essayais de comprendre le sens exact de scope dans C. Ce que je pouvais comprendre, c’est que scope se limite à la compilation Par exemple, si vous accédez à la variable locale à partir d’une autre fonction. Cela entraînera une erreur de temps de compilation. D’autre part, le programme suivant fonctionne bien. Cela signifie que le C a un modèle de mémoire à plat et que tout est accessible au moment de l’exécution. Les livres C associent la scope à la durée de vie et à la visibilité variable, ce qui m’a dérouté. Je pense que tous ces termes n’ont de sens que pour le temps de compilation. Quelqu’un peut-il s’il vous plaît éclaircir le sujet?

#include "stdio.h" int *ptr; int func(void) { /** abc is a local variable **/ int abc = 132; ptr = &abc; return 0; } int func1(void) { /** although scope of abc is over still I can change the value in the address of abc **/ *ptr = 200; printf("the value of abc=%d\r\n",*ptr); } int main(void) { func(); func1(); return 0; } 

Résultats: la valeur de abc = 200

En termes plus simples, qu’entend-on par scope? Cela entre-t-il au moment de l’exécution ou de la compilation? Comme nous pouvons le constater, nous pouvons accéder à tout au moment de l’exécution. Mais, si nous ne suivons pas les règles, alors nous aurons l’erreur de compilation. Par exemple, référence de variable locale dans une autre fonction. Le compilateur générera une erreur en disant “variable non définie …”.

Puis-je dire ce qui suit à propos des variables?

 1) Scope atsortingbute comes under comstack time. 2) Lifetime atsortingbute comes under run-time. 3) Visibility atsortingbute comes under comstack-time 

Oui, le modèle de mémoire de C vous permet d’accéder facilement à tout ce qui vous permet de faire des choses comme celle-ci et de voir des résultats “intéressants”.

Cependant, ce que vous avez fait ici est spécifié comme un comportement indéfini (UB) par le standard C. Cela signifie littéralement que tout peut arriver; c’est peut-être ce que vous attendez ou non.

Notez que vous n’avez pas accédé à “la variable locale” car au moment où vous créez la func access, celle-ci a déjà été renvoyée. La durée de vie de ses variables locales a donc expiré. Vous avez eu access à une région de mémoire qui “vient de se passer” a une valeur intéressante. Si vous func1 partir de func le comportement serait alors bien défini.

Quelques notes supplémentaires:

Scope est sans aucun doute un concept uniquement compilable; la scope d’un nom (variable, identificateur, etc.) est le sous-ensemble du code du programme où ce nom est reconnu par le compilateur.

Ceci est très différent de la durée de vie des variables, qui est indépendante de la scope dans le cas général, et la fusion des deux est une erreur commune. La durée de vie et la scope des variables locales sont en effet liées, mais ce n’est pas vrai de tout.

Alors qu’en théorie c’est “juste UB”, dans la pratique, il s’agit d’échouer. L’emplacement de abc est (dans chaque implémentation que je connaisse) quelque part sur la stack. Puisque vous avez quitté le bloc initial et que vous avez entré un autre bloc, il est fort probable que quelque chose d’autre occupe cet emplacement de mémoire. Ce que vous allez écraser.

Qu’entend-on par scope?

La scope d’une variable est la partie du texte dans laquelle la variable peut être référencée. Une variable locale a une scope de bloc : elle est visible depuis son sharepoint déclaration jusqu’à la fin du corps de la fonction englobante.
Cela n’a rien à voir avec la durée de vie d’une variable . C’est la durée de stockage qui indique la durée de vie d’une variable.

Cela entre-t-il au moment de l’exécution ou de la compilation?

Il entre en jeu au moment de la compilation et du temps de liaison. Lorsque le programme tentera d’accéder à une variable locale en dehors de son bloc, le compilateur vous indiquera une erreur à propos de cette variable non déclarée (qui est locale à son bloc).
Cet exemple l’expliquera mieux:

 #include  void userlocal(void); int main() { int a= 2; printf("local a in outer scope of main is %d\n",a); userlocal(); printf("local a in scope of userlocal is %d\n",b); // This will give error at comstack time return 0; } void userlocal(void) { int b = 20; printf("local a in scope of userlocal is %d\n",b); } 

Sortie:

 [Error] 'b' undeclared (first use in this function) 

Puis-je dire ce qui suit à propos des variables?

Oui tu peux dire.

Pour ce qui est de la scope, il est plus facile de penser que C est compilé dans une série de manipulations de registre et de mémoire, des structures telles que des blocs, des boucles for, des instructions if, des structs, etc., n’ont aucune signification autre que la compilation, abstractions pour vous permettre en tant que programmeur de garder votre santé mentale.

Pour ce qui est de l’exemple et de la mémoire, voici ma tentative de l’expliquer .

Comme tout le monde le dit, vous utilisez certaines implémentations spécifiques au compilateur d’une action standard non définie. Pour comprendre comment cela fonctionne, vous pouvez penser que le programme que vous écrivez a deux mémoires, le tas et la stack. À titre d’exemple, char *foo = malloc(50); alloue de la mémoire sur le tas et char foo[] = "Foo" alloue sur la stack. La stack est la mémoire qui se souvient de ce que vous faites et contient une longue liste de frameworks de stack, chaque appel de fonction ajoute un cadre et chaque retour ouvre un cadre. Le tas est l’autre type de mémoire.

Pour illustrer cela, nous avons ce programme:

 int *ptr; int func() { int abc = 123; ptr = &abc; return 0; } int func1() { int def; printf("func1() :: *abc=%i\n", *ptr); def = 200; return 0; } int main() { func(); printf("main() :: *ptr=%i\n", *ptr); func1(); printf("main() :: *ptr=%i\n", *ptr); } 

Et ce qui suit se passera sur la stack ( - est la mémoire indéfinie / inutilisée, > à gauche est l’endroit où votre programme est en cours d’exécution, X est la donnée):

 |-----| |-----| |-----| 

En entrant main() il pousse le cadre de stack dans la stack:

  |-----| |-----| >|XXXXX| <- This is where all memory needed to execute main() is. 

Ensuite, vous appelez func() qui, dans la mémoire nécessaire à l'exécution, contient l'entier 123 .

  |-----| >|XX123| <- This is the stack frame for func() |XXXXX| <- Still the stack frame for main() 

func() définit le pointeur global *ptr sur l'adresse de l'entier de la stack. Cela signifie que lorsque func() retourne (et que la mémoire n’est pas effacée, ce qui constituerait un gaspillage de cycles de la CPU), la valeur rest

  |-----| |--123| >|XXXXX| <- main() 

et peut toujours être référencé par *ptr jusqu'à ce que vous appeliez la fonction suivante

  |-----| >|XXXXX| <- This is the stack frame for func1() |XXXXX| 

Maintenant, *ptr semblera avoir une valeur aléatoire ... mais vous pouvez toujours accéder à la position de la mémoire et la changer. (Si func() et func1() définissent chacun un seul entier local, il est fort probable que *ptr func1() cet entier dans func1() )

Prime

Je n'ai pas testé le programme, mais je suppose qu'il imprimerait quelque chose comme ceci:

 main() :: *ptr=123 func1() :: *ptr= main() :: *ptr= 

Vous avez raison, C a un modèle de mémoire plate. Et permet d’accéder à n’importe quelle région de la mémoire. Ici ptr est un pointeur global sur un int . Donc, il pointe vers une adresse où un entier peut être stocké et récupéré. Cela conduira à un undefined behaviour dans différents types de scénarios.

Scope est une propriété de compilation qui traite lorsque le référencement d’une variable est valide ou visible, ce qui est différent de la storage duration ou de la durée de vie d’un object indiquant la validité de l’access à la mémoire associée à la variable.

Dans ce cas, abc a une durée de stockage automatique, ce qui signifie que sa durée s’étend pour le bloc dans lequel il se trouve, ce qui est dans ce cas la fonction func et le fait de tenter d’accéder à la mémoire associée à abc dehors de ce bloc constitue un comportement indéfini. travail mais les résultats ne peuvent pas être invoqués.

Le paragraphe 6.2.4 stockage des objects de la version préliminaire de la norme C99 6.2.4 les différentes durées de stockage: static et automatic . Dans explique qu’une durée de vie de variables static correspond à l’exécution complète du programme et au paragraphe 5, il est indiqué:

Pour un tel object qui n’a pas de type tableau de longueur variable, sa durée de vie s’étend de l’entrée dans le bloc auquel il est associé jusqu’à l’exécution de ce bloc. [..]

Donc, une durée de vie variable automatique correspond au bloc dans lequel elle est contenue et le paragraphe 2 dit:

La durée de vie d’un object correspond à la partie de l’exécution du programme pendant laquelle il est garanti que la mémoire lui est réservée. Un object existe, a une adresse constante, 25) et conserve sa dernière valeur stockée pendant toute sa durée de vie.26) Si un object est référencé en dehors de sa durée de vie, le comportement est indéfini. La valeur d’un pointeur devient indéterminée lorsque l’object vers lequel il pointe atteint la fin de sa vie.

Donc, se référer à un object en dehors de sa durée de vie garantie est un comportement indéfini.

C’est indéfini et vous ne devriez pas faire confiance à la valeur. Avec de petits changements sur votre code (MSVC 2013), il y a une surprise.

 int func1() { /** although scope of abc is over still I can change the value in the address of abc **/ printf("the value of abc=%d\r\n", *ptr); *ptr = 200; printf("the value of abc=%d\r\n", *ptr); return 0; } int main() { func(); printf("the value of abc=%d\r\n", *ptr); func1(); } 

la sortie est sur mon ordinateur.

la valeur de abc = 132
la valeur de abc = 1519668
la valeur de abc = 200