2012-05-21 10 views
8
int main() 
{ 
    char *p; 
    p = (char*) malloc(sizeof(char) * 0); 
    printf("Hello Enter the data without spaces :\n"); 
    scanf("%s",p); 
    printf("The entered string is %s\n",p); 
    //puts(p); 
} 

Al compilar el código anterior y ejecutarlo, el programa puede leer la cadena aunque hayamos asignado una memoria de 0 bytes al puntero p.comportamiento de malloc (0)

¿Qué sucede realmente en la declaración p = (char*) malloc(0)?

+5

Fuera de tema ... Pero no es una buena práctica lanzar malloc return .. Ver [this] (http://stackoverflow.com/questions/605845/doi-i-cast-the-result-of-malloc) – Krishnabhadra

+4

C no lo protege contra sobrepasar los búferes que le han asignado; debe tener cuidado de no hacer esto, pisoteará la memoria de otra persona. – Rup

+1

posible duplicado de [¿Por qué Malloc (0) devuelve una dirección no nula en Windows?] (Http://stackoverflow.com/questions/9723030/why-does-malloc0-return-a-non-null-address- en la ventana) – Jay

Respuesta

12

Es la implementación definida qué malloc() devolverá pero es un comportamiento indefinido usar ese puntero. Y el comportamiento indefinido significa que cualquier cosa puede suceder literalmente, desde el funcionamiento del programa sin fallas hasta el colapso, todas las apuestas seguras están desactivadas.

C99 estándar:

7.22.3 funciones de gestión de memoria
apartados 1:

Si el tamaño del espacio solicitado es cero, el comportamiento es definido por la implementación, ya sea: se devuelve un puntero nulo o el comportamiento es como si el tamaño fuera un valor distinto de cero, excepto que el puntero devuelto no se utilizará para acceder a un objeto.

+0

Pero tenga en cuenta que * siempre * está OK para llamar 'gratis' al resultado de 'malloc'. –

+1

Una adición a lo que escribió Als: el comportamiento indefinido incluye "hace lo que creo que debería hacer, * esta vez *". La próxima vez podría ser diferente. En un sistema operativo diferente, probablemente sea diferente. – cschneid

+2

.. siempre que solo lo haga una vez. –

0

Además de Als comment - qué ocurre: escribe en algún lugar de la memoria y recupera los datos de allí. Dependiendo de su sistema y sistema operativo, usted obtiene una excepción o un comportamiento indefinido

0

Solo por curiosidad, probé su código usando gcc en Linux, y es mucho más robusto de lo que esperaba (después de todo, escribir datos en un búfer de caracteres de longitud 0 es un comportamiento indefinido ... Lo haría esperar que se bloquee).

Aquí es mi modificación de su código:

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

int main() 
{ 
    char *p; 
    p = malloc(sizeof(char)*0); 
    printf("Hello Enter some without spaces :\n"); 
    scanf("%s",p); 

    char *q; 
    q = malloc(sizeof(char)*0); 
    printf("Hello Enter more data without spaces :\n"); 
    scanf("%s",q); 

    printf("The first string is '%s'\n",p); 
    printf("The second string is '%s'\n",q); 
} 

Mi primer pensamiento fue que puedas ser salvo por el hecho de que sólo está leyendo datos en una sola posición de memoria - si utiliza dos memorias intermedias, el segundo podría sobrescribir la primera ... así que se rompió el código en las secciones de entrada y salida:

Hello Enter some without spaces : 
asdf 
Hello Enter more data without spaces : 
tutututu 
The first string is 'asdf' 
The second string is 'tutututu' 

Si el primer tampón había sido sobrescrito, veríamos

The first string is 'tutututu' 
The second string is 'tutututu' 

Así que ese no es el caso. [Pero esto depende de la cantidad de datos que meter en cada búfer ... ver más abajo]

Entonces, pegué una increíble cantidad de datos en ambas variables:

perl -e 'print "c" x 5000000 . "\n" ' | xsel -i 

(Esto puso de + 4 MB de 'c's en el buffer de copia). Pegué esto en la primera y en la segunda llamada scanf. El programa lo tomó sin una falla de segmentación.

Aunque no tuve un error de segmentación, el primer buffer se sobrescribió. No podría decirlo porque mucha información pasó volando por la pantalla.Aquí está un funcionamiento con menos datos:

$ ./foo 
Hello Enter some without spaces : 
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 
Hello Enter more data without spaces : 
ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc 
The first string is 'aaaaaaaaaaaa' 
The second string is 'ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc' 

Había un pequeño glifo después aaaaaaaaaaaa, que es como mi terminal representa un carácter Unicode que no se puede mostrar. Esto es típico de los datos sobreescritos: usted no sabe lo que va a sobrescribir sus datos ... es un comportamiento indefinido, por lo que es propenso a los demonios nasales.

La conclusión es que cuando escribe en la memoria que no ha asignado espacio para (explícitamente usando malloc o implícitamente con una matriz), está jugando con fuego. Tarde o temprano, sobrescribirá la memoria y se causará todo tipo de dolor.

La verdadera lección aquí es que C no hace límites verificando. Se felizmente le permite escribir en la memoria que no es de su propiedad. Puedes hacerlo todo el día. Su programa puede ejecutarse correctamente, y puede que no. Se puede bloquear, puede escribir datos dañados o puede funcionar hasta que escanee en un byte más de lo que usó durante la prueba. No le importa, así que tienes que hacerlo.

La caja de malloc(0) es simplemente una caja especial de this question.

+0

Cuando no estamos asignando ninguna memoria al puntero, en tal caso escribir en una memoria no existente debería causar un error de Segmentación ¿verdad? También estoy trabajando con gcc en Linux y la falla de segmentación no está ocurriendo. ¡Estoy confundido con este comportamiento del malloc (0)! – svKris

+0

@svKris Mi mejor suposición es que cuando ejecutas malloc (0), obtienes un puntero en algún lugar del montón. malloc no reserva ninguna memoria, por lo que no tiene ninguna garantía de que no sobrescriba los datos en otro lugar en el montón, pero (probablemente) no le fallará. Si intenta escribir en un puntero no inicializado, va a escribir a quién sabe qué dirección de memoria, por lo que probablemente segmente la falla. Exactamente cómo se maneja esto depende de la implementación. –

+0

@svKris de acuerdo con el estándar c99 citado anteriormente, malloc (0) puede devolver nulo, o puede devolver algo más. Escribir en un puntero nulo le dará una falla seg, por lo que aparentemente eso no es lo que hace gcc. –