Autres exemples plus compliqués
#include <stdio.h>
float y ; // c'est une variable globale définie en dehors de tout bloc
// de fonction
void plus_un(float * y) // "y" est un PF de la fonction plus_un
// qui contiendra la valeur d'une adresse
// vers un flottant
{
// point P :
*y = *y + 1;
// printf("%f",x); // a dé-commenter pour essayer, cela génère une erreur
// l'identificateur "x" n'est pas connu en ce point. C'est une VL de main.
}
Float cube(float x) // "x" est un PF de type float de la fonction cube
// qui contiendra la valeur d'un flottant
{
int y ; // "y" est une variable locale de la fonction cube
// tout identicateur "y" défini avant n'existe plus
// seule la déclaration la plus locale compte donc la VG "y"
// n'est plus visible, c'est la définition locale qui domine.
// point C1
plus_un(&x); // "x" est ici le PF de cube, c'est un réceptacle,
// son adresse existe, il est utilisé comme PR
// de la fonction "plus_un". Le quadruplet de "&x" est donc
// ici (V, PR plus_un, float, adresse de x PF de cube)
// la valeur de "adresse de x PF de cube" ne peut être
// déterminée, elle dépend de l'exécution, cela n'a pas
// d'importance car ce n'est pas l'adresse de "x" PF de cube
// que l'on modifie mais "x" la valeur du PF de cube
// grâce à l'utilisation de son adresse.
y = x*x*x; // il y a un cast automatique
// point C2
//printf("%f",z); // a dé-commenter pour essayer, cela génère une erreur
// car l'identificateur "z" n'est pas défini en ce point, c'est une VL
// de la fonction "main" qui n'existe pas ici
return (float)y; // il y a un cast explicite
}
int main(void)
{
float x,z; // x est une variable locale de la fonction main
// z est une variable locale de la fonction main
// point M1 x = cube(3.0);
// le PR paramètre réel de cube est le littéral constant 3.0
// point M2 y = cube(x);
// le PR paramètre réel de cube est la valeur de la VL "x" de main
// point M3
{ int t; // un bloc dans un bloc avec "t" une nouvelle VL du nouveau bloc
z= 4.0;
printf("%f",z); // fonctionne car "z" est définie dans le bloc englobant
}
// printf("%d",t); a dé-commenter pour essayer, cela génère une erreur
// car l'identificateur "t" n'est plus défini en ce point du code
}
Comme on peut redéfinir localement un identificateur, on peut donc changer son type.
Quand le programme s'exécute nous passons dans l'ordre par les points :
M1 : fonction main
C1 : fonction cube
P : fonction plus_un
C2 : fonction cube
M2 : fonction main
C1 : fonction cube
P : fonction plus_un
C2 : fonction cube
M3 : fonction main
Nous ne nous préoccupons pas des identificateurs "z" et "t" pour l'exécution de ce code, ils ont été ajoutés pour mettre en évidence la notion de "visible", si l'on dé-commente les lignes qui les affichent le compilateur génère une erreur et refuse de compiler. Ils ne sont en effet pas visibles au niveau des "printf" mis en commentaires.
Nous nous intéressons aux deux identificateurs "x" et "y". Quel quadruplet leur associer en chacun des points d'observation ?
Le tableau suivant se lit de haut en bas et suit l'exécution du code :
A vous de suivre pas à pas le déroulement du code en appliquant les règles.
Au point P, "y" est le PF de "plus_un" qui contient une valeur qui est un "pointeur vers flottant".
Au point C1, "x" est l'identificateur du PF de "cube" qui contient une valeur flottante.
Le réceptacle de "x" existe, il est créé dynamiquement au moment de l'appel de "cube", donc on peut calculer son adresse.
nous rappelons que les PF et VL d'une fonction n'existent qu'au moment où la fonction est invoquée et pendant son déroulement, ni avant l'appel de la fonction, ni après l'appel de la fonction (car ils sont dynamiquement créés puis détruits).
-
Il est très dangereux de modifier la valeur d'un identificateur qui n'est pas localement défini. Par exemple, modifier dans une fonction la valeur d'une variable globale. Cela peut conduire à des erreurs graves de programmation. Cette pratique est à proscrire.
-
Les fonctions doivent être conçues comme des entités de traitement indépendantes, elles reçoivent des paramètres, utilisent éventuellement des variables locales pour effectuer leurs traitements et retournent une valeur.
-
Si vous avez, en cours de programmation, un doute sur l'existence d'un identificateur en un point du programme, insérez un "printf" pour essayer de voir la valeur de cet identificateur. Si ce dernier n'est pas visible alors le compilateur refusera de compiler le code. En effet, cet identificateur ne sera alors pas connu et la règle "on ne peut utiliser quelque chose que si cette chose est préalablement déclarée" sera alors violée. Ce que détectera le compilateur