web-dev-qa-db-fra.com

Je suis très confus à propos de malloc () et calloc () sur C

J'ai toujours programmé en Java, c'est probablement pourquoi je suis si confus à ce sujet:

Dans Java je déclare un pointeur:

int[] array

et l'initialiser ou lui affecter de la mémoire:

int[] array = {0,1,0}
int[] array = new int[3]

Maintenant, en C, tout est tellement déroutant. Au début, je pensais que c'était aussi simple que de le déclarer:

int array[]

et l'initialiser ou lui affecter de la mémoire:

int array[] = {0,1,0}
int array[] = malloc(3*sizeof(int))
int array[] = calloc(3,sizeof(int))

À moins que je ne me trompe, tout ce qui précède est équivalent à Java-C, non?

Puis, aujourd'hui, j'ai rencontré un code dans lequel j'ai trouvé ce qui suit:

pthread_t tid[MAX_OPS];

et quelques lignes ci-dessous, sans aucune sorte d'initialisation ...

pthread_create(&tid[0],NULL,mou_usuari,(void *) 0);

Étonnamment (du moins pour moi), le code fonctionne! Au moins en Java, cela retournerait une belle "NullPointerException"!

Donc, dans l'ordre:

  1. Suis-je correct avec toutes les "traductions" Java-C?

  2. Pourquoi ce code fonctionne-t-il?

  3. Y a-t-il une différence entre l'utilisation de malloc(n*sizeof(int)) et calloc(n,sizeof(int))?

Merci d'avance

27
bluehallu

Vous ne pouvez pas affecter de mémoire à un tableau. Un tableau a une taille fixe, pour toute sa durée de vie. Un tableau ne peut jamais être nul. Un tableau n'est pas un pointeur.

malloc renvoie l'adresse à un bloc mémoire réservé au programme. Vous ne pouvez pas "affecter" cela (étant le bloc de mémoire) à un tableau, mais vous pouvez stocker l'adresse de ce bloc de mémoire dans un pointeur: heureusement, l'abonnement au tableau est défini via des pointeurs - vous pouvez donc "utiliser des pointeurs comme des tableaux" , par exemple.

int *ptr = malloc(5 * sizeof *ptr);
ptr[2] = 5; // access the third element "of ptr"
free(ptr); // always free at the end

