95 votes

Plus précisément, qu'est-ce qui est dangereux dans le fait de couler le résultat de malloc ?

Avant que les gens ne commencent à considérer cela comme un double, j'ai lu tout ce qui suit, mais aucun ne fournit la réponse que je cherche :

  1. C FAQ : Quel est le problème avec le casting de la valeur de retour de malloc ?
  2. SO : Dois-je exprimer explicitement la valeur de retour de malloc() ?
  3. SO : Pointeurs-cast inutiles en C
  4. SO : Est-ce que je jette le résultat de malloc ?

La FAQ C et de nombreuses réponses aux questions ci-dessus citent une erreur mystérieuse qui coule malloc Cependant, aucun d'entre eux ne donne un exemple spécifique d'une telle erreur dans la pratique. Maintenant, faites attention que j'ai dit erreur pas avertissement .

Maintenant, étant donné le code suivant :

#include <string.h>
#include <stdio.h>
// #include <stdlib.h>

int main(int argc, char** argv) {

    char * p = /*(char*)*/malloc(10);
    strcpy(p, "hello");
    printf("%s\n", p);

    return 0;
}

La compilation du code ci-dessus avec gcc 4.2, avec et sans le cast donne les mêmes avertissements, et l'exécution s'effectue correctement et fournit les mêmes résultats dans les deux cas.

anon@anon:~/$ gcc -Wextra nostdlib_malloc.c -o nostdlib_malloc
nostdlib_malloc.c: In function ‘main’:
nostdlib_malloc.c:7: warning: incompatible implicit declaration of built-in function ‘malloc’
anon@anon:~/$ ./nostdlib_malloc 
hello

Quelqu'un peut-il donner un exemple de code spécifique d'une erreur de compilation ou d'exécution qui pourrait se produire à cause de l'encodage ? malloc ou s'agit-il d'une légende urbaine ?

Editar Je suis tombé sur deux arguments bien écrits concernant cette question :

  1. En faveur du Casting : Avis du CERT : Transformez immédiatement le résultat d'un appel de fonction d'allocation de mémoire en un pointeur vers le type alloué.
  2. Contre le Casting

67voto

Ferdinand Beyer Points 27723

Vous n'aurez pas erreur de compilation mais un avertissement du compilateur . Comme le disent les sources que vous citez (notamment le premier ), vous peut obtenir un imprévisible erreur d'exécution lors de l'utilisation de la fonte sans inclure stdlib.h .

Donc l'erreur de votre côté n'est pas le casting, mais d'avoir oublié d'inclure stdlib.h . Les compilateurs peuvent supposer que malloc est une fonction qui renvoie int en convertissant donc le void* renvoyé par malloc a int et ensuite à votre type de pointeur en raison du cast explicite. Sur certaines plateformes, int et les pointeurs peuvent occuper des nombres d'octets différents, de sorte que les conversions de type peuvent entraîner une corruption des données.

Heureusement, les compilateurs modernes émettent des avertissements qui indiquent l'erreur réelle. Voir le gcc sortie que vous avez fournie : Il vous avertit que le implicite déclaration ( int malloc(int) ) est incompatible avec le système intégré malloc . Donc gcc semble savoir malloc même sans stdlib.h .

Le fait d'omettre la distribution pour éviter cette erreur revient à faire le même raisonnement que d'écrire

if (0 == my_var)

au lieu de

if (my_var == 0)

car ce dernier pourrait conduire à un bug sérieux si l'on confondait = y == alors que la première conduirait à une erreur de compilation. Personnellement, je préfère ce dernier style car il reflète mieux mon intention et je n'ai pas tendance à faire cette erreur.

Il en va de même pour le casting de la valeur retournée par malloc : Je préfère être explicite en programmation et je vérifie généralement deux fois pour inclure les fichiers d'en-tête pour toutes les fonctions que j'utilise.

47voto

AndreyT Points 139512

Un des bons arguments de haut niveau contre le casting du résultat de malloc n'est souvent pas mentionné, même si, à mon avis, il est plus important que les problèmes de niveau inférieur bien connus (comme la troncature du pointeur lorsque la déclaration est manquante).

Une bonne pratique de programmation consiste à écrire du code aussi indépendant du type que possible. Cela signifie, en particulier, que les noms de types doivent être mentionnés dans le code aussi peu que possible, voire pas du tout. Cela s'applique aux casts (évitez les casts inutiles), aux types en tant qu'arguments de la fonction sizeof (évitez d'utiliser les noms de type dans sizeof ) et, en général, toutes les autres références aux noms de types.

Les noms de type appartiennent aux déclarations. Dans la mesure du possible, les noms de types doivent être limités aux déclarations et uniquement aux déclarations.

De ce point de vue, ce bout de code est mauvais.

int *p;
...
p = (int*) malloc(n * sizeof(int));

et ceci est bien mieux

int *p;
...
p = malloc(n * sizeof *p);

non pas simplement parce qu'il "ne jette pas le résultat de malloc "mais plutôt parce qu'il est indépendant du type.

18voto

unwind Points 181987

Les fonctions non-prototypées sont supposées retourner int .

Donc vous lancez un int à un pointeur. Si les pointeurs sont plus larges que int sur votre plateforme, c'est un comportement très risqué.

En plus, bien sûr, que certaines personnes considèrent les avertissements comme étant être les erreurs, c'est-à-dire que le code devrait compiler sans elles.

Personnellement, je pense que le fait que vous n'avez pas besoin de lancer void * vers un autre type de pointeur est une fonctionnalité du C, et on considère que le code qui le fait est cassé.

11voto

Peeter Joot Points 1975

Si vous faites cela lorsque vous compilez en mode 64 bits, votre pointeur retourné sera tronqué à 32 bits.

EDIT : Désolé d'avoir été trop bref. Voici un exemple de fragment de code à des fins de discussion.

main()
{
   char \* c = (char \*)malloc(2) ;
   printf("%p", c) ;
}

Supposons que le pointeur de tas retourné soit quelque chose de plus grand que ce qui est représentable dans un int, disons 0xAB00000000.

Si malloc n'est pas prototypée pour retourner un pointeur, la valeur int retournée sera initialement dans un registre avec tous les bits significatifs activés. Maintenant le compilateur dit, "ok, comment puis-je convertir un int en un pointeur". Cela va être soit une extension de signe, soit une extension de zéro des 32 bits d'ordre inférieur qu'on lui a dit que malloc "retourne" en omettant le prototype. Puisque int est signé, je pense que la conversion sera l'extension du signe, ce qui dans ce cas convertira la valeur en zéro. Avec une valeur de retour de 0xABF0000000, vous obtiendrez un pointeur non nul qui causera également quelques problèmes lorsque vous essaierez de le déréférencer.

5voto

Test Points 1395

Une règle logicielle réutilisable :

Dans le cas de l'écriture d'une fonction inline dans laquelle est utilisée malloc(), afin de la rendre réutilisable pour le code C++ également, veuillez faire un casting de type explicite (par exemple (char*)) ; sinon le compilateur se plaindra.

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