¿El nombre de una matriz es un puntero?

¿El nombre de una matriz es un puntero en C? Si no, ¿cuál es la diferencia entre el nombre de una matriz y una variable de puntero?

Una matriz es una matriz y un puntero es un puntero, pero en la mayoría de los casos, los nombres de matriz se convierten en punteros. Un término que se usa a menudo es que se descomponen en punteros.

Aquí hay una matriz:

 int a[7]; 

a contiene espacio para siete enteros, y puede poner un valor en uno de ellos con una asignación, como esta:

 a[3] = 9; 

Aquí hay un puntero:

 int *p; 

p no contiene ningún espacio para enteros, pero puede apuntar a un espacio para un entero. Podemos, por ejemplo, configurarlo para que apunte a uno de los lugares del conjunto a , como el primero:

 p = &a[0]; 

Lo que puede ser confuso es que también puedes escribir esto:

 p = a; 

Esto no copia los contenidos de la matriz a en el puntero p (lo que sea que eso signifique). En cambio, el nombre de la matriz a se convierte en un puntero a su primer elemento. Entonces esa tarea hace lo mismo que la anterior.

Ahora puede usar p de forma similar a una matriz:

 p[3] = 17; 

La razón por la que esto funciona es porque el operador de eliminación de referencias de matriz en C, “[]”, se define en términos de punteros. x [y] significa: comience con el puntero x , el paso y los elementos hacia adelante después de lo que señala el puntero, y luego tome lo que esté allí. Usando la syntax aritmética del puntero, x [y] también se puede escribir como * (x + y) .

Para que esto funcione con una matriz normal, como nuestra a , el nombre a en a [3] debe convertirse primero en un puntero (al primer elemento en a ). Luego avanzamos 3 elementos hacia adelante y tomamos lo que está allí. En otras palabras: toma el elemento en la posición 3 en la matriz. (Que es el cuarto elemento de la matriz, ya que el primero está numerado 0.)

Por lo tanto, en resumen, los nombres de matriz en un progtwig C se convierten (en la mayoría de los casos) en punteros. Una excepción es cuando usamos el operador sizeof en una matriz. Si a se convirtiera en un puntero en este contexto, sizeof (a) daría el tamaño de un puntero y no de la matriz real, lo que sería bastante inútil, por lo que en ese caso a significa la matriz misma.

Cuando una matriz se usa como un valor, su nombre representa la dirección del primer elemento.
Cuando una matriz no se usa como un valor, su nombre representa toda la matriz.

 int arr[7]; /* arr used as value */ foo(arr); int x = *(arr + 1); /* same as arr[1] */ /* arr not used as value */ size_t bytes = sizeof arr; void *q = &arr; /* void pointers are compatible with pointers to any object */ 

Si aparece una expresión de tipo de matriz (como el nombre de la matriz) en una expresión más grande y no es el operando de los operadores & o de los operadores, entonces el tipo de expresión de la matriz se convierte de “N-element array of T” “a” puntero a T “, y el valor de la expresión es la dirección del primer elemento en la matriz.

En resumen, el nombre de la matriz no es un puntero, pero en la mayoría de los contextos se trata como si fuera un puntero.

Editar

Respondiendo la pregunta en el comentario:

Si uso sizeof, ¿cuento el tamaño de solo los elementos de la matriz? Entonces la matriz “cabeza” también ocupa espacio con la información sobre la longitud y un puntero (y esto significa que se necesita más espacio, que un puntero normal).

Cuando crea una matriz, el único espacio asignado es el espacio para los elementos en sí; no se materializa el almacenamiento para un puntero separado o cualquier metadato. Dado

 char a[10]; 

lo que obtienes en la memoria es

  +---+ a: | | a[0] +---+ | | a[1] +---+ | | a[2] +---+ ... +---+ | | a[9] +---+ 

La expresión a refiere a toda la matriz, pero no hay ningún objeto separado de los elementos de la matriz. Por lo tanto, sizeof a le da el tamaño (en bytes) de toda la matriz. La expresión &a le da la dirección de la matriz, que es la misma que la dirección del primer elemento . La diferencia entre &a y &a[0] es el tipo de resultado 1int (*)[10] en el primer caso e int * en el segundo.

Donde las cosas se ponen raras es cuando quieres acceder a elementos individuales: la expresión a[i] se define como el resultado de *(a + i) – dado un valor de dirección a , compensa i elementos ( no bytes ) de esa dirección y desreferencia el resultado.

El problema es que a no es un puntero o una dirección: es todo el objeto de la matriz. Por lo tanto, la regla en C que cada vez que el comstackdor ve una expresión del tipo de matriz (como a , que tiene el tipo char [10] ) y esa expresión no es el operando del sizeof o unary & operadores, el tipo de esa expresión se convierte (“decae”) a un tipo de puntero ( char * ), y el valor de la expresión es la dirección del primer elemento de la matriz. Por lo tanto, la expresión a tiene el mismo tipo y valor que la expresión &a[0] (y por extensión, la expresión *a tiene el mismo tipo y valor que la expresión a[0] ).

