Hello et bravo pour ta persévérance.

Envoyé par
Seb_33
On utilise typedef quand on veut typer un éléments que l'on créer comme une structure si l'on souhaite réutiliser le type créer sinon c'est pas nécessaire?
Non, c'est plus simple que ça : typedef signifie « Type Definition » mais sert en fait à créer un alias sur un type existant. Par exemple :
… te permet de déclarer indistinctement des variables de type « int » ou « entier ». Elles seront de même nature. Dans cet exemple-ci, ça n'a pas beaucoup d'intérêt. Par contre, lorsque ton type est très long ou très compliqué, par exemple, dans le cas d'un pointeur de fonction, ça devient vite très utile, surtout quand cela permet de s'affranchir des subtilités syntaxiques. Par exemple, si je veux définir un pointeur sur une fonction qui elle-même attend en paramètre un pointeur vers une fonction de type « void * fnct (void *) » et qui m'en renvoie un autre en retour, il faudrait écrire :
void * (*(*ptr)(void *(*)(void*)))(void *)
… ce qui est parfaitement imbuvable. Par contre, tu peux spécifier deux typedef. Le premier pour simplifier « void * fnct (void *) », qui est une fonction recevant un pointeur void et en renvoyant un autre, et le second pour écrire le type de la fonction qui reçoit un pointeur vers ce genre de fonction et qui en renvoie un autre également :
1 2 3 4
| typedef void * (VoidFunction) (void *);
typedef VoidFunction * (SpecialFunction) (VoidFunction *);
SpecialFunction *ptr, |
… ce qui est déjà plus clair.
Question en plus (tant que tu m'envoies pas sur les roses je reste!! lol):
Je ne comprends pas bien la déclaration suivante:
typedef int (*test_fp_t)(char**, int, char const*, ...);
En lisant juste cette ligne un programmeur identifie tout de suite qu'il s'agit d'un tableau? si oui cela provient du fait du dernier parenthésage?
Non, ce n'est pas un tableau, mais un pointeur de fonction. En C, les tableaux sont toujours représentés avec des crochets « [ ] ».
« test_fp_t » est le nom du pointeur. Il pointe en mémoire une fonction variadique (ie: au nombre variable d'arguments) mais renvoyant toujours un int.
Concernant la ligne3:
fferror_t function_factory( int *nbfunctions, (test_fp_t[]) *functions);
tu juxtaposes le code d'erreur fferror_t et la fonction function_factory => le compilateur identifie tout de suite que fferror_t est le code d'erreur associé à la fonction function_factory?
Non plus. Il s'agit là d'une déclaration de fonction ordinaire. « fferror_t » est donc un nom de type, défini là encore avec « typedef ». La convention veut que les symboles se finissant en « _t » soient des noms de types pour qu'on les reconnaissent, même si le compilateur ne t'oblige en rien à suivre cette règle.
La plupart du temps, ces types correspondront tous à de simples « int » ou « unsigned int ». Le fait de leur donner un nom explicite a deux avantages :
- Cela réintroduit un peu de sémantique : ça permet de savoir à l'avance si le numéro qu'on va lire est une taille, un code d'erreur, un booléen, une position quelconque, ou autre chose encore ;
- Ça permet d'adapter automatiquement le format de l'entier utilisé à l'architecture sur laquelle on travaille.
Par exemple, strlen(), qui te donne la taille d'une chaîne de caractères, te renvoie un « size_t ». C'est un nom explicite et comme la seule limite d'une chaîne de caractère est l'espace mémoire qui la contient, c'est tout naturellement que l'on va choisir un entier capable de représenter sa capacité. On peut raisonnablement penser que le fichier « types.h » ou l'un de ses dérivés va donc faire un « typedef unsigned int size_t » sur une machine 32 bits et un « typedef unsigned long int size_t » sur une machine 64 bits.
Pour finir, décortiquons une fois de plus ton expression initiale :
1. « sgInstanceTable » est un nom de variable, et plus précisément une instance d'une structure. Son nom et son contenu sont définis ailleurs dans ton code. Nous, on ne les connaît pas, mais ça ne nous empêche pas de poursuivre ;
sourceInfo = &((SourceInstance*)sgInstanceTable.itemTable[index])->sourceInfo;
2. « itemTable » est l'un des champs de cette structure. Puisque l'on y met des crochets, c'est vraisemblablement un tableau ;
sourceInfo = &((SourceInstance*)sgInstanceTable.itemTable[index])->sourceInfo;
3. « index » est un nom de variable. Le type de cette variable est un entier. La variable elle-même est définie, elle aussi, quelque part dans le code et est complètement indépendante, en elle-même, de l'expression qui nous intéresse ou de la structure examinée ici. C'est juste qu'elle contient le numéro de la « case » du tableau qui nous intéresse ;
sourceInfo = &((SourceInstance*)sgInstanceTable.itemTable[index])->sourceInfo;
4. Cette expression nous permet donc de récupérer l'élément en question. Il s'agit vraissemblablement d'un pointeur. Ce pointeur est probablement de type « void * », idée confortée par le fait qu'une « table d'items » doit pouvoir contenir des éléments de différentes natures ;
sourceInfo = &((SourceInstance*)sgInstanceTable.itemTable[index])->sourceInfo;
5. Un nom de type seul (fût-ce un pointeur) entouré de parenthèses et précédant une valeur ou une variable quelconque est un transtypage ou cast. Ça sert à demander au compilateur de convertir la valeur en question dans le type de destination. En l'occurrence, on va convertir un pointeur supposé « void » vers un « SourceInstance * » explicite. Au niveau de sa valeur, ça ne change rien du tout : son adresse en mémoire reste rigoureusement identique. Par contre, ça permet d'indiquer ce que l'on pointe au compilateur ;
sourceInfo = &((SourceInstance*)sgInstanceTable.itemTable[index])->sourceInfo;
6.Cette expression entière retourne donc un pointeur vers une instance de la structure SourceInstance :
sourceInfo = &((SourceInstance*)sgInstanceTable.itemTable[index])->sourceInfo;
7. On déréférence ce pointeur pour aller directement pour se référer, dans la structure qu'il pointe, au champ « sourceinfo » ;
sourceInfo = &((SourceInstance*)sgInstanceTable.itemTable[index])->sourceInfo;
8. On récupère l'adresse en mémoire du champ en question, dans cette instance en particulier ;
sourceInfo = &((SourceInstance*)sgInstanceTable.itemTable[index])->sourceInfo;
9. On affecte cette valeur à une variable locale nommée également « sourceinfo » mais qui n'a rien à voir avec notre structure. Cette variable est autonome. Elle n'est pas non plus membre d'une autre structure. Faire cela nous permettra de mettre directement ce champ à jour sans avoir à re-spécifier l'expression entière. C'est utile si le code qui va exploiter cette adresse est utilisé pour mettre à jour d'autres structures construites de la même façon.
sourceInfo = &((SourceInstance*)sgInstanceTable.itemTable[index])->sourceInfo;
À noter enfin la priorité des opérateurs utilisés. Dans l'ordre :
— Les crochets : « [ ] », puis
— L'opérateur de sélection d'un membre « . », puis
— L'opérateur de sélection d'un membre avec déréférencement « -> », puis
— L'opérateur d'adresse « & », puis
— L'opérateur de transtypage « (…) », et enfin
— L'opérateur d'affectation « = ».
Lorsque ces priorités correspondent bien à l'ordre des opérations que tu veux effectuer, pas de problème. Sinon, il faut mettre des parenthèses, comme en algèbre.
Comme « -> » est prioritaire par rapport à « & » et que tout le reste est entre parenthèses, le « & » est la dernière chose évaluée avant l'affectation et tu es sûr que c'est bien l'adresse du membre « sourceInfo » que tu récupères, et pas celle d'autre chose.
Partager