Vérifier qu'un malloc s'est déroulé correctement

Résolu/Fermé
JeVeuxJusteEtreIngenieure - Modifié le 30 janv. 2023 à 14:08
sambia39 Messages postés 610 Date d'inscription vendredi 31 juillet 2009 Statut Membre Dernière intervention 9 février 2023 - 9 févr. 2023 à 12:52

Bonjour,

J'ai examen d'informatique dans quelques jours et j'ai beaucoup de mal avec le langage C.

struct Liste {
  int valeur;
  struct Liste *suivant;
}

struct Liste nouveau;
struct Liste tete;
tete=NULL;
nouveau = (Liste*)malloc(sizeof(struct Liste));
nouveau->suivant = tete;
tete = nouveau;

De manière systématique, comment peut-on vérifier q'une allocation dynamique s'est déroulée correctement ? Par exemple dans la situation d'une liste simplement liée :

Dois-je vérifier cela avec une boucle for ou une while ?

Merci pour votre aide !
Macintosh / Safari 15.6.1

A voir également:

6 réponses

PierrotLeFou
20 janv. 2023 à 18:27

La seule façcon de vérifier si le malloc s'est déroulé normalement est de vérifier que le pointeur retourné n'est pas NULL.

2
mamiemando Messages postés 33073 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 16 avril 2024 7 748
30 janv. 2023 à 14:19

Bonjour,

Par rapport au tout premier message :

  • N'oublie pas le ";" à la fin de la déclaration de la structure
  • Les instructions doivent toujours être dans une fonction

De manière générale, une allocation dynamique devrait ressembler à ceci (tu peux adapter cet exemple à ton code) :

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

int main() {
    int * tab;

    tab = malloc(sizeof(int) * 10);
    if (!tab) {
        fprintf(stderr, "Memory error");
    } else {
/*
        for (size_t i = 0; i < 10; i++) {
            tab[i] = i;
        }   
        for (size_t i = 0; i < 10; i++) {
            printf("%d\n", tab[i]);
        }
*/
        free(tab);
    }   
    
    return 0;
}
  • On contrôle que le pointeur retourné par malloc est non NULL.
  • Un pointeur est une adresse mémoire, qui a donc toujours la même taille (32 bits sur une architecture 32 bits, 64 bits sur une architecture 64 bits).
  • Un cast n'impacte pas la valeur du pointeur, mais modifie son type. Les types assignés aux pointeurs permettent au compilateur de vérifier que tu fais quelque chose de cohérent (i.e., tu ne mélanges pas des torchons et des serviettes). Cependant il peut arriver que tu veuilles volontairement changer la nature d'un pointeur, et c'est dans ce cas un cast est nécessaire pour que le compilateur ne se plaigne pas.
    • Dans le cas particulier de malloc, tu n'es pas obligé de faire un cast. Les compilateurs récent "savent" que bien souvent, le pointeur void * (adresse générique) retourné par malloc va être assigné à un pointeur typé. 
  • On n'utilise un pointeur que si celui-ci a été alloué avec succès.
  • On ne libère un pointeur que si celui-ci a été alloué avec succès. 

Bonne chance

1
[Dal] Messages postés 6174 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 2 février 2024 1 083
Modifié le 20 janv. 2023 à 18:43

Salut JeVeuxJusteEtreIngenieure,

Comme le dit Pierrot, tu dois vérifier, à chaque usage de malloc() que le pointeur retourné par malloc() n'est pas le pointeur NULL.

Ta documentation de référence des fonctions standard du langage C pour la fonction malloc() te donne la solution, en t'indiquant ce que renvoie malloc(). Tu devrais prendre l'habitude de consulter et comprendre ta documentation de référence.

https://cplusplus.com/reference/cstdlib/malloc/

Sur cette page, tu as également un exemple de traitement du renvoi de NULL, dans cet exemple, consistant à quitter le programme prématurément avec exit() et un code d'erreur (un message d'erreur serait bien aussi avant de quitter le programme, pour expliquer ce qui se passe à l'utilisateur). C'est un peu radical, mais c'est souvent de cette façon que sont traitées les erreurs d'allocation de mémoire dans les "devoirs".

Une autre façon de faire serait d'avertir l'utilisateur que la capacité mémoire maximale a été atteinte, de refuser de créer une nouvelle liste ou de nouveaux éléments, et de l'inviter à libérer de la mémoire, à sauvegarder la liste pour ne pas perdre les données qui y seraient déjà, etc. (à toi de décider).

0
JeVeuxJusteEtreIngenieure
20 janv. 2023 à 18:57

Merci pour votre réponse !

Dans le cadre de mon examen, c'est une procédure qu'on doit faire obligatoirement pour montrer qu'on a compris le principe.

Savez-vous si ceci est donc correct pour la vérification ?

Tete = nouveau;

if(tete==NULL);

return NULL;

Bonne soirée

0

Je pourrais ajouter qu'il n'est pas recommandé de caster le type retourné par malloc.
Cela encombre le code inutilement.