Lorsque vous déclarez un tableau sans taille (c'est-à-dire array[]), cela signifie simplement que la taille du tableau est déterminée à partir de la liste d'initialisation. C'est

int array[] = {1, 2, 3, 4, 5}; // is equal to
int array[5] = {1, 2, 3, 4, 5};

Essayer de déclarer un tableau sans taille et sans initialiseur est une erreur.


Le code pthread_t tid[MAX_OPS]; déclare un tableau nommé tid de type pthread_t et de taille MAX_OPS.

Si le tableau a un stockage automatique (c'est-à-dire que la déclaration est à l'intérieur d'une fonction et non statique, pas globale), alors chacun des éléments des tableaux a une valeur indéterminée (et cela entraînerait un comportement indéfini en essayant de lire une telle valeur). Heureusement, tout ce que l'appel de fonction fait, c'est qu'il prend l'adresse du premier élément du tableau comme premier paramètre, et l'initialise probablement (l'élément) à l'intérieur de la fonction.


La différence entre calloc et malloc est que le bloc de mémoire que calloc renvoie est initialisé à zéro. C'est;

int *ptr = calloc(5, sizeof *ptr);
// is somewhat equal to
int *ptr = malloc(5 * sizeof *ptr);
memset(ptr, 0, 5 * sizeof *ptr);

La différence entre

int *ptr = malloc(5 * sizeof *ptr);
// and
int array[5];

est que array a un stockage automatique, (est stocké sur la pile) et est "libéré" après avoir été hors de portée. ptr, cependant (est stocké sur le tas), est alloué dynamiquement et doit être freed par le programmeur.

38
eq-

Il vous manque trois sujets C très basiques et serrés (et trompeurs!):

  • la différence entre le tableau et les pointeurs
  • la différence entre l'allocation statique et l'allocation dynamique
  • la différence de déclarer des variables sur la pile ou sur le tas

Si vous écrivez int array[] = malloc(3*sizeof(int)); vous obtiendrez une erreur de compilation (quelque chose comme 'identifiant': l'initialisation du tableau nécessite des accolades).

Cela signifie que déclarer un tableau ne permet qu'une initialisation statique:

  • int array[] = {1,2,3}; Qui réserve 3 entiers contigus sur la pile;
  • int array[3] = {1,2,3}; Qui est le même que le précédent;
  • int array[3]; Qui réserve toujours 3 entiers contigus sur la pile, mais ne les initialise pas (le contenu sera des ordures aléatoires)
  • int array[4] = {1,2,3}; Lorsque la liste d'initialisation n'initialise pas tous les éléments, les autres sont mis à 0 (C99 §6.7.8/19): dans ce cas, vous obtiendrez 1,2,3,0

Notez que dans tous ces cas, vous n'utilisez pas allocation de nouvelle mémoire, vous utilisez simplement la mémoire déjà affectée à la pile. Vous ne rencontreriez un problème que si la pile est pleine (devinez, ce serait un débordement de pile). Pour cette raison, déclarer int array[]; Serait faux et dénué de sens.

Pour utiliser malloc, vous devez déclarer un pointeur: int* array.

Lorsque vous écrivez int* array = malloc(3*sizeof(int)); vous effectuez en fait trois opérations:

  1. int* array Indique au compilateur de réserver un pointeur sur la pile (une variable entière qui contient une adresse mémoire)
  2. malloc(3*sizeof(int)) alloue sur le tas 3 entiers contigus et retourne l'adresse du premier
  3. = Affecte des copies qui renvoient la valeur (l'adresse du premier entier que vous avez alloué) à votre variable pointeur

Donc, pour revenir à votre question:

pthread_t tid[MAX_OPS];

est un tableau sur la pile, il n'a donc pas besoin d'être alloué (si MAX_OPS est, disons, 16, alors sur la pile sera réservé le nombre d'octets contigus nécessaires pour contenir 16 pthread_t). Le contenu de cette mémoire sera des ordures (les variables de pile ne sont pas initialisées à zéro), mais pthread_create Renvoie une valeur dans son premier paramètre (un pointeur vers une variable pthread_t) Et ignore tout contenu précédent , donc le code est très bien.

4
lornova

C offre une allocation de mémoire statique ainsi que dynamique - vous pouvez allouer des tableaux hors de la pile ou dans la mémoire exécutable (gérée par le compilateur). C'est exactement la même chose qu'en Java, vous pouvez allouer un int sur la pile ou un Integer sur le tas. Les tableaux en C sont comme n'importe quelle autre variable de pile - ils sortent du domaine, etc. En C99, ils peuvent également avoir une taille variable, bien qu'ils ne puissent pas être redimensionnés.

La principale différence entre {} et malloc/calloc est que les tableaux {} sont alloués statiquement (n'ont pas besoin d'être libérés) et automatiquement initialisés pour vous, tandis que les tableaux malloc/calloc doivent être libérés explicitement et vous devez les initialiser explicitement. Mais bien sûr, les tableaux malloc/calloc ne sortent pas du domaine et vous pouvez (parfois) les réallouer ().

1
Puppy

2 - Cette déclaration de tableau est statique:

pthread_t tid[MAX_OPS];

Nous n'avons pas besoin d'allouer de bloc de mémoire, au lieu d'allocation dynamique:

pthread_t *tid = (pthread_t *)malloc( MAX_OPS * sizeof(pthread_t) );

N'oubliez pas de libérer la mémoire:

free(tid);

3 - La différence entre malloc et calloc est que calloc alloue un bloc de mémoire pour un tableau et initialise tous ses bits à 0.

0
remy_jourde