Lorsque j'utilise malloc
dans un programme C, je reçois un avertissement:
warning: incompatible implicit declaration of built-in function 'malloc' [enabled by default]
Je peux ensuite inclure <malloc.h>
ou <stdlib.h>
pour se débarrasser de warning
bien qu'il fonctionne également sans.
Je me demandais donc quelle est la différence entre ces en-têtes et lequel fait des liens gcc
quand je n'inclus rien?
(J'utilise ubuntu 12.04 64-bit
avec gcc 4.6.3
)
Le <malloc.h>
l'en-tête est obsolète (et assez spécifique à Linux, sur lequel il définit des fonctions non standard comme mallinfo (3) ) . Utilisation <stdlib.h>
à la place si vous avez simplement besoin de malloc (3) et des fonctions standard associées (par exemple free
, calloc
, realloc
....). Remarquerez que <stdlib.h>
est défini par les normes C89 (et ultérieures), mais pas <malloc.h>
Regardez dans /usr/include/malloc.h
vous y trouverez des fonctions non standard (par exemple malloc_stats (3) , etc ...) - en plus de malloc
....
Et gcc
ne lie pas les fichiers d'en-tête, mais les bibliothèques. Lisez le livre de Levine sur linkers & loaders pour en savoir plus.
Si vous n'incluez aucun en-tête (et que vous ne déclarez pas explicitement malloc
vous-même, ce qui serait une mauvaise idée), malloc
est implicitement déclaré comme renvoyant une valeur int
(qui est faux). Je vous invite à passer au moins le -Wall
drapeau à gcc
lors de son utilisation.
Vous pourriez également passer -v
à gcc
pour comprendre les programmes réels impliqués: cc1
est le compilateur proprement dit (produisant le code assembleur), as
l'assembleur, ld
l'éditeur de liens et collect2 un utilitaire interne qui appelle l'éditeur de liens.
stdlib.h est un en-tête C standard qui déclare entre autres les fonctions malloc (), calloc (), free (). C'est l'en-tête que vous devez inclure.
malloc.h est un en-tête non standard, trouvé sur de nombreux systèmes où il définit souvent des fonctions supplémentaires spécifiques à l'implémentation malloc utilisée par cette plateforme.
Si vous n'incluez aucun de ces éléments, il n'y a pas de valeur par défaut, cependant si vous appelez malloc () sans déclaration préalable de la fonction malloc, C supposera que le prototype de la fonction est int malloc();
, ce qui est souvent faux. En plus des en-têtes, les compilateurs C sont généralement liés à une bibliothèque standard, par ex. glibc sous Linux, où réside l'implémentation de malloc.
Notez qu'il existe une différence entre les fichiers d'en-tête et les bibliothèques. Les fichiers d'en-tête déclarent des choses, comme des structures et des prototypes de fonctions. Les bibliothèques contiennent l'implémentation, le code compilé. Vous créez un lien vers la bibliothèque et vous #incluez les fichiers d'en-tête.
Les en-têtes déclarent différents ensembles de fonctions, mais les deux déclarent en avant malloc
.
Si vous n'incluez aucun d'entre eux, vous n'avez pas de prototype pour malloc
, d'où l'avertissement. Mais vous liez malgré tout la même fonction, car il n'y a qu'une seule fonction malloc
. C'est juste déclaré à deux reprises. Les déclarations directes ne sont pas là pour aider à établir un lien avec la fonction malloc
, elles sont là pour que le compilateur puisse émettre le code correct autour de l'appel, pour spécifier les arguments et lire la valeur de retour.
Notez que <malloc.h>
n'est pas une inclusion standard. Je ne pense pas stdlib.h
inclut toujours malloc.h
sur GCC, mais vous pouvez imaginer que ce pourrait être le cas puisque c'est une façon de fournir la déclaration nécessaire.
<malloc.h>
N'est pas un en-tête standard et n'est donc pas portable. La norme met malloc()
et al. dans <stdlib.h>
.
D'autres ont déjà discuté des différences entre <malloc.h> et <stdlib.h>
Quant à l'avertissement quand aucun n'est inclus, c'est la définition du fonctionnement des fonctions C. Une fonction sans prototype (qui est ce que vous avez lorsque vous ne déclarez pas le vôtre et n'incluez pas d'en-tête avec un) est traitée comme une fonction avec un type de retour int
et un non spécifié liste d'arguments.
Le compilateur effectuera des promotions par défaut (par exemple, float to double et autres) et la fonction est appelée. Si le nombre d'arguments utilisés par la fonction est différent du nombre passé, ou si les types d'arguments après les promotions par défaut ne sont pas compatibles avec l'implémentation de la fonction, il s'agit d'un comportement non défini.
Voir ISO 9899: 1999 (C99) §6.5.2.2, ¶ 6:
Si l'expression qui dénote la fonction appelée a un type qui n'inclut pas de prototype, les promotions entières sont effectuées sur chaque argument et les arguments qui ont le type
float
sont promus endouble
. Ceux-ci sont appelés promotions d'argument par défaut. Si le nombre d'arguments n'est pas égal au nombre de paramètres, le comportement n'est pas défini. Si la fonction est définie avec un type qui inclut un prototype et que le prototype se termine par des points de suspension (, ...) ou que les types d'arguments après promotion ne sont pas compatibles avec les types de paramètres, le comportement n'est pas défini. Si la fonction est définie avec un type qui n'inclut pas de prototype et que les types des arguments après promotion ne sont pas compatibles avec ceux des paramètres après promotion, le comportement n'est pas défini, sauf dans les cas suivants:
- un type promu est un type entier signé, l'autre type promu est le type entier non signé correspondant, et la valeur est représentable dans les deux types;
- les deux types sont des pointeurs vers des versions qualifiées ou non qualifiées d'un type de caractère ou
void
.
Dans le cas de l'appel de malloc()
sans prototype, cela peut être très mauvais. malloc()
accepte un size_t
argument et renvoie un void *
pointeur. Si le résultat de la promotion par défaut de votre argument entier produit un entier de taille différente de size_t
, vous aurez un comportement indéfini. Et si int
est d'une taille différente de void *
(par exemple, sur les systèmes 64 bits, où int
est souvent 32 bits et void *
sera de 64 bits,) le pointeur renvoyé sera gâché.
Pour apprendre la différence, vous devriez lire leur contenu par vous-même.
Par défaut, gcc ne lit ni l'un ni l'autre.
Lorsque vous les lirez, vous verrez qu'ils déclarent malloc
différemment.