C se derivó de un lenguaje anterior llamado B, y en B a era un objeto puntero separado de los elementos de la matriz a[0] , a[1] , etc. Ritchie quería mantener la semántica de la matriz de B, pero no quería lío con el almacenamiento del objeto puntero por separado. Entonces él se deshizo de eso. En cambio, el comstackdor convertirá expresiones de matriz a expresiones de puntero durante la traducción, según sea necesario.

Recuerda que dije que las matrices no almacenan ningún metadato sobre su tamaño. Tan pronto como la expresión de la matriz “se descompone” en un puntero, todo lo que tiene es un puntero a un solo elemento. Ese elemento puede ser el primero de una secuencia de elementos, o puede ser un solo objeto. No hay forma de saber en función del puntero.

Cuando pasa una expresión de matriz a una función, toda la función que recibe es un puntero al primer elemento; no tiene idea de qué tan grande es la matriz (esta es la razón por la cual la función gets era una amenaza y finalmente se eliminó de la biblioteca) . Para que la función sepa cuántos elementos tiene la matriz, debe usar un valor centinela (como el terminador 0 en las cadenas C) o debe pasar la cantidad de elementos como un parámetro separado.


  1. Lo cual * puede * afectar la interpretación del valor de la dirección, depende de la máquina.

Una matriz declarada así

 int a[10]; 

asigna memoria para 10 int s. No puede modificar a pero puede hacer aritmética de puntero con a .

Un puntero como este asigna memoria solo para el puntero p :

 int *p; 

No asigna ningún int s. Puedes modificarlo:

 p = a; 

y use subíndices de matriz como pueda con a:

 p[2] = 5; a[2] = 5; // same *(p+2) = 5; // same effect *(a+2) = 5; // same effect 

El nombre de la matriz por sí solo produce una ubicación de memoria, por lo que puede tratar el nombre de la matriz como un puntero:

 int a[7]; a[0] = 1976; a[1] = 1984; printf("memory location of a: %p", a); printf("value at memory location %p is %d", a, *a); 

Y otras cosas ingeniosas que puede hacer con el puntero (por ejemplo, sumr / restar un desplazamiento), también puede hacerlo a una matriz:

 printf("value at memory location %p is %d", a + 1, *(a + 1)); 

Language-wise, si C no expone la matriz como una especie de “puntero” (pedante es simplemente una ubicación de memoria. No puede señalar una ubicación arbitraria en la memoria, ni puede ser controlado por el progtwigdor). Siempre necesitamos codificar esto:

 printf("value at memory location %p is %d", &a[1], a[1]); 

Creo que este ejemplo arroja algo de luz sobre el tema:

 #include  int main() { int a[3] = {9, 10, 11}; int **b = &a; printf("a == &a: %d\n", a == b); return 0; } 

Comstack bien (con 2 advertencias) en gcc 4.9.2 e imprime lo siguiente:

 a == &a: 1 

oops 🙂

Entonces, la conclusión es no, la matriz no es un puntero, no está almacenada en la memoria (ni siquiera en la de solo lectura) como un puntero, aunque parezca que sí, ya que puede obtener su dirección con el operador & . Pero, vaya, ese operador no funciona :-)), de cualquier forma, te han advertido:

 pc: In function 'main': pp.c:6:12: warning: initialization from incompatible pointer type int **b = &a; ^ pc:8:28: warning: comparison of distinct pointer types lacks a cast printf("a == &a: %d\n", a == b); 

C ++ rechaza cualquier bash de este tipo con errores en tiempo de comstackción.

Editar:

Esto es lo que quise demostrar:

 #include  int main() { int a[3] = {9, 10, 11}; void *c = a; void *b = &a; void *d = &c; printf("a == &a: %d\n", a == b); printf("c == &c: %d\n", c == d); return 0; } 

Aunque c y a “punto” en la misma memoria, puede obtener la dirección del puntero c , pero no puede obtener la dirección del puntero a.

El nombre de la matriz se comporta como un puntero y apunta al primer elemento de la matriz. Ejemplo:

 int a[]={1,2,3}; printf("%p\n",a); //result is similar to 0x7fff6fe40bc0 printf("%p\n",&a[0]); //result is similar to 0x7fff6fe40bc0 

Ambas instrucciones de impresión darán exactamente la misma salida para una máquina. En mi sistema dio:

 0x7fff6fe40bc0 

Una matriz es una colección de elementos secuenciales y contiguos en la memoria. En C, el nombre de una matriz es el índice del primer elemento y, al aplicar una compensación, puede acceder al rest de los elementos. Un “índice para el primer elemento” es de hecho un puntero a la dirección de la memoria.

La diferencia con las variables de puntero es que no puede cambiar la ubicación a la que apunta el nombre de la matriz, por lo que es similar a un puntero const (es similar, no es lo mismo. Consulte el comentario de Mark). Pero también que no necesita desreferenciar el nombre de la matriz para obtener el valor si usa puntero aritmético:

 char array = "hello wordl"; char* ptr = array; char c = array[2]; //array[2] holds the character 'l' char *c1 = ptr[2]; //ptr[2] holds a memory direction that holds the character 'l' 

Entonces la respuesta es un poco ‘sí’.

El nombre de la matriz es la dirección del primer elemento de una matriz. Entonces sí, el nombre de la matriz es un puntero de const.