declarar - punteros c++
¿Cómo es que la dirección de una matriz es igual a su valor en C? (7)
En el siguiente bit de código, los valores del puntero y las direcciones del puntero difieren según lo esperado.
¡Pero los valores y las direcciones de matriz no lo son!
¿Cómo puede ser esto?
Salida
my_array = 0022FF00
&my_array = 0022FF00
pointer_to_array = 0022FF00
&pointer_to_array = 0022FEFC
#include <stdio.h>
int main()
{
char my_array[100] = "some cool string";
printf("my_array = %p/n", my_array);
printf("&my_array = %p/n", &my_array);
char *pointer_to_array = my_array;
printf("pointer_to_array = %p/n", pointer_to_array);
printf("&pointer_to_array = %p/n", &pointer_to_array);
printf("Press ENTER to continue.../n");
getchar();
return 0;
}
De las preguntas frecuentes de comp.lang.c:
- Entonces, ¿qué se entiende por "equivalencia de punteros y matrices" en C?
- Como las referencias de matriz decaen en punteros, si arr es una matriz, ¿cuál es la diferencia entre arr y & arr?
O vaya a leer toda la sección Arrays and Pointers .
El nombre de una matriz normalmente evalúa la dirección del primer elemento de la matriz, por lo que array
y &array
tienen el mismo valor (pero diferentes tipos, por lo que array+1
y &array+1
no serán iguales si la matriz es más de 1 elemento largo).
Hay dos excepciones a esto: cuando el nombre de la matriz es un operando de sizeof
o unario &
(address-of), el nombre se refiere al objeto de la matriz en sí. Así, sizeof array
te da el tamaño en bytes de toda la matriz, no el tamaño de un puntero.
Para una matriz definida como T array[size]
, tendrá el tipo T *
. Cuando / si lo incrementa, llega al siguiente elemento de la matriz.
&array
evalúa a la misma dirección, pero dada la misma definición, crea un puntero del tipo T(*)[size]
, es decir, es un puntero a una matriz, no a un solo elemento. Si incrementa este puntero, agregará el tamaño de la matriz completa, no el tamaño de un solo elemento. Por ejemplo, con un código como este:
char array[16];
printf("%p/t%p", (void*)&array, (void*)(&array+1));
Podemos esperar que el segundo puntero sea 16 más grande que el primero (porque es un conjunto de 16 caracteres). Como% p generalmente convierte punteros en hexadecimal, podría ser algo así como:
0x12341000 0x12341010
En C, cuando usó el nombre de una matriz en una expresión (incluyendo pasarlo a una función), a menos que sea el operando de la dirección de operador ( &
) o el operador sizeof
, se desintegrará a un puntero a su primera elemento.
Es decir, en la mayoría de contextos, la array
es equivalente a &array[0]
tanto en tipo como en valor.
En su ejemplo, my_array
tiene el tipo char[100]
que se desintegra a un char*
cuando lo pasa a printf.
&my_array
tiene tipo char (*)[100]
(puntero a matriz de 100 caracteres). Como es el operando de &
, este es uno de los casos en que my_array
no decae inmediatamente en un puntero a su primer elemento.
El puntero a la matriz tiene el mismo valor de dirección que un puntero al primer elemento de la matriz, ya que un objeto de matriz es solo una secuencia contigua de sus elementos, pero un puntero a una matriz tiene un tipo diferente a un puntero a un elemento de esa matriz Esto es importante cuando se realiza una aritmética de puntero en los dos tipos de puntero.
pointer_to_array
tiene el tipo char *
- initialized para apuntar al primer elemento de la matriz, ya que es a lo que my_array
descompone en la expresión del inicializador - y &pointer_to_array
tiene el tipo char **
(puntero a un puntero a un char
).
De estos: my_array
(después de decay a char*
), &my_array
pointer_to_array
y pointer_to_array
apuntan directamente a la matriz o al primer elemento de la matriz y, por lo tanto, tienen el mismo valor de dirección.
En el lenguaje de programación B, que era el predecesor inmediato de C, los punteros y los enteros eran libremente intercambiables. El sistema se comportaría como si toda la memoria fuera una matriz gigante. Cada nombre de variable tenía asociada una dirección global o relativa a la pila, para cada nombre de variable las únicas cosas de las que el compilador tenía que hacer un seguimiento era si era una variable global o local, y su dirección relativa al primer indicador global o local variable.
Dada una declaración global como i;
[no había necesidad de especificar un tipo, ya que todo era un entero / puntero] sería procesado por el compilador como: address_of_i = next_global++; memory[address_of_i] = 0;
address_of_i = next_global++; memory[address_of_i] = 0;
y una declaración como i++
se procesaría como: memory[address_of_i] = memory[address_of_i]+1;
.
Una declaración como arr[10];
se procesaría como address_of_arr = next_global; memory[next_global] = next_global; next_global += 10;
address_of_arr = next_global; memory[next_global] = next_global; next_global += 10;
. Tenga en cuenta que tan pronto como se procesó esa declaración, el compilador podría olvidarse inmediatamente de que arr
es una matriz . Una declaración como arr[i]=6;
se procesaría como memory[memory[address_of_a] + memory[address_of_i]] = 6;
. Al compilador no le importaría si arr
representara una matriz y i
un entero, o viceversa. De hecho, no le importaría si ambos fueran matrices o ambos enteros; sería perfectamente feliz generar el código como se describe, sin tener en cuenta si el comportamiento resultante sería útil.
Uno de los objetivos del lenguaje de programación C era ser ampliamente compatible con B. En B, el nombre de una matriz [llamada "vector" en la terminología de B] identificó una variable que contenía un puntero que inicialmente se asignó para señalar al primer elemento de una asignación del tamaño dado, de modo que si ese nombre aparecía en la lista de argumentos para una función, la función recibiría un puntero al vector. Aunque C agregó tipos de matriz "reales", cuyo nombre estaba rígidamente asociado con la dirección de la asignación en lugar de una variable de puntero que indicaría inicialmente la asignación, las matrices se descomponen en punteros que crean código que declara que una matriz de tipo C se comporta de forma idéntica al código B que declaró un vector y luego nunca modificó la variable que contiene su dirección.
En realidad, &myarray
y myarray
son la dirección base.
Si quieres ver la diferencia en lugar de usar
printf("my_array = %p/n", my_array);
printf("my_array = %p/n", &my_array);
utilizar
printf("my_array = %s/n", my_array);
printf("my_array = %p/n", my_array);
Eso es porque el nombre de la matriz ( my_array
) es diferente de un puntero a una matriz. Es un alias de la dirección de una matriz, y su dirección se define como la dirección de la matriz misma.
Sin embargo, el puntero es una variable C normal en la pila. Por lo tanto, puede tomar su dirección y obtener un valor diferente de la dirección que contiene dentro.
Escribí sobre este tema here , por favor, eche un vistazo.
La razón por la cual my_array
y &my_array
dan como resultado la misma dirección se puede entender fácilmente cuando observa el diseño de la memoria de una matriz.
Digamos que tienes una matriz de 10 caracteres (en lugar de los 100 en tu código).
char my_array[10];
Memory for my_array
ve algo así como:
+---+---+---+---+---+---+---+---+---+---+
| | | | | | | | | | |
+---+---+---+---+---+---+---+---+---+---+
^
|
Address of my_array.
En C / C ++, una matriz se desintegra al puntero al primer elemento en una expresión como
printf("my_array = %p/n", my_array);
Si examina dónde se encuentra el primer elemento de la matriz, verá que su dirección es la misma que la dirección de la matriz:
my_array[0]
|
v
+---+---+---+---+---+---+---+---+---+---+
| | | | | | | | | | |
+---+---+---+---+---+---+---+---+---+---+
^
|
Address of my_array[0].