Je suis tombé sur le puzzle C suivant:
Q: Pourquoi le programme suivant fonctionne-t-il correctement sur IA-64, mais fonctionne-t-il correctement sur IA-32?
int main()
{
int* p;
p = (int*)malloc(sizeof(int));
*p = 10;
return 0;
}
Je sais que la taille d' int
une machine 64 bits peut ne pas être la même que la taille d'un pointeur ( int
peut être 32 bits et le pointeur 64 bits). Mais je ne sais pas comment cela se rapporte au programme ci-dessus. Des idées?
c
pointers
segmentation-fault
utilisateur7
la source
la source
stdlib.h
ne pas être inclus?#include stdlib.h
(pour malloc)#include <stdlib.h>
, c'est parfaitement trouvé, mais ce n'est pas dans la question.sizeof(int) == sizeof(int*)
, par exemple, des pointeurs étaient renvoyés via un registre différent deint
s dans la convention d'appel utilisée.malloc()
. GCC dit:warning: incompatible implicit declaration of built-in function 'malloc'
aussi.Réponses:
Le cast
int*
masque le fait que sans le bon,#include
le type de retour demalloc
est supposé êtreint
. IA-64 se trouve avoirsizeof(int) < sizeof(int*)
ce qui rend ce problème évident.(Notez également qu'en raison du comportement non défini, il peut toujours échouer même sur une plate-forme où la valeur
sizeof(int)==sizeof(int*)
est vraie, par exemple si la convention d'appel utilise des registres différents pour renvoyer des pointeurs que des entiers)La FAQ comp.lang.c contient une entrée expliquant pourquoi la conversion du retour de
malloc
n'est jamais nécessaire et potentiellement mauvaise .la source
new
en C ++ et de toujours compiler C avec un compilateur C et non un compilateur C ++.int
s'il n'est pas connumalloc
)void*
. Mais le code appelant pense que la fonction retourneint
(puisque vous avez choisi de ne pas lui dire le contraire), il essaie donc de lire la valeur de retour selon la convention d'appel pour unint
. Par conséquent,p
ne pointe pas nécessairement vers la mémoire allouée. Il se trouve que cela fonctionne pour IA32 car unint
et unvoid*
ont la même taille et sont retournés de la même manière. Sur IA64, vous obtenez la mauvaise valeur.Très probablement parce que vous n'incluez pas le fichier d'en-tête pour
malloc
et, alors que le compilateur vous en avertit normalement, le fait que vous lancez explicitement la valeur de retour signifie que vous lui dites que vous savez ce que vous faites.Cela signifie que le compilateur s'attend
int
à ce que un soit renvoyé à partirmalloc
duquel il est ensuite converti en pointeur. S'ils sont de tailles différentes, cela va vous causer du chagrin.C'est pourquoi vous ne transtypez jamais le
malloc
retour en C.Levoid*
qu'il renvoie sera implicitement converti en un pointeur du type correct (sauf si vous n'avez pas inclus l'en-tête auquel cas il vous aurait probablement averti de l'int- conversion en pointeur).la source
void *
peut être converti en tout autre type de pointeur implicitement.int *p = malloc(sizeof(int))
fonctionne si le prototype approprié est dans la portée et échoue si ce n'est pas le cas (car alors le résultat est supposé êtreint
). Avec le casting, les deux compileraient et ce dernier entraînerait des erreurs quandsizeof(int) != sizeof(void *)
.stdlib.h
, le compilateur ne connaît pasmalloc
ni son type de retour. Donc, il suppose simplementint
par défaut.C'est pourquoi vous ne compilez jamais sans avertissement sur les prototypes manquants.
Le cast est nécessaire pour la compatibilité C ++. Il y a peu de raisons (lire: aucune raison ici) de l'omettre.
La compatibilité C ++ n'est pas toujours nécessaire, et dans certains cas pas du tout possible, mais dans la plupart des cas, elle est très facile à réaliser.
la source