Que se passe-t-il si j’utilise malloc avec une taille incorrecte?

J’essaie d’en apprendre davantage sur l’allocation de mémoire et j’ai donc écrit un code de test ci-dessous pour voir ce qui se passerait si j’essayais d’allouer une mémoire d’une taille inférieure à celle dont j’avais besoin.

#include  #include  typedef struct { char *message; int number; } Object; int main(int argc, char *argv[]) { Object *obj = malloc(sizeof(Object) - 8); printf("The size of the struct is: %ld\n", sizeof(Object)); printf("The size of what was allocated is: %ld\n", sizeof(*obj)); obj->message = "Hello there! My name is Chris!"; obj->number = 435543; puts(obj->message); printf("%d\n", obj->number); free(obj); return 0; } 

Tout d’abord, sizeof (* obj) est-il la bonne façon de voir combien de mémoire a été réellement allouée dans ce cas? Deuxièmement, pourquoi suis-je toujours en mesure d’affecter des valeurs à des objects structur même si je n’ai pas alloué suffisamment d’espace?

Mon système d’exploitation est Ubuntu 12.10 64bits, le compilateur est gcc 4.7.2

Voici la sortie de valgrind:

 ==14257== Memcheck, a memory error detector ==14257== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al. ==14257== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info ==14257== Command: ./ex ==14257== The size of the struct is: 16 The size of what was allocated is: 16 ==14257== Invalid write of size 4 ==14257== at 0x400640: main (ex.c:15) ==14257== Address 0x51f1048 is 0 bytes after a block of size 8 alloc'd ==14257== at 0x4C2B3F8: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==14257== by 0x400604: main (ex.c:10) ==14257== Hello there! My name is Chris! ==14257== Invalid read of size 4 ==14257== at 0x40065A: main (ex.c:18) ==14257== Address 0x51f1048 is 0 bytes after a block of size 8 alloc'd ==14257== at 0x4C2B3F8: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==14257== by 0x400604: main (ex.c:10) ==14257== 435543 ==14257== ==14257== HEAP SUMMARY: ==14257== in use at exit: 0 bytes in 0 blocks ==14257== total heap usage: 1 allocs, 1 frees, 8 bytes allocated ==14257== ==14257== All heap blocks were freed -- no leaks are possible ==14257== ==14257== For counts of detected and suppressed errors, rerun with: -v ==14257== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 2 from 2) 

Dans ce cas spécifique, vous essayez potentiellement d’allouer 0 octet, car sizeof(Object) vaut 8 dans la plupart des compilateurs 32 bits. malloc(0) retournera NULL car sa taille n’est pas valide, et essayer d’écrire à l’adresse NULL plantera certainement votre application.

Mais supposons que vous allouiez avec succès 4 octets et essayiez d’y écrire 8 octets. Dans une application à un seul thread, cela devrait probablement fonctionner sans problème, car même si vous écrivez dans un espace mémoire non alloué, vous n’écrirez pas exactement à une adresse folle perdue dans la mémoire virtuelle.

Cependant, si vous faites cela:

 Object* a = (Object*)malloc(4); Object* b = (Object*)malloc(4); 

Il est potentiellement vrai que a et b ont été atsortingbués en série. Cela signifie que, dans la plupart des compilateurs 32 bits, l’écriture sur un a->number écrasera le b->message avec la même valeur et inversement, car les deux essaieront de stocker des informations dans le même espace mémoire.

C’est bien que vous fassiez ce genre d’exploration. La plupart des bibliothèques c standard incluent des extensions non standard qui vous permettent de vérifier la taille de la mémoire après son affectation. Vous devriez appeler cela après votre retour de malloc et voir la quantité de mémoire réellement allouée par la bibliothèque standard si vous souhaitez en savoir plus sur la façon dont malloc est implémenté. Vous constaterez peut-être qu’une tâche aussi simple que malloc(1) peut renvoyer un bloc de mémoire volumineux.

Comme d’autres lecteurs l’ont fait remarquer, vous pourriez demander à malloc d’allouer zéro octet dans votre code d’exemple si vous deviez recomstackr un système 32 bits. Il se plie volontiers et renvoie NULL .

cela dépend du compilateur et du système d’exploitation. Sur beaucoup, il finit par tomber en panne. Certainement pas recommandé. Peut potentiellement conduire à un débordement de mémoire tampon également.

Pour répondre à la sous-question sur sizeof: sizeof donne un résultat basé sur le type que vous utilisez (en C, il existe un cas séparé pour les tableaux de longueur variable). Si vous écrivez

 T* obj = malloc (any value); 

alors sizeof (* obj) ne regarde que le type de * obj, qui est T, et donne la taille d’un object de type T. Peu importe que l’allocation ait échoué et que obj soit réellement NULL, ou que vous en ayez alloué moins ou plus d’octets que la taille d’un T, peu importe si vous n’appelez pas du tout malloc et obj est une variable non initialisée.