2009-11-23 9 views
7

Dado el siguiente código:similitudes y diferencias entre las matrices y los punteros a través de un ejemplo práctico

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

int main() 
{ 
    int a[1]; 
    int * b = malloc(sizeof(int)); 

    /* 1 */ 
    scanf("%d", &a); 
    printf("%d\n", a[0]); 

    /* 2 */ 
    scanf("%d", &b); 
    printf("%d\n", b[0]); 

    return 0; 
} 

las siguientes advertencias se obtienen cuando se compila (i686-manzana-darwin9-gcc-4.0.1):

array.c: In function 'main': 
array.c:9: warning: format '%d' expects type 'int *', but argument 2 has type 'int (*)[0u]' 
array.c:14: warning: format '%d' expects type 'int *', but argument 2 has type 'int **' 

pero, ¿por qué se produce una ejecución de error en el segundo printf, por su parte funciona por primera printf?

Aún más, por lo que se obtiene el mismo resultado si la primera scanf está sustituido por scanf ("% d", a);?

gracias mucho por adelantado

Respuesta

10

En la mayoría de los contextos, una expresión de tipo de matriz se convertirá implícitamente de una "matriz de elementos N de T" a "apuntador a T" y su valor se establecerá para apuntar al primer elemento de la matriz. Las excepciones a esta regla son cuando la matriz es un operando de los operadores & o sizeof, o si la matriz es una cadena literal que se usa para inicializar otra matriz en una declaración.

Entonces, ¿cómo se relaciona todo esto con su código?

En la línea

scanf("%d", &a); 

Usted está solicitando al operador & a la matriz. Esto suprime la conversión implícita de "array of T" a "pointer to T" y devuelve un valor de tipo "pointer to array of T", o T (*)[N] (de ahí su primera advertencia). Ahora resulta que el valor de un puntero a una matriz y el valor de un puntero al primer elemento de la matriz son los mismos, simplemente tienen diferentes tipos.Así que asumiendo que a está en la dirección 0x0001000:

expression  type   value   note 
----------  ----   -----   ---- 
     a  int *   0x0001000  implicitly converted to pointer 
     &a  int (*)[N] 0x0001000  
    &a[0]  int *   0x0001000 

Es por eso que la primera llamada a scanf() "trabaja"; está pasando el puntero derecho valor, pero el compilador se queja porque tipo de la expresión no coincide con lo que espera la función. que había escrito

scanf("%d", a); 

que no habría recibido ninguna advertencia, ya que se tomará el tipo de a ser int *, que es lo scanf() Espera. Tenga en cuenta que esto es idéntico a llamar

scanf("%d", &a[0]); 

En cuanto a b ...

Usted declara explícitamente b como un puntero a int y asignar un bloque de memoria a la misma. Cuando aplica el operador &, lo que obtiene es la dirección de la variable b con el tipo int ** (de ahí la segunda advertencia), no la dirección a la que apunta b.

expression  type   value   note 
----------  ----   -----   ---- 
     b  int *   0x0040000  value contained in b 
     &b  int **  0x0001000  address of b 

Para ese caso, que acaba de pasar el b sin decorar:

scanf("%d", b); 
5

La matriz de una se coloca en la pila, la dirección para el primer elemento es la misma que la dirección de a. & a [0] y & a es la misma dirección de

La matriz B se asigna con malloc, la dirección para el almacenamiento es en el montón, mientras que la dirección para el puntero b está en la pila. & b [0] no es la misma dirección que & b.

Es por eso que el primer scanf y printf funcionan pero no el segundo.

La C-FAQ lo explica con mucho más detalle.

2

En el primer scanf pasa una referencia a una matriz. En C los arrays son punteros a un bloque de memoria del tipo asignado, en su caso int * y una expresión como a[0] se traduce a *(a + 0) (que por cierto da lugar a la divertida variante 0[a] que realmente compilará.) Esta matriz se asigna a la pila . La segunda matriz se asigna en el montón y la pila contiene la variable de puntero a esa matriz.

En ambos casos, no pasa un puntero a la primera entrada de la matriz, sino a la matriz y el puntero a la matriz, respectivamente.

Su primer scanf sobrescribe lo que es la matriz, ya que está asignada en la pila, su valor termina (por suerte) en la matriz.

Su segundo scanf sobrescribe el puntero a la matriz, cambiando así el puntero a una dirección de memoria que probablemente no exista en su segmento de datos. Esto da como resultado el error de ejecución.

1

eso es normal ...

En primer lugar, scanf requiere un puntero. "a" y "b" ya son punteros! Entonces:

/* 1 */ 
scanf("%d", a); 
printf("%d\n", a[0]); 

/* 2 */ 
scanf("%d", b); 
printf("%d\n", b[0]); 

Will work.

Normalmente/* 1 */no debería funcionar. Pero gcc transforma "& a" por "a" porque "& a" no tiene ningún sentido.

printf("&a = %p\n", &a); 
printf("a = %p\n", a); 
printf("&b = %p\n", &b); 
printf("b = %p\n", b); 

&a = 0x7ffff6be67d0 
a = 0x7ffff6be67d0 
&b = 0x7ffff6be67c8 
b = 0xb0b010 

No puede tomar la dirección de a. Pero b es una "variable normal" de tipo puntero, y por lo tanto puede tomar su dirección por "& b".

En/* 2 */está poniendo el valor ingresado por el usuario en b y, por lo tanto, * b (o b [0]) se bloqueará a menos que el usuario ingrese una dirección válida de memoria legible.

1

En su caso, lo que está sucediendo es que está pasando las dos variables a y b con el operador & a la función scanf. Lo que hace este operador es "preguntar" la dirección de memoria de la variable y pasar esa dirección a la función scanf. Pero, como ambas variables son punteros, lo que sí tienen es una dirección de memoria, por lo que cuando pasa & ao & b, está pasando la memoria del puntero, no la dirección de memoria que contiene.

Ejemplo:

int x; 
int *ptr; 

x = 10; 

suponer la dirección de memoria de x es 1000. Usted está almacenando el número 10 en la dirección de memoria 1000. Ahora se hace esto:

ptr = &x; 

va a almacenar el dirección 1000 en el puntero. Pero 1000, aparte de ser una dirección, es un número en sí mismo, por lo que el puntero, al igual que x, aún necesita una dirección de memoria para almacenar esa información. Supongamos que la posición de memoria del puntero se 1004. Ahora mira el ejemplo:

*ptr == 10; //x content 
ptr == 1000 //x memory address 
&ptr == 1004 // ptr memory address. 

Así que si quieres pasar a scanf la variable x, pero utilizando el puntero, es necesario pasar la dirección x almacenado en ella

scanf("%d", ptr); 

Sólo para ilustrar otro ejemplo de punteros y vectores

int main 
{ 
    int vet[5]; 
    int *ptr; 

    ptr = vet; 

    for(int i = 0; i < 5; ++i) 
    { 
     scanf("%d", (ptr+i)); 
    } 
} 

Aquí puede leer el vector con el puntero. Además, usando aritmética de puntero puede iterar sobre las direcciones de memoria del vector.

Cuestiones relacionadas