Me encontré con el siguiente acertijo C:
P: ¿Por qué el siguiente programa se produce por defecto en IA-64, pero funciona bien en IA-32?
int main()
{
int* p;
p = (int*)malloc(sizeof(int));
*p = 10;
return 0;
}
Sé que el tamaño de int
en una máquina de 64 bits puede no ser el mismo que el tamaño de un puntero ( int
podría ser de 32 bits y el puntero podría ser de 64 bits). Pero no estoy seguro de cómo se relaciona esto con el programa anterior. ¿Algunas ideas?
c
pointers
segmentation-fault
usuario7
fuente
fuente
stdlib.h
no estar incluido?#include stdlib.h
(para malloc)#include <stdlib.h>
, se encuentra perfectamente, pero esa no es la cuestión.sizeof(int) == sizeof(int*)
, por ejemplo, los punteros se devuelven a través de un registro diferente aint
s en la convención de llamada utilizada.malloc()
. GCC dice:warning: incompatible implicit declaration of built-in function 'malloc'
también.Respuestas:
La conversión a
int*
enmascara el hecho de que sin el#include
tipo de retorno adecuadomalloc
se supone que esint
. IA-64 tiene losizeof(int) < sizeof(int*)
que hace que este problema sea obvio.(Tenga en cuenta también que, debido al comportamiento indefinido, aún podría fallar incluso en una plataforma donde
sizeof(int)==sizeof(int*)
se cumple, por ejemplo, si la convención de llamada usa registros diferentes para devolver punteros que enteros)Las preguntas frecuentes de comp.lang.c tienen una entrada que explica por qué
malloc
no es necesario nunca emitir el retorno de y es potencialmente malo .fuente
new
en C ++ y siempre compilar C con un compilador de C y no con un compilador de C ++.int
si no se conocemalloc
)void*
. Pero el código de llamada cree que la función regresaint
(ya que optó por no decirlo de otra manera), por lo que intenta leer el valor de retorno de acuerdo con la convención de llamada para unint
. Porp
lo tanto , no apunta necesariamente a la memoria asignada. Dio la casualidad de que funcionaba para IA32 porque anint
y avoid*
son del mismo tamaño y se devolvieron de la misma manera. En IA64 obtiene un valor incorrecto.Lo más probable es que se deba a que no incluye el archivo de encabezado
malloc
y, aunque el compilador normalmente le advierte de esto, el hecho de que esté emitiendo explícitamente el valor de retorno significa que le está diciendo que sabe lo que está haciendo.Eso significa que el compilador espera
int
que se devuelva un desde elmalloc
que luego lanza a un puntero. Si son de diferentes tamaños, eso te causará dolor.Esta es la razón por la que nunca emite el
malloc
retorno en C.Elvoid*
que devuelve se convertirá implícitamente en un puntero del tipo correcto (a menos que no haya incluido el encabezado, en cuyo caso probablemente le habría advertido de la información potencialmente insegura). conversión a puntero).fuente
void *
se puede convertir implícitamente a cualquier otro tipo de puntero.int *p = malloc(sizeof(int))
funciona si el prototipo adecuado está dentro del alcance y falla si no lo está (porque entonces se supone que el resultado esint
). Con el elenco, ambos se compilarían y el último daría lugar a errores cuandosizeof(int) != sizeof(void *)
.stdlib.h
, el compilador no sabemalloc
ni su tipo de retorno. Así que simplemente asumeint
como predeterminado.Es por eso que nunca compila sin advertencias sobre prototipos faltantes.
El elenco es necesario para la compatibilidad con C ++. Hay pocas razones (léase: aquí no hay razón) para omitirlo.
La compatibilidad con C ++ no siempre es necesaria, y en algunos casos no es posible en absoluto, pero en la mayoría de los casos se logra muy fácilmente.
fuente