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

10 minutos de lectura

avatar de usuario
Alejandro

En el siguiente fragmento de código, los valores de puntero y las direcciones de 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;
}

  • De las preguntas frecuentes de comp.lang.c: – [So what is meant by the “equivalence of pointers and arrays” in C? ](c-faq.com/aryptr/aryptrechev.html) – [Since array references decay into pointers, if arr is an array, what’s the difference between arr and &arr? ](c-faq.com/aryptr/aryvsadr.html) O ve a leer todo Matrices y punteros sección.

    – jamesdlin

    27 de marzo de 2010 a las 9:18

  • Agregué una respuesta con un diagrama a esta pregunta hace dos años aquí ¿Qué significa sizeof(&array) ¿devolver?

    –Grijesh Chauhan

    8 mayo 2015 a las 16:39


  • ¿Responde esto a tu pregunta? ¿Qué es el decaimiento de matriz a puntero?

    – Andreas Wenzel

    11 de enero a las 6:46

avatar de usuario
ataúd de jerry

El nombre de una matriz generalmente se evalúa como 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 será no 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 sizeof o unario & (dirección de), el nombre se refiere al propio objeto de matriz. Por lo tanto sizeof array le 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á 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 toda la matriz, 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 veces mayor que el primero (porque es una matriz de 16 caracteres). Dado que %p normalmente convierte los punteros en hexadecimal, podría tener un aspecto similar al siguiente:

0x12341000    0x12341010

  • @Alexandre: &array es un puntero al primer elemento de la matriz, donde como array se refiere a toda la matriz. La diferencia fundamental también puede observarse comparando sizeof(array)para sizeof(&array). Tenga en cuenta, sin embargo, que si pasa array como argumento de una función, sólo &array es de hecho pasado. No puede pasar una matriz por valor a menos que esté encapsulada dentro de un struct.

    – Clifford

    27 de marzo de 2010 a las 7:52

  • @Clifford: si pasa una matriz a una función, se descompone en un puntero a su primer elemento de manera tan efectiva &array[0] se pasa, no &array que sería un puntero a la matriz. Puede ser un detalle, pero creo que es importante dejarlo en claro; los compiladores advertirán si la función tiene un prototipo que coincida con el tipo del puntero pasado.

    –CB Bailey

    27 de marzo de 2010 a las 9:53

  • @Jerry Coffin Por ejemplo, int *p = &a, si quiero la dirección de memoria del puntero int p, puedo hacer &p. Dado que &array se convierte en la dirección de toda la matriz (que comienza en la dirección del primer elemento). Entonces, ¿cómo encuentro la dirección de memoria del puntero de la matriz (que almacena la dirección del primer elemento de la matriz)? Debe estar en algún lugar de la memoria, ¿verdad?

    – Juan Lee

    12 de septiembre de 2012 a las 8:44


  • @JohnLee: No, no tiene que haber un puntero a la matriz en ninguna parte de la memoria. Si crea un puntero, puede tomar su dirección: int *p = array; int **pp = &p;.

    – Jerry Ataúd

    12 de septiembre de 2012 a las 14:06

  • @Clifford, el primer comentario está mal, ¿por qué seguir conservándolo? Creo que podría generar malentendidos para aquellos que no lean la siguiente respuesta de (@Charles).

    – Rick

    9 de abril de 2018 a las 2:24


Eso es porque el nombre de 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 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í – por favor echa un vistazo.

  • &my_array no debería ser una operación no válida ya que el valor de my_array no está en la pila, solo my_array[0…length] ¿son? Entonces todo tendría sentido…

    – Alejandro

    27 de marzo de 2010 a las 6:22

  • @Alexandre: No estoy seguro de por qué está permitido, en realidad.

    – Eli Bendersky

    27 de marzo de 2010 a las 6:46

  • Puede tomar la dirección de cualquier variable (si no está marcada register) cualquiera que sea su duración de almacenamiento: estático, dinámico o automático.

    –CB Bailey

    27 de marzo de 2010 a las 10:00

  • my_array en sí mismo está en la pila, porque my_array es toda la matriz.

    – café

    27 de marzo de 2010 a las 12:42

  • my_arraycuando no sea objeto de la & o sizeof operadores, se evalúa como un puntero a su primer elemento (es decir, &my_array[0]) – pero my_array en sí mismo es no ese puntero (my_array sigue siendo la matriz). Ese puntero es solo un valor efímero (por ejemplo, dado int a;es como a + 1) – conceptualmente al menos se “calcula según sea necesario”. El verdadero “valor” de my_array es el contenido de toda la matriz; es solo que fijar este valor en C es como tratar de atrapar niebla en un frasco.

    – café

    28 de marzo de 2010 a las 21:18

avatar de usuario
CB Bailey

En C, cuando usa el nombre de una matriz en una expresión (incluido pasarlo a una función), a menos que sea el operando de la dirección de (&) operador o el sizeof operador, es decae a un puntero a su primer elemento.

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

En tu ejemplo, my_array tiene tipo char[100] que decae a un char* cuando lo pasas a printf.

&my_array tiene tipo char (*)[100] (puntero a matriz de 100 char). Como es el operando de &este es uno de los casos que my_array no decae inmediatamente a 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 realiza aritmética de punteros en los dos tipos de punteros.

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

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

La razón por la cual my_array y &my_array El resultado en la misma dirección se puede entender fácilmente cuando observa el diseño de 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];

Memoria para my_array se ve algo como:

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

En C/C++, una matriz se descompone en 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].

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 hacer 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; [there was no need to specify a type, since everything was an integer/pointer] sería procesado por el compilador como: address_of_i = next_global++; memory[address_of_i] = 0; y una declaración como i++ sería procesado como: memory[address_of_i] = memory[address_of_i]+1;.

Una declaración como arr[10]; sería procesado 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 arr siendo una matriz. Una declaración como arr[i]=6; sería procesado como memory[memory[address_of_a] + memory[address_of_i]] = 6;. Al compilador no le importaría si arr representó una matriz y i un número entero, o viceversa. De hecho, no importaría si ambos fueran arreglos o ambos enteros; generaría felizmente 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 [called a “vector” in the terminology of B] identificó una variable que contenía un puntero que se asignó inicialmente para apuntar al primer elemento de una asignación del tamaño dado, por lo que si ese nombre aparecía en la lista de argumentos de una función, la función recibiría un puntero al vector. Aunque C agregó tipos de matrices “reales”, cuyo nombre se asoció rígidamente con la dirección de la asignación en lugar de una variable de puntero que apuntaría inicialmente a la asignación, hacer que las matrices se descompusieran en punteros hizo que el código que declaraba una matriz de tipo C se comportara de manera idéntica. al código B que declaró un vector y luego nunca modificó la variable que contiene su dirección.

avatar de usuario
glglgl

Realmente &myarray y myarray ambos 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);

usar

printf("my_array = %s\n", my_array);
printf("my_array = %p\n", my_array);

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad