2012-04-21 20 views
5

estaba escribiendo un código usando malloc por algo y luego enfrentado a un problema, así que escribí un código de prueba que en realidad se resume toda la confusión que está por debajo ::asignación de memoria dinámica en 'c' Cuestiones

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

int main()  
{ 
    int *p = NULL; 
    void *t = NULL; 
    unsigned short *d = NULL; 

    t = malloc(2); 
    if(t == NULL) perror("\n ERROR:"); 
    printf("\nSHORT:%d\n",sizeof(short)); 
    d =t; 
    (*d) = 65536; 
    p = t; 
    *p = 65536; 
    printf("\nP:%p: D:%p:\n",p,d); 
    printf("\nVAL_P:%d ## VAL_D:%d\n",(*p),(*d)); 
    return 0; 
    } 
    Output:: [email protected]:~/Desktop/ad/A1/CC$ ./test 

      SHORT:2 
      P:0x9512008: D:0x9512008: 
      VAL_P:65536 ## VAL_D:0 

Estoy asignando 2 bytes de memoria usando malloc. Malloc que devuelve un puntero void * se almacena en un puntero * void 't'.

Luego, después de que 2 punteros se declaran p - tipo entero yd - del tipo abreviado. luego asigné t a ambos * (p = t y d = t) * que significa que ambos d & p apuntan a la misma ubicación de memoria en el montón.

al intentar guardar 65536 (2^16) a (* d) recibo una advertencia de que el valor int grande se trunca, que es el esperado. Ahora volví a guardar 65536 (2^16) a (* p) que no causó ninguna advertencia.

* Al imprimir tanto (* p) como (d) obtuve valores diferentes (aunque cada uno corregido para su propio tipo de puntero definido).

Mi pregunta es:

  1. aunque he asignado 2 bytes (es decir, 16 bits) de montón mem usando malloc cómo soy yo capaz de salvar a 65.536 en esos dos bytes (usando (p) que es un puntero de tipo entero). ?? tengo la sensación de que la causa de esto es el tipo automático converion of void a int * puntero (en p = t) así que la asignación de t a p lleva al acceso a regiones de memoria fuera de lo asignado a través de malloc. ??

  2. Aunque todo esto está sucediendo, cómo desencadenando la misma región de memoria a través de (* p) y (* d) imprime dos respuestas diferentes (aunque esto también puede explicarse si lo que estoy pensando es la causa en la pregunta 1)

Puede alguien poner un poco de luz sobre esto, será muy appreciated..and también si alguien puede explicar las razones detrás de esto ..

Muchas gracias

+0

Malloc puede estar redondeando la cantidad solicitada hacia un cierto multiplicador. No sé sobre * nix, pero a Windows le gusta redondearlo a múltiplos de 8 bytes. Si ese es el caso, está escribiendo fuera del área solicitada, pero se encuentra dentro del margen de seguridad y, por lo tanto, no está corrompiendo nada más. – DCoder

+0

Ha abusado cuidadosamente de la potencia de fundición para obtener un comportamiento indefinido. C te da un gran poder y flexibilidad para hacer * cosas malas *. Tienes que tomar la responsabilidad de *** no hacerlos ***. – dmckee

+0

@dmckee Sé que lo que estoy haciendo no debería hacerse, pero después de tropezar con esto, tenía curiosidad por saber las razones exactas detrás de esto, lo que inicialmente supuse era el hecho de que la conversión automática de tipo a int * de un byte 2 void void * lleva al puntero int * a acceder a más memoria que a 2 bytes porque en mi máquina int es de 4 bytes. Es correcto (mi suposición) – abhi

Respuesta

2

responder a su segunda pregunta:

La explicación es el hecho de que un int es generalmente de 4 bytes y los bytes más significativos pueden ser almacenados en las dos primeras posiciones.Un short, que tiene solo 2 bytes, también almacena sus datos en las dos primeras posiciones. Claramente, entonces, almacenar 65536 en un int y un short, pero apuntando a la misma ubicación de memoria, hará que los datos se almacenen compensados ​​por dos bytes para el int en relación con el short, con los dos bytes menos significativos del int correspondiente al almacenamiento para el short.

Por lo tanto, cuando las impresiones del compilador *d, interpreta esto como un short y mira a la zona correspondiente al almacenamiento para un short, que no es donde el compilador almacena previamente el 65536 cuando *p fue escrito. Tenga en cuenta que escribiendo *p = 65536; sobrescribió el *d = 65536; anterior, rellenando los dos bytes menos significativos con 0.

En cuanto a la primera pregunta: el compilador no tienda de la 65536 para *p dentro de 2 bytes. Simplemente va más allá de los límites de la memoria que ha asignado, lo que probablemente cause un error en algún momento.

+0

Por lo tanto, el hecho de que la conversión de tipo automática a int * de un puntero void * de 2 bytes asignados lleve a int * puntero a acceder a más memoria que a 2 bytes porque en mi máquina int es de 4 bytes. ¿Es correcto (mi suposición) – abhi

+0

y gracias por la respuesta clara y nítida que explica todo :) – abhi

+1

Conceptualmente, eso es todo. Diría que técnicamente hablando, quizás no es la mejor fraseología para decir "conversión automática de tipo" en su oración, porque nada se está convirtiendo en realidad, es solo que el compilador está accediendo a los bytes en la memoria como si estuvieran almacenando un tipo de datos, en lugar de acceder a ellos como si estuvieran almacenando otro tipo. –

1

En C hay no es protección en absoluto para escribir fuera de los límites de una asignación. Simplemente no lo hagas, cualquier cosa puede pasar. Aquí parece que funciona para usted porque, por alguna coincidencia, el espacio detrás de los dos bytes que asignó no se usa para otra cosa.

+0

O al menos no ha hecho nada que demuestre algún otro uso para esa memoria. La respuesta silenciosa a este tipo de error es lo que les permite acercarse sigilosamente. – dmckee

0

1) La granularidad del administrador de memoria del sistema operativo es 4K. Es poco probable que una sobreescritura por un bit desencadene AV/segfault, pero dañará cualquier dato en la ubicación adyacente, lo que lleva a:

2) Comportamiento indefinido. Este conjunto de comportamientos incluye 'operación aparentemente correcta', (¡por ahora!).

Cuestiones relacionadas