Pourquoi?
Version imprimable
Voici quelques explications:
- C'est inutile!
- En cas d'oubli de l'inclusion de stdlib.h, la malloc() n'est pas déclarée. Dans ce cas, le compilateur suppose que malloc() retourne un entier de type int. Or, la conversion implicite d'un entier de type int en un pointeur n'est pas légale et le compilateur renvoie une erreur. Par contre, si une conversion explicite est effectuer, le compilateur comprendra "je sais que je fais quelque chose de pas catholique, mais je le sais" et ne renverra pas d'erreur. On se prive ainsi qu'une possibilité de vérification automatique par le compilateur.
- Plus grave (toujours en cas d'oubli de stdlib.h), sur les systèmes 64 bits où un pointeur est représenté sur 64 bits et un int sur 32, la valeur retournée par malloc(), considérée comme un entier de type int par le compilateur, sera tronquée à 32 bits.
Thierry
C'est bien là le problème, et pas la présence ou l'absence de cast. Il ne faut pas appeler une fonction non-déclarée, qu'on utilise la valeur de retour comme un entier, ou qu'on l'ignore, enfin qu'on suppose ou pas que c'est un pointeur.
Ou alors il faut bannir toutes les fonctions qui renvoient un entier, quelque chose qui se converti implicitement en entier, ou dont la valeur de retour est généralement ignorée.
La solution est de demander au compilateur de raller dès qu'il voit un appel à une fonction non déclarée. Et laisser malloc tranquille.
En effet, l'erreur décrite par Thierry est généralement due à une combinaison de facteurs (cast + oubli d'inclusion + oubli des warnings).
Mais ce n'est pas si difficile que ça à obtenir: Après avoir grandi avec des EDI, j'ai moi-même appris à mes dépens que gcc en ligne de commande n'affiche pas de warnings à moins qu'on les lui demande explicitement...
Mais si le fait de ne pas mettre de cast permet de la détecter, alors que le cast n'est pas requis et n'aide en rien, c'est une bonne chose. Pourquoi vouloir s'en priver ?
Ton « ou alors » n'a pas de sens pour le développeur. Ce n'est pas lui qui choisi la spécification du langage.
Par contre, c'est clair qu'il faudrait penser à toujours demander le maximum de warning en phase de développement. C'est clair que j'ai toujours trouvé qu'il serait plus intelligent que le compilateur donne tous les avertissements par défaut. Dans le cas où l'utilisateur les trouve génant, ce serait explicitement qu'il activerait une option de limitation. Mais ce n'est pas ainsi.
et pourquoi le "déconseiller", et même "fortement" comme c'est dit dans plusieurs posts et/ou threads ??
Je trouve que ces arguments sont un peu faibles, car en fait cela montre les déficiences du compilo, pas du programme ou de l'utilisation du langage...
Et d'abord le "cast" peut être requis dans plusieurs utilisations (passages de paramètres à une fonction par exemple, où le -pedantic -Wall gueulera qu'il n'y a pas le bon type de paramètres..).
Et donc mettre au rencart une instruction partiellement me semble plus dangereux...
?Citation:
Et d'abord le "cast" peut être requis dans plusieurs utilisations (passages de paramètres à une fonction par exemple, où le -pedantic -Wall gueulera qu'il n'y a pas le bon type de paramètres..).
Tu pourrais préciser et mettre un exemple ? J'ai du mal à voir de quoi tu veux parler, et je n'ai pas de gcc sous la main non plus...
bah plein de cas, dès que tu as par exemple une structure d'un certain type, même en la passant comme pointeur, et que tu passes une structure équivalente, mais d'un autre nom que celui utilisé dans la déclaration du prototype...
Ou une variable short que tu veux passer à une fonction attendant un int, etc etc....
Un compilateur bien réglé te jettera si tu ne mets pas de cast...
Et donc, si je suis ce que vous dites, il faut déconseiller l'usage du cast, mais il sera demandé dans certaines conditions :aie: pas évident comme notion quand on débute...
Et quand on ne débute plus, ben l'inconvénient cité n'est plus important, car on sait ce qu'on fait..
Exemple :
Un compilateur bien réglé te jettera ... car Fils != PereCode:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 typedef struct pPere { int age ; int nb_Enfs ; char *Nom ; } Pere ; typedef struct pFils { int age ; int nb_Enfs ; char *Nom ; } Fils ; void Affiche_Pere ( Pere *Parent ); ... void Fonction ( void ) { Fils Enf ; /* Appel */ Affiche_Pere ( &Enf); }
peut-être que cela marche pour short <-> int (je n'en suis pas certain cependant, car je travaille sur des codes où c'est casté et si on enlève le cast le compilo gueule), mais en tous cas avec les exemples donnés plus haut, dès qu'il y a structure ou autre, il faut caster...
je suis pas sur la machine, là, mais qquelque chose comme "call does not correspond to prototype" ...
Avec les flags de réenforcement des protos stricts.
Bref, ceci n'est qu'un à côté. Le principal est que pour les variables, structures et pointeurs en particuliers, le compilo bien réglé te jettera, et cela nécessitera le cast.
Or donc, il y aura 2 notions à indiquer : une qui est que le cast est déconseillé, l'autre qu'il faut le mettre... Bof... Pas très pédagogique... Et surtout générateur d'erreurs (ou plutôt de "légéretés" plus tard).
Je ne suis pas contre, entendez-moi.. Ce que je dis, c'est que je suis étonné de la virulence avec laquelle on "déconseille" son usage... Et que les arguments ne me semblent pas logiques...
souviron34: Aucun des cas que tu as décris n'a à voir avec le cast explicite d'un void*.
Justement, ce qu'un pédagogue voudra faire passer, c'est que l'important, c'est de comprendre le typage et de savoir quand il faut mettre un cast.
Sinon, c'est très facile de coller des casts dès que le compilateur râle à propos d'un problème de type, sans essayer de comprendre pourquoi il râle. C'est la plus rapide méthode pour faire des programmes qui compilent mais ne fonctionnent pas (ça, et les mauvaises manipulations de pointeurs).
Voir pleins de casts dans un programme est souvent indicatif d'une mauvaise programmation (ou alors, qu'on ne peut pas faire autrement).
que ce soit dans C99, ok.
Mais par exemple dans K&R :
ouCode:
1
2
3
4
5
6
7 #include <stdlib.h> /* talloc: make a tnode */ struct tnode *talloc(void) { return (struct tnode *) malloc(sizeof(struct tnode)); }
pour n'en citer que 2...Code:
1
2
3
4
5
6
7
8
9 char *strdup(char *s) /* make a duplicate of s */ { char *p; p = (char *) malloc(strlen(s)+1); /* +1 for '\0' */ if (p != NULL) strcpy(p, s); return p; }
Bref, de toutes façons, ce que je dis c'est que les arguments ne sont pas logiques....
Encore une fois, qui en dehors des débutants oublie stdlib.h dans un programme ?????????????????????Citation:
En cas d'oubli de l'inclusion de stdlib.h, la malloc() n'est pas déclarée. Dans ce cas, le compilateur suppose que malloc() retourne un entier de type int. Or, la conversion implicite d'un entier de type int en un pointeur n'est pas légale et le compilateur renvoie une erreur. Par contre, si une conversion explicite est effectuer, le compilateur comprendra "je sais que je fais quelque chose de pas catholique, mais je le sais" et ne renverra pas d'erreur. On se prive ainsi qu'une possibilité de vérification automatique par le compilateur.
Plus grave (toujours en cas d'oubli de stdlib.h), sur les systèmes 64 bits où un pointeur est représenté sur 64 bits et un int sur 32, la valeur retournée par malloc(), considérée comme un entier de type int par le compilateur, sera tronquée à 32 bits.
Tu aurais pu prendre l'exemple des adresse socket :
Code:
1
2
3
4
5 struct sockaddr { unsigned char sa_len; /* longueur totale */ sa_family_t sa_family; /* famille d'adresse */ char sa_data[14]; /* valeur de l'adresse */ };
La structure assez particulière fait qu'il est nécessaire de caster les sockaddr_in en sockaddr, par exemple avec :Code:
1
2
3
4
5
6
7 struct sockaddr_in { uint8_t sin_len; /* longueur totale */ sa_family_t sin_family; /* famille : AF_INET */ in_port_t sin_port; /* le numéro de port */ struct in_addr sin_addr; /* l'adresse internet */ unsigned char sin_zero[8]; /* un champ de 8 zéros */ };
Code:int connect(int sockfd, struct sockaddr *serv_addr, socklen_t addrlen);
Souviron34 : Comme Emmanuel l'a souvent souligné, il faut lire tout le K&R, y compris les errata.
Citation:
Envoyé par http://cm.bell-labs.com/cm/cs/cbook/2ediffs.html
bon j'arrêterais là, mais même dans ce que tu cites :
Je reviens encore une dernière fois :Citation:
possibly harmful if malloc, or a proxy for it, fails to be declared as returning void *.
qui en dehors des débutants oublie stdlib.h dans un programme ?????????????????????
8O
Ce qui est le seul argument.... (or stdlib déclare comme void *)
Donc....
:P
Non, l'autre argument est que c'est inutile, et un troisième est que ça a l'avantage de ne pas compiler en C++.
Ou plutôt, on met l'accent sur le fait que ce soient deux langages différents. Et ainsi, on peut éviter que quelqu'un fasse un bête copier-coller du code et tente de le compiler sur un compilateur C++ alors qu'il n'est pas fait pour ça...
Tu dois être très très bien placé par ton expérience pour savoir que les erreurs d'inattention peuvent survenir chez n'importe qui non ? Et qu'il y a des domaines où on ne veut même pas prendre ce risque.
Et l'argument de la « non compilation en C++ » est aussi une bonne raison.
si on programme pour C, on s'en fiche de ce qui se passe en C++ :P
Et pour le reste, je suis bien place pour dire que dans tous les projets sur lesquels j'ai travaille depuis plus de 19 ans, il y a un fichier "Project_Common_C.h" qui inclus tous les entetes standards utiles en general (stdio, stdlib, time, math et quelques autres), qui est a inclure dans tout fichier :P
Donc je refute ces arguments ;)
Absence de cast de void* en X*, en particulier sur le résultat de malloc.
C'est idiomatique mais à part cela, il n'y a pas de très bonnes raisons
fondamentale. La plus profonde étant peut-être une volonté d'écrire
quelque chose qui ne compile pas en C++, les autres tiennent plus de la
rationalisation que d'autre chose.
Deux fois la même raison -- si on n'a pas le prototype, on peut avoir des
- Ca reste à justifier. Il y a un effet de documentation pas négligeable.
Citation:- En cas d'oubli de l'inclusion de stdlib.h, la malloc() n'est pas
déclarée. Dans ce cas, le compilateur suppose que malloc() retourne un
entier de type int. Or, la conversion implicite d'un entier de type int en
un pointeur n'est pas légale et le compilateur renvoie une erreur. Par
contre, si une conversion explicite est effectuer, le compilateur
comprendra "je sais que je fais quelque chose de pas catholique, mais je le
sais" et ne renverra pas d'erreur. On se prive ainsi qu'une possibilité de
vérification automatique par le compilateur.- Plus grave (toujours en
cas d'oubli de stdlib.h), sur les systèmes 64 bits où un pointeur est
représenté sur 64 bits et un int sur 32, la valeur retournée par malloc(),
considérée comme un entier de type int par le compilateur, sera tronquée à
32 bits.
problèmes. La volonté de vouloir détecter ce problème est louable, la
méthode douteuse et peu universelle.
Formellement indéfini...
Cela n'a jamais été nécessaire (sans prototype, il y a de toute manièreCitation:
Ou une variable short que tu veux passer à une fonction attendant un
int, etc etc....
promotion, avec prototype, la conversion est faite).
Tu peux donner l'exemple exact après avoir vérifier que ça gueule? Je ne
vois aucune raison.
La probabilité qu'il y a un bug dans le code ou le compilateur est
supérieur à celle qu'un code C qu'en compilateur C++ compile sans problème
a un comportement différent compilé par le compilateur C++ et par le
compilateur C.
Pour ma part, au sujet du cast de la valeur de retour de malloc:
* l'argument "inutile" est cohérent, même si la remarque Jean-Marc.Bourguet sur l'effet 'documentation' est valable
* l'argument "dangeureux" en cas d'oubli d'inclusion de sdtlib.h n'est pas valable. Un compilo bien réglé ne peux laisser passer ca.
J'ai du mal à imaginer qu'un développeur sérieux puisse livrer ou mettre en prod des applis compilées avec un compilo si mal réglé.
Je pense que fondamentalement, la seule raison qui fasse déconseiller le cast de la valeur de retour de malloc en C est de rendre le source incompilable en C++, hormis le fait que le cast est intrinsèquement inutile !
Tu veux dire que jamais personne autour de toi ne fait d'erreurs dues à la fatigue ou à l'inattention temporaire ? Je t'assure que tu es statistiquement une exception très rare alors. La majorité des erreurs sont dues à des inattentions. Et ils sont en général vite corrigé grâce à la compilation. Rien de grave donc.
L'argument de l'effet documentaire du cast est le seul qui me parle (un peu).
Thierry
Oui.
Si je veux qu'un code puisse compiler aussi bien en C qu'en C++, je le conçois exprès pour ça et j'utilise pas mal de macros (notamment pour les casts).
Dans ce cas, je ne comprends pas la question. Peux-tu l'expliciter ?
Pourquoi vouloir volontairement que quelque chose ne compile pas en C++?
(Sur le sujet, je ne caste pas le resultat de malloc en C parce que c'est idiomatique, mais c'est la seule raison et je continue a trouver que la convertion implicite de void* en T* est une horreur.)
OK, je comprends la question maintenant. Eh bien, c'est partiellement par idéologie (C et C++ sont des langages différents), et partiellement par paresse (si je décide dès le début que mon prog ne compilera pas en C++, je n'ai pas à me soucier des différences plus subtiles).
Edit: Et aussi parce que je n'aime pas les casts C-Style et j'essaie de les éviter quand c'est possible. D'où mon emploi, quand je veux du compatible, de macros comme void_cast(type,p), qui donne juste p en C et un static_cast<> en C++...
oui je dois dire pas mal d'accord avec Jean-Marc et Vicenzo....
Et je réitère : nous sommes en C. Que viennent faire des considérations sur C++ ici ???