Aussi, on voit souvent des sizeof(char) dans les malloc. C'est également inutile car sizeof(char) vaut toujours 1.

0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
PierrotLeFou
20 janv. 2023 à 19:32

On n'a pas la procédure au complet. Que fais-tu si tête n'est pas NULL?
En C, procédure = fonction ...

0
sambia39 Messages postés 610 Date d'inscription vendredi 31 juillet 2009 Statut Membre Dernière intervention 9 février 2023 49
9 févr. 2023 à 12:52

Bonjour,

La fonction malloc est utilisée pour réserver un espace mémoire pendant l'exécution (c'est ce qu'on appelle communément l'allocation dynamique). Malloc renvoie un pointeur universel ou autrement générique (c'est-à-dire capable de pointer vers n'importe quel type d'objet comme mentionné brièvement) qui contient l'adresse de la zone mémoire allouée qui est une mémoire réservée mais non disponible réellement (vous ne disposer pas a ce moment là de la mémoire réel physique en RAM) . Elle renvoie donc une adresse mémoire virtuelle ou NULL en cas d'erreur.

Vous devez donc, comprendre que la mémoire réelle en RAM n'est absolument pas immédiate ou disponible juste après la demande de la fonction malloc. Car l'adresse mémoire obtenue sont des adresses mémoire virtuelles (c'est l'une des raisons pour lesquelles malloc échoue rarement car elle utilise des adresses virtuelles).

Cette façon de faire (avoir une adresse mémoire virtuelle et non réelle) est ce qu'on appelle une "mascarade de mémoire" (utilisée par les systèmes d'exploitation GNU/Linux ou Unix et probablement Windows pour vous donner l'illusion d'une mémoire infinie pour chaque "contexte d'exécution" communément appelé processus). La mémoire n'est réellement allouée ou disponible que lorsque vous y écrivez quelque chose et ce n'est qu'à ce moment-là que le système d'exploitation vous donne la mémoire souhaitée en RAM.

Par conséquent, il n'est pas seulement souhaitable de vérifier la disponibilité d'une adresse fournie par la fonction d'allocation de mémoire, mais aussi de vérifier l'allocation réelle de la mémoire physique après que vous l'ayez demandée en écrivant à l'emplacement de la mémoire, c'est ce qu'on appelle l'initialisation par défaut (mise à zéro de la mémoire qui entraîneront une erreur d'accès "erreur de segmentation contrôlée par le système" ce qui fournira une page de mémoire réelle et non virtuelle vous avez également la possibilité d'utiliser calloc qui initialise donc qui assure la disponibilité de la mémoire réel de façon immédiate). 

Et lorsque la mémoire réservée n'est plus nécessaire (cela implique que le pointeur d'initialisation n'est plus utilisé avec les fonctions d'allocation dynamique), l'espace mémoire doit être libéré et cela se fait avec la fonction free. Cela libère le bloc de mémoire désigné par votre pointeur, mais attention l'adresse contenue dans le pointeur n'est pas modifiée si vous essayez d'utiliser ou d'écrire voir autre, cela se terminerait systématiquement par une erreur. Le plus souvent, après avoir libéré la mémoire, il est conseillé de mettre le pointeur à NULL pour dire que la variable pointeur ne pointe plus que sur une adresse invalide (donc non utilisée) ; ainsi, toute manipulation incorrecte du pointeur sera automatiquement détectée.

Exemple rapide sans compilation.

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

int main( void ){

  /*  Initialisation  */
  unsigned int * p = NULL;
  unsigned int offset = 0x0;

  /* Allocation (réservation) de la mémoire */
  errno = 0x0;
  if( NULL == (p = malloc(BUFSIZ * sizeof(*p) )) ){
    (void)fprintf(stderr, "[-]Error(%d)\t:%s\n",
        errno, strerror(errno) );
    return EXIT_FAILURE;
  }

  /*  Disposition réel de la mémoire  
  *   en cas d'erreur sortie fatal 
  *  autre option sortie propre return EXIT
  */
  if( NULL == memset(p, 0x0, BUFSIZ) ){
    (void)fprintf(stderr, "[!]Error(%d)\t:%s\n",
      errno, strerror(errno) );
    abort();  // core dump
  }

  /* Version sortie propre 1 */
  /*if( NULL == memset(p, 0x0, BUFSIZ) ){
    (void)fprintf(stderr, "[!]Error(%d)\t:%s\n",
      errno, strerror(errno) );
    return errno;  // ou return EXIT_FAILURE
  }*/

  /* Remplissage des valeurs et print value  */
  for( ; BUFSIZ > offset; offset++ ){
    *(p + offset) = offset;
    (void)fprintf(stdout, "Offset[%d]\t = %d\n",
        offset, *(p+ offset) );
  }

  /*  libération de la mémoire */
  free(p);
  p = NULL;

  
  return EXIT_SUCCESS;
}

à bientôt.


0