Contrôle de type sur les macros

Contrôle de type sur les macros - C - Programmation

Marsh Posté le 23-03-2007 à 09:35:36    

Bonjour
 
Je peux lire dans beaucoup de documentations que les macros sont dangereuses parce qu'il n'y a aucun contrôle de type effectué dessus. Je ne comprends pas pourquoi  :??:  
Si j'ai bien lu le principe des macros, c'est du simple remplacement de texte effectué par le préprocesseur avant compilation. Si je remplace moi même à la main mes appels de macro par le code correspondant, le compilateur effectuera bien un contrôle sur les types ?
Où est la différence? :heink:  
Où fais je l'erreur?

Message cité 1 fois
Message édité par lafourchette le 23-03-2007 à 09:41:11
Reply

Marsh Posté le 23-03-2007 à 09:35:36   

Reply

Marsh Posté le 23-03-2007 à 14:01:34    

lafourchette a écrit :

Je peux lire dans beaucoup de documentations que les macros sont dangereuses parce qu'il n'y a aucun contrôle de type effectué dessus. Je ne comprends pas pourquoi  :??:  
Si j'ai bien lu le principe des macros, c'est du simple remplacement de texte effectué par le préprocesseur avant compilation. Si je remplace moi même à la main mes appels de macro par le code correspondant, le compilateur effectuera bien un contrôle sur les types ?
Où est la différence? :heink:  
Où fais je l'erreur?


Ca pose problème dans les pseudo fonctions avec paramètres. Les paramètres des macros ne sont pas typés et font simplement un remplacement de texte.

Code :
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. /* 0|1 */
  4. #if 1
  5. #define str_to_double(s, p)\
  6.    *p = strtod(s, NULL)
  7. #else
  8. void str_to_double(char const *s, double *p)
  9. {
  10.    *p = strtod(s, NULL);
  11. }
  12. #endif
  13. int main (void)
  14. {
  15.    int x;
  16.    char line[] = "1234.56";
  17.    str_to_double (line, &x);
  18.    printf ("%d\n", x);
  19.    return 0;
  20. }


Avec la macro : pas de warning
Avec la fonction : warning


---------------
Des infos sur la programmation et le langage C: http://www.bien-programmer.fr Pas de Wi-Fi à la maison : http://www.cpl-france.org/
Reply

Marsh Posté le 24-03-2007 à 13:15:02    

Ainsi présenté, c'est déjà plus lumineux.  :jap:  
 
Dans le cas 0, le cas de la fonction, j'ai effectivement un warning:
 [Warning] passing arg 2 of `str_to_double' from incompatible pointer type  
Le compilateur à, je suppose, constaté  qu'à l'appel de la fonction "str_to_double", les paramètres effectifs divergeaient par rapport aux paramètres formels.
Mais je peux executer le programme et j'obtiens:
1889785610 Résultat sans doute sans signification intinsèque mais qui doit pouvoir s'expliquer.  
 
Dans le cas 1, le cas de la macro, tout se passe bien et j'obtiens:
1234 Résultat que cette fois ci je peux expliquer.  :D  
 
Par contre, dans les deux cas j'ai commis à la lecture du code, une erreur  de débutant et j'espère que je suis pas le seul:  :(  
J'étais persuadé que le compilateur m'enverrait un petit avertissement de principe pour essayer d'affecter le contenu d'une variable de type double à une variable de type int.

Reply

Marsh Posté le 24-03-2007 à 19:16:02    

lafourchette a écrit :

J'étais persuadé que le compilateur m'enverrait un petit avertissement de principe pour essayer d'affecter le contenu d'une variable de type double à une variable de type int.


Où vois-tu une affectation de double vers int ? strtod() renvoie un double, "p" étant de type "double étoile" il s'ensuit que "*p" est de type "double" donc en affectant "strtod()" à "*p" tout est correct...
 

lafourchette a écrit :

Mais je peux executer le programme et j'obtiens:
1889785610 Résultat sans doute sans signification intinsèque mais qui doit pouvoir s'expliquer.


De façon très facile: la fonction "strtod" à laquelle tu passes un pointeur (une adresse) ira stocker à cette adresse les 8 octets correspondant à une valeur au format "double" qu'elle calcule, valeur codée sur 8 octets (selon les spécifications du codage des double avec la mantisse, l'exposant, etc...).
 
Toi, tu lui passes l'adresse d'un "int" qui est convertie en adresse de double par le cast implicite. Une adresse étant toujours une adresse et étant considérée comme pointant sur 8 octets au format "double", la fonction va donc stocker à cette adresse les 8 octets du double (ici risque de plantage car il est possible que les 4 octets suivants le "int" ne soient pas disponibles mais passons).
Au retour de la fonction, quand tu affiches ton "int", ça récupère les 4 premiers octets de la zone mémoire correspondante, les décode selon les spécifications du codage des "int" (1 bit de signe, 31 bits de valeur) et t'affiche le résultat.
Donc t'as codé en spécification "double" et tu décodes en spécifications "int". Déjà que les spécifications ne sont pas les mêmes et en plus tu n'en décodes que la moitié de ce qui a été mis. C'aurait été miraculeux que tu obtiennes "1234"...

Message cité 1 fois
Message édité par Sve@r le 24-03-2007 à 19:40:00

---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 26-03-2007 à 15:37:41    

Sve@r a écrit :


 
De façon très facile: la fonction "strtod" à laquelle tu passes un pointeur (une adresse) ira stocker à cette adresse les 8 octets correspondant à une valeur au format "double" qu'elle calcule, valeur codée sur 8 octets (selon les spécifications du codage des double avec la mantisse, l'exposant, etc...).
 
Toi, tu lui passes l'adresse d'un "int" qui est convertie en adresse de double par le cast implicite. Une adresse étant toujours une adresse et étant considérée comme pointant sur 8 octets au format "double", la fonction va donc stocker à cette adresse les 8 octets du double (ici risque de plantage car il est possible que les 4 octets suivants le "int" ne soient pas disponibles mais passons).
Au retour de la fonction, quand tu affiches ton "int", ça récupère les 4 premiers octets de la zone mémoire correspondante, les décode selon les spécifications du codage des "int" (1 bit de signe, 31 bits de valeur) et t'affiche le résultat.
Donc t'as codé en spécification "double" et tu décodes en spécifications "int". Déjà que les spécifications ne sont pas les mêmes et en plus tu n'en décodes que la moitié de ce qui a été mis. C'aurait été miraculeux que tu obtiennes "1234"...


 :jap:  

Citation :

Où vois-tu une affectation de double vers int ? strtod() renvoie un double, "p" étant de type "double étoile" il s'ensuit que "*p" est de type "double" donc en affectant "strtod()" à "*p" tout est correct...  


C'est dans le cas de la macro en fait que j'ai un probleme.
Si je fais l'expansion de la macro à son appel j'obtiens
*(&x) = strtod("1234.56", NULL); soit
x = strtod("1234.56", NULL); x étant bien une variable de type int, non  :??:  
Sur deux compilateurs différents, j'ai essayé d'affecter une constante décimale à un int.
Seul le second m'a mis un petit warning  :??:  
 

Reply

Marsh Posté le 26-03-2007 à 21:08:42    

lafourchette a écrit :


x = strtod("1234.56", NULL); x étant bien une variable de type int, non  :??:  
Sur deux compilateurs différents, j'ai essayé d'affecter une constante décimale à un int.
Seul le second m'a mis un petit warning  :??:


 
Mouais. Tu peux essayer d'écrire "int x=2.5" voir si ton compilo te mettra un warning. En général, cela tombe dans le cadre du cast implicite...


---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 27-03-2007 à 12:56:50    

Tu peux contrôler les types en faisant des affectations dans ta macro :

Code :
  1. #define MA_MACRO(A, B, B) do { \
  2.         int a = A; \
  3.         long b = B; \
  4.         char *c = C; \
  5.         ...
  6. } while (0)

Reply

Marsh Posté le 27-03-2007 à 14:43:01    

matafan a écrit :

Tu peux contrôler les types en faisant des affectations dans ta macro :

Code :
  1. #define MA_MACRO(A, B, C) do { \
  2.         int a = A; \
  3.         long b = B; \
  4.         char *c = C; \
  5.         ...
  6. } while (0)



Pourquoi faire une boucle qui ne bouclera pas ???

Code :
  1. #define MA_MACRO(A, B, C) { \
  2.         int a = A; \
  3.         long b = B; \
  4.         char *c = C; \
  5.         ... \
  6. }



---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 27-03-2007 à 14:44:55    

Sve@r a écrit :

Pourquoi faire une boucle qui ne bouclera pas ???

Code :
  1. #define MA_MACRO(A, B, C) { \
  2.         int a = A; \
  3.         long b = B; \
  4.         char *c = C; \
  5.         ... \
  6. }



Pour forcer l'usage du ';' en fin de macro. C'est une vielle ficelle de métier, je suis étonné que tu ne la connaisses pas...
 


---------------
Des infos sur la programmation et le langage C: http://www.bien-programmer.fr Pas de Wi-Fi à la maison : http://www.cpl-france.org/
Reply

Marsh Posté le 27-03-2007 à 14:50:21    

Emmanuel Delahaye a écrit :

Pour forcer l'usage du ';' en fin de macro. C'est une vielle ficelle de métier, je suis étonné que tu ne la connaisses pas...


Joli. En plus, je m'étais même dit "tiens il a oublié le ';' après son while mais je ne vais pas lui faire remarquer un truc qui n'est qu'une étourderie" alors que je m'aperçois que c'était voulu ! Bien vu  :bounce:  
 

Emmanuel Delahaye a écrit :

C'est une vielle ficelle de métier, je suis étonné que tu ne la connaisses pas...


Héhé... on ne peut pas tout connaître...  :sol:  


---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 27-03-2007 à 14:50:21   

Reply

Marsh Posté le 27-03-2007 à 14:52:29    

matafan a écrit :

Tu peux contrôler les types en faisant des affectations dans ta macro :

Code :
  1. #define MA_MACRO(A, B, B) do { \
  2.         int a = A; \
  3.         long b = B; \
  4.         char *c = C; \
  5.         ...
  6. } while (0)



 
Quand on en est à créer un bloc avec son scope, et des variables pour définir le type, on s'approche quand même vachement d'une fonction, il reste quoi comme raison pour ne pas en utiliser une directement ?


---------------
Me: Django Localization, Yogo Puzzle, Chrome Grapher, C++ Signals, Brainf*ck.
Reply

Marsh Posté le 27-03-2007 à 15:21:53    

0x90 a écrit :

Quand on en est à créer un bloc avec son scope, et des variables pour définir le type, on s'approche quand même vachement d'une fonction, il reste quoi comme raison pour ne pas en utiliser une directement ?


Next step : inline !
 


---------------
Des infos sur la programmation et le langage C: http://www.bien-programmer.fr Pas de Wi-Fi à la maison : http://www.cpl-france.org/
Reply

Marsh Posté le 27-03-2007 à 18:05:48    

Le "do { } while (0)", c'est surtout pour permettre d'utiliser la macro dans un if/else sans bloc, comme on le ferait avec une fonction. Sans le do/while ça donnerait après expansion :

Code :
  1. if (...)
  2.         { ... }; /* Le point virgule est en fait une instruction vide */
  3. else ...


Ce qui ne compile pas puisqu'on a un else sans if. Avec le do/while, ça donne :

Code :
  1. if (...)
  2.         do { ... } while (0); /* Le point virgule fini la mono-instruction du if */
  3. else ...


Et ça c'est bon.

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed