7 votes

Le champ d'application en C n'est-il lié qu'au moment de la compilation, puisque nous savons que nous pouvons accéder à n'importe quelle mémoire au moment de l'exécution ?

J'essayais de comprendre la signification exacte de la portée en C. Ce que j'ai pu comprendre, c'est que la portée est limitée au temps de compilation uniquement. Par exemple, si vous accédez à une variable locale à partir d'une autre fonction. Cela entraînera une erreur de compilation. D'un autre côté, le programme suivant fonctionne bien. Cela signifie que le C a un modèle de mémoire plat et que l'on peut accéder à tout au moment de l'exécution. Les livres de C associent le champ d'application à la durée de vie et à la visibilité des variables, je trouve cela assez déroutant. Je pense que tous ces termes n'ont de sens qu'au moment de la compilation. Quelqu'un peut-il m'éclairer à ce 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 portée ? Intervient-elle 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 respectons pas les règles, nous obtiendrons une erreur de compilation. Par exemple, la référence à une variable locale dans une autre fonction. Le compilateur lancera une erreur disant "variable non définie...".

Puis-je dire la chose suivante à propos des variables ?

1) Scope attribute comes under compile time.
2) Lifetime attribute comes under run-time.
3) Visibility attribute comes under compile-time

8voto

Jon Points 194296

Oui, le modèle de mémoire du C vous permet d'accéder facilement à n'importe quoi, ce qui vous permet de faire des choses comme celles décrites ci-dessus et d'obtenir des résultats "intéressants".

Cependant, ce que vous avez fait ici est spécifié comme comportement indéfini (UB) par la norme C. Cela signifie que tout peut littéralement arriver ; cela peut être ce que vous attendez, ou pas.

Notez que vous n'avez pas accédé à "la variable locale" car au moment où vous effectuez l'accès func est déjà revenu, donc la durée de vie de ses variables locales a expiré. Ce que vous a fait L'accès était une région de la mémoire qui "se trouvait" avoir une valeur intéressante. Si vous appelez func1 de l'intérieur func alors le comportement serait bien défini.

Quelques notes supplémentaires :

Portée La portée d'un nom (variable, identifiant, etc.) est le sous-ensemble du code du programme où ce nom est reconnu par le compilateur.

C'est très différent de la à vie de variables, qui est indépendant de la portée dans le cas général, et confondre les deux est une erreur courante. La durée de vie et la portée des variables locales sont en effet entremêlées, mais ce n'est pas vrai pour tout.

1voto

haccks Points 33022

qu'entend-on par portée ?

La portée d'une variable est la partie du texte dans laquelle la variable peut être référencée. Une variable locale a portée du bloc il est visible depuis son point de 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 renseigne sur la durée de vie d'une variable.

Intervient-il au moment de l'exécution ou de la compilation ?

Il intervient au moment de la compilation et de la liaison. Lorsque le programme essaiera d'accéder à une variable locale en dehors de son bloc, le compilateur vous donnera une erreur concernant cette variable non déclarée (qui est locale à son bloc).
Cet exemple l'expliquera mieux :

#include <stdio.h>

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 compile 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 au sujet des variables ?

Oui, vous pouvez le dire.

1voto

aragaer Points 3518

Si, en théorie, il s'agit d'un "simple UB", en pratique, c'est une invitation à l'échec. L'emplacement de abc est (dans toutes les implémentations que je connais) quelque part sur la pile. Puisque vous avez quitté le bloc initial pour entrer dans un autre bloc, il est fort probable que quelque chose d'autre occupe cet emplacement de la mémoire. Que vous allez écraser.

1voto

Hobblin Points 422

En ce qui concerne le champ d'application, il est plus facile de penser que le C est compilé en une série de manipulations de registres et de mémoire, les structures telles que les blocs, les boucles for, les instructions if, les structures, etc. n'ont aucune signification au-delà de la compilation, ce ne sont que des abstractions qui vous permettent, en tant que programmeur, de garder la raison.

En ce qui concerne l'exemple et la mémoire, voici ma tentative d'explication. .

Comme tout le monde le dit, vous utilisez des implémentations spécifiques à certains compilateurs d'une action, par défaut, non définie. Pour comprendre comment cela fonctionne, vous pouvez considérer que le programme que vous écrivez possède deux mémoires, le tas et la pile. Par exemple char *foo = malloc(50); alloue de la mémoire sur le tas et char foo[] = "Foo" l'alloue sur la pile. La pile est la mémoire qui se souvient de ce que vous faites et elle contient une longue liste de cadres de pile, chaque appel de fonction ajoute un cadre et chaque retour enlève 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 produira sur la pile ( - est une mémoire non définie/non utilisée, > à gauche est l'endroit où votre programme est en train de s'exécuter, X est une donnée) :

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

Alors que nous entrons main() il pousse le cadre de la pile sur la pile :

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

Puis 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 à l'adresse de l'entier sur la pile. Cela signifie que lorsque func() retourne (et la mémoire n'est pas effacée car cela serait un gaspillage de cycles CPU), la valeur demeure

 |-----|
 |--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 modifier. (Si func() y func1() chacun ne définit qu'un seul entier local dans son champ d'application, il est fort probable que *ptr pointera vers cet entier dans func1() aussi)

Bonus

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

main() :: *ptr=123
func1() :: *ptr=<some random values>
main() :: *ptr=<possibly 200, could be something else>

0voto

sr01853 Points 2861

Vous avez raison de dire que le C a un modèle de mémoire plat. Et permet d'accéder à n'importe quelle région mémoire. Ici ptr est un pointeur global vers un int . Il pointe donc vers une adresse où un nombre entier peut être stocké et récupéré. Cela conduira à un undefined behaviour dans différents types de scénarios.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X