Plus précisément, quel est le danger de lancer le résultat de malloc?

Maintenant, avant que les gens commencent à marquer cela comme un dup, j’ai lu tous les éléments suivants, dont aucun ne fournit la réponse que je recherche:

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

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

Maintenant donné le code suivant:

 #include  #include  // #include  int main(int argc, char** argv) { char * p = /*(char*)*/malloc(10); strcpy(p, "hello"); printf("%s\n", p); return 0; } 

Comstackr le code ci-dessus avec gcc 4.2, avec et sans la dissortingbution, donne les mêmes avertissements et le programme s’exécute 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 

Donc, 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 la valeur de retour de malloc , ou s’agit-il simplement d’une légende urbaine?

Modifier J’ai rencontré deux arguments bien écrits concernant ce problème:

  1. En faveur du casting: CERT Advisory: Lance immédiatement le résultat d’un appel de fonction d’allocation de mémoire dans un pointeur sur le type alloué
  2. Contre la diffusion (erreur 404 au 14/02/2012: utilisez la copie de la machine de recopie Internet Archive à partir du 27/01/2010. {2016-03-18: “La page ne peut pas être explorée ou affichée à cause de robots.txt.”})

Vous ne recevrez pas d’ erreur de compilation , mais un avertissement du compilateur . Comme le disent les sources que vous citez (en particulier la première ), vous pouvez obtenir une erreur d’exécution imprévisible lorsque vous utilisez la dissortingbution sans inclure stdlib.h .

Donc, l’erreur de votre côté n’est pas la dissortingbution, mais en oubliant d’inclure stdlib.h . Les compilateurs peuvent supposer que malloc est une fonction renvoyant int , convertissant donc le pointeur void* réellement retourné par malloc en int et ensuite en votre type de pointeur en raison de la conversion explicite. Sur certaines plates-formes, int et les pointeurs peuvent prendre différents nombres d’octets, de sorte que les conversions de type peuvent entraîner une corruption des données.

Heureusement, les compilateurs modernes donnent des avertissements qui indiquent votre erreur réelle. Voir la sortie de gcc vous avez fournie: Elle vous avertit que la déclaration implicite ( int malloc(int) ) est incompatible avec le malloc intégré. Donc, gcc semble connaître malloc même sans stdlib.h .

Laisser de côté la dissortingbution pour éviter cette erreur est principalement le même raisonnement que l’écriture

 if (0 == my_var) 

au lieu de

 if (my_var == 0) 

car ce dernier pourrait conduire à un bogue sérieux si on confondait = et == , alors que le premier entraînerait 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.

La même chose est vraie pour la conversion de la valeur renvoyée par malloc : je préfère être explicite dans la programmation et je vérifie généralement en double pour inclure les fichiers d’en-tête pour toutes les fonctions que j’utilise.

L’un des arguments de haut niveau contre le lancement du résultat de malloc n’est souvent pas mentionné, même si, à mon avis, il est plus important que les problèmes de bas niveau bien connus (comme tronquer le pointeur lorsque la déclaration est manquante) .

Une bonne pratique de programmation consiste à écrire du code, qui est aussi indépendant du type que possible. Cela signifie, en particulier, que les noms de type doivent être mentionnés dans le code aussi peu que possible ou qu’ils ne sont pas du tout mentionnés. Cela s’applique aux moulages (évitez les jets inutiles), les types en tant qu’arguments de sizeof (évitez d’utiliser des noms de type dans sizeof ) et, en général, toutes les autres références aux noms de type.

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 sharepoint vue, ce bout de code est mauvais

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

et c’est beaucoup mieux

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

pas simplement parce qu’il “ne lance pas le résultat de malloc “, mais plutôt parce qu’il est indépendant du type (ou du type agnositique, si vous préférez), car il s’adapte automatiquement au type avec lequel p est déclaré, sans nécessiter de intervention de l’utilisateur.

Les fonctions non prototypées sont supposées renvoyer int .

Donc, vous lancez un int dans un pointeur. Si les pointeurs sont plus larges que int sur votre plate-forme, il s’agit d’un comportement très risqué.

De plus, certaines personnes considèrent que les avertissements sont des erreurs, c’est-à-dire que le code doit être compilé sans eux.

Personnellement, je pense que le fait que vous n’ayez pas besoin de convertir void * en un autre type de pointeur est une fonctionnalité de C, et considérez que le code doit être rompu.

Si vous faites cela lors de la compilation en mode 64 bits, votre pointeur renvoyé sera tronqué à 32 bits.

EDIT: Désolé d’être trop bref. Voici un exemple de fragment de code à des fins de discussion.

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

Supposons que le pointeur de segment renvoyé soit plus gros que ce qui est représentable dans un int, par exemple 0xAB00000000.

Si malloc n’est pas prototypé pour renvoyer un pointeur, la valeur int renvoyée sera initialement dans un registre avec tous les bits significatifs définis. Maintenant, le compilateur dit, “okay, comment puis-je convertir et int en un pointeur”. Cela va être soit une extension de signe, soit une extension zéro des 32 bits de faible poids qui ont été annoncés par malloc “en retournant” en omettant le prototype. Puisque int est signé, je pense que la conversion sera une extension de signe, qui dans ce cas convertira la valeur à zéro. Avec une valeur de retour de 0xABF0000000, vous obtiendrez un pointeur différent de zéro, ce qui entraînera également du plaisir lors de la tentative de dérogation.

Une règle de logiciel réutilisable:

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

Un pointeur vide dans C peut être affecté à n’importe quel pointeur sans transtypage explicite. Le compilateur donnera un avertissement mais il peut être réutilisable en C ++ par type casting malloc() au type correspondant. Avec out-casting, il peut également être utilisé en C , car C n’est pas une vérification de type ssortingcte . Mais C ++ est ssortingctement une vérification de type , il est donc nécessaire de taper cast malloc() en C ++.