4
votes

Comment comprendre exactement la syntaxe de deux fonctions malloc () et calloc ()

J'étudie le C et j'ai des questions sur la syntaxe d'allocation de mémoire dynamique.

Le code ci-dessous est un exemple d'allocation de mémoire dynamique. Si je comprends bien

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

int main()
{
   char *pr;

   pr = (char*)malloc( 50 * sizeof(char) ); 

   return 0;
}

renverra un pointeur vers un type de données de char et

  pr = (char *) malloc (50 * sizeof (char));

attribuera le pointeur 'pr' au pointeur déclaré lors de l'allocation de mémoire dynamique. Nous aurons donc un pointeur vers un autre pointeur

 (char *) malloc (50 * sizeof (char));

J'ai encore du mal à comprendre la syntaxe utilisée pour allouer de la mémoire dynamique. Quelqu'un peut-il expliquer en détail? merci


2 commentaires

Dois-je diffuser le résultat de malloc?


Notez également que sizeof (char) est toujours un, par définition. Par 6.5.3.4 Les opérateurs sizeof et _Alignof , paragraphe 4 : "Lorsque sizeof est appliqué à un opérande de type char , unsigned char ou caractère signé , (ou une version qualifiée de celui-ci) le résultat est 1. "


3 Réponses :



3
votes

affectera le pointeur 'pr' au pointeur déclaré lors de l'allocation de mémoire dynamique. Nous aurons donc un pointeur vers un autre pointeur

Non, vous aurez la valeur de la variable de pointeur stockée.

C'est exactement la même chose que

char * pr = malloc (50 * sizeof (*pr));  // no casting, and sizeof (*pr) is a
                                         // more robust way to have the size defined 

ou, dans un plus cas complexe

int test(void) { return 5;}
int x = test();

dans ce cas, test () renvoie une valeur 5 , qui est affectée à x . De la même manière

int x = 5;

attribue le pointeur renvoyé par malloc () à pr . Il n'y a pas de pointeur vers le pointeur ici.

En d'autres termes, pr contient le pointeur qui pointe vers l'emplacement mémoire alloué par l'appel à malloc () , ou un NULL (constante de pointeur nul) en cas d'échec de l'appel.


0 commentaires

2
votes

Commençons par clarifier un peu la terminologie:

Nous aurons donc un pointeur vers un autre pointeur

Ceci est incorrect. Lorsque vous affectez un pointeur à un autre pointeur, le deuxième pointeur ne pointe pas vers le premier pointeur, il pointe vers la même chose que le premier pointeur. Le fait de pointer vers un pointeur implique deux niveaux de déréférence nécessaires - dans ce cas - pour accéder au char ie char a = ** pr;

Donc , regardons le code.

int *pr = calloc(50, sizeof *pr);

Le prototype de malloc est

int* pr;
// Lots of code
pr = malloc( 50 * sizeof *pr );
for (int i = 0 ; i < 50 ; ++i)
{
    pr[i] = 0; // OK if malloc returns non NULL, otherwise undefined behaviour, probably a SIGSEV
} 

malloc alloue taille octets de mémoire et renvoie un pointeur vers ce bloc de mémoire (ou NULL s'il ne peut pas allouer un bloc de mémoire). malloc n'a aucune idée du type de chose sur lequel vous voulez qu'il pointe, donc il choisit de renvoyer un void * qui signifie un pointeur vers quelque chose d'inconnu. Le (char *) au début de votre appel à malloc est un type de cast. Il change le type de l'expression à sa droite pour le type entre parenthèses qui est char * dans cette instance (vous pouvez mettre ou laisser l'espace entre le char et le * sans aucun effet).

Le pointeur, qui a maintenant le type char * est alors affecté à pr qui a également le type char * .

La seule chose est que C fera le cast automatiquement, tant qu'il est de void * et vers un autre type de pointeur. Donc, ce que vous avez écrit est exactement équivalent à

int* pr;
// Lots of code
pr = malloc( 50 * sizeof(char) );
for (int i = 0 ; i < 50 ; ++i)
{
    pr[i] = 0; // buffer overflow!
} 

Il est généralement considéré comme meilleur style de le faire de cette façon que de mettre le cast explicite. C'est plus facile à lire et moins encombré.

Il y avait aussi le danger que, si vous oubliez de #include , le compilateur supposerait que malloc renvoie un int Si vous avez omis le cast, le compilateur marquerait la tentative de conversion de int vers char * comme une erreur, mais si vous la mettez, l'erreur serait supprimée. Ce problème n'existe plus car C11 interdit l'utilisation d'une fonction qui n'a pas été déclarée.

Il y a un autre problème qui est en quelque sorte l'inverse de celui décrit ci-dessus. Si vous avez

char* pr;
// Lots of code
pr = malloc( 50 * sizeof(char) );
pr[49] = 0; 

et que vous décidez que pr doit vraiment pointer vers int , vous pourriez vous retrouver avec p>

pr = malloc( 50 * sizeof(char) );

Ce qui compilerait mais ce comportement n'est pas défini car le bloc alloué n'est pas assez grand pour contenir 50 int s. Vous pouvez résoudre cela en revenant au cast explicite ( int * pr = (char *) malloc (...) est une erreur), mais le meilleur moyen est de faire une sizeof sur le pointeur déréférencé

void *malloc(size_t size);

calloc est comme malloc sauf qu'au lieu de fournir le nombre absolu d'octets, vous fournissez un compte des choses pointées et la taille des choses désignées séparément. calloc met également à zéro les octets du bloc alloué.

pr = (char*)malloc( 50 * sizeof(char) ); 

est effectivement le même que les bits de code précédents, sauf que ce n'est pas un comportement indéfini si la mémoire ne peut pas être allouée.


NB: Beaucoup de gens disent que vous ne devriez absolument pas mettre le cast explicite, mais il y a vraiment arguments dans les deux sens. this .


2 commentaires

@EricPostpischil c'est la toute première chose que j'ai abordée dans la réponse.


Désolé, je ne sais pas comment j'ai raté ça. Tu as raison: