¿Cómo es que la dirección de una matriz es igual a su valor en C?

Resuelto Alexandre asked hace 14 años • 6 respuestas

En el siguiente fragmento de código, los valores y las direcciones del puntero difieren como se esperaba.

¡Pero los valores de matriz y las direcciones no!

¿Cómo puede ser esto?

Producción

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;
}
Alexandre avatar Mar 27 '10 12:03 Alexandre
Aceptado

El nombre de una matriz generalmente se evalúa como la dirección del primer elemento de la matriz, por lo que arrayy &arraytiene el mismo valor (pero de diferentes tipos, por lo que array+1y no&array+1 será igual si la matriz tiene más de 1 elemento de largo).

Hay dos excepciones a esto: cuando el nombre de la matriz es un operando de sizeofo unario &(dirección de), el nombre se refiere al objeto de la matriz en sí. Por lo tanto, sizeof arrayle proporciona 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á tipo T *. Cuando/si lo incrementas, llegas al siguiente elemento de la matriz.

&arrayevalú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 toda la matriz, no el tamaño de un solo elemento. Por ejemplo, con código como este:

char array[16];
printf("%p\t%p", (void*)&array, (void*)(&array+1));

Podemos esperar que el segundo puntero sea 16 mayor que el primero (porque es una matriz de 16 caracteres). Dado que %p normalmente convierte punteros en hexadecimal, podría verse así:

0x12341000    0x12341010
Jerry Coffin avatar Mar 27 '2010 06:03 Jerry Coffin

Esto se debe a que el nombre de la matriz ( my_array) es diferente de un puntero a la matriz. Es un alias de la dirección de una matriz y su dirección se define como la dirección de la propia matriz.

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.

Escribí sobre este tema aquí ; échale un vistazo.

Eli Bendersky avatar Mar 27 '2010 06:03 Eli Bendersky

En C, cuando se utiliza el nombre de una matriz en una expresión (incluido pasarlo a una función), a menos que sea el operando del &operador de dirección de () o el sizeofoperador, se descompone en un puntero a su primer elemento.

Es decir, en la mayoría de los contextos arrayes equivalente &array[0]tanto en tipo como en valor.

En su ejemplo, my_arraytiene un tipo char[100]que decae a char*cuando lo pasa a printf.

&my_arraytiene tipo char (*)[100](puntero a una matriz de 100 char). Como es el operando de &, este es uno de los casos que my_arrayno 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 haces aritmética de punteros en los dos tipos de punteros.

pointer_to_arraytiene tipo char *- inicializado para apuntar al primer elemento de la matriz, ya que eso es lo que my_arraydecae en la expresión del inicializador - y &pointer_to_array tiene tipo char **(puntero a un puntero a a char).

De estos: my_array(después de desintegrarse en char*), &my_arrayy pointer_to_arraytodos apuntan directamente a la matriz o al primer elemento de la matriz y, por lo tanto, tienen el mismo valor de dirección.

CB Bailey avatar Mar 27 '2010 10:03 CB Bailey

El motivo my_arrayy &my_arrayel resultado en la misma dirección se pueden entender fácilmente cuando se observa el diseño de la memoria de una matriz.

Digamos que tiene una matriz de 10 caracteres (en lugar de los 100 en su código).

char my_array[10];

La memoria para my_arrayse parece a:

+---+---+---+---+---+---+---+---+---+---+
|   |   |   |   |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+---+---+
^
|
Address of my_array.

En C/C++, una matriz decae hasta el 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].
R Sahu avatar Feb 17 '2015 22:02 R Sahu

En el lenguaje de programación B, que fue el predecesor inmediato de C, los punteros y los números 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, lo único que el compilador tenía que realizar un seguimiento era si era una variable global o local, y su dirección relativa a la primera variable global o local. variable.

Dada una declaración global como i;[no había necesidad de especificar un tipo, ya que todo era un número entero/puntero] sería procesada por el compilador como: address_of_i = next_global++; memory[address_of_i] = 0;y una declaración como i++sería procesada 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;. Tenga en cuenta que tan pronto como se procesó esa declaración, el compilador podría olvidarse inmediatamente de arrser 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 arrrepresenta una matriz y iun número entero, o viceversa. De hecho, no importaría si ambos fueran matrices o ambos números enteros; generaría perfectamente el código como se describe, sin tener en cuenta si el comportamiento resultante probablemente sería útil.

Uno de los objetivos del lenguaje de programación C era ser en gran medida compatible con B. En B, el nombre de una matriz [llamada "vector" en la terminología de B] identificaba una variable que contenía un puntero que inicialmente se asignó para apuntar a al primer elemento de una asignación del tamaño dado, por lo que si ese nombre apareciera en la lista de argumentos de una función, la función recibiría un puntero al vector. A pesar de que C agregó tipos de matrices "reales", cuyo nombre estaba rígidamente asociado con la dirección de la asignación en lugar de una variable de puntero que inicialmente apuntaría a la asignación, al descomponer las matrices en punteros, el código que declaraba una matriz de tipo C se comportaba de manera idéntica. al código B que declaró un vector y luego nunca modificó la variable que contiene su dirección.

supercat avatar Sep 14 '2015 17:09 supercat