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

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  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; } 

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 

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 stack. Por lo tanto, puede tomar su dirección y obtener un valor diferente de la dirección que contiene dentro.

Escribí sobre este tema aquí , por favor, eche un vistazo.

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.

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]. 

En el lenguaje de progtwigció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 stack, para cada nombre de variable las únicas cosas de las que el comstackdor 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 statement global como i; [no había necesidad de especificar un tipo, ya que todo era un entero / puntero] sería procesado por el comstackdor como: address_of_i = next_global++; memory[address_of_i] = 0; address_of_i = next_global++; memory[address_of_i] = 0; y una statement como i++ se procesaría como: memory[address_of_i] = memory[address_of_i]+1; .

Una statement 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 statement, el comstackdor podría olvidarse inmediatamente de que arr es una matriz . Una statement como arr[i]=6; se procesaría como memory[memory[address_of_a] + memory[address_of_i]] = 6; . Al comstackdor 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 objectives del lenguaje de progtwigció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);