¿Es un nombre de matriz un puntero?

203

¿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?

Lundin
fuente
44
No. Pero la matriz es la misma & matriz [0]
36
@pst: &array[0]produce un puntero, no una matriz;)
jalf
28
@Nava (y pst): array y & array [0] no son realmente lo mismo. Caso en cuestión: sizeof (array) y sizeof (& array [0]) dan resultados diferentes.
Thomas Padron-McCarthy
1
@Thomas está de acuerdo, pero en términos de punteros, cuando desreferencia la matriz y & matriz [0], producen el mismo valor de matriz [0] .ie * matriz == matriz [0]. Nadie quiso decir que estos dos punteros son iguales, pero en este caso específico (apuntando al primer elemento) puede usar el nombre de matriz.
Nava Carmon
1
Esto también podría ayudarlo a comprender: stackoverflow.com/questions/381542 , stackoverflow.com/questions/660752
Dinah

Respuestas:

255

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

pno contiene espacios para enteros, pero puede apuntar a un espacio para un entero. Podemos, por ejemplo, configurarlo para que apunte a uno de los lugares de la matriz a, como el primero:

p = &a[0];

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

p = a;

Esto no copia el contenido de la matriz aen el puntero p(lo que sea que eso signifique). En cambio, el nombre de la matriza se convierte en un puntero a su primer elemento. Entonces esa asignación hace lo mismo que la anterior.

Ahora puede usar pde manera similar a una matriz:

p[3] = 17;

La razón por la que esto funciona es que el operador de desreferencia de matriz en C [ ], se define en términos de punteros. x[y]significa: comience con el puntero x, avance yelementos después de lo que señala el puntero y luego tome lo que esté allí. Usando la sintaxis aritmética del puntero, x[y]también se puede escribir como*(x+y) .

Para que esto funcione con una gama normal, como nuestra a, el nombre aen a[3]primer lugar debe ser convertido a un puntero (para el primer elemento dea ). Luego avanzamos 3 elementos hacia adelante y tomamos lo que está allí. En otras palabras: tome el elemento en la posición 3 de la matriz. (Cuál es el cuarto elemento en la matriz, ya que el primero está numerado 0.)

Entonces, en resumen, los nombres de matriz en un programa C se convierten (en la mayoría de los casos) en punteros. Una excepción es cuando usamos el sizeofoperador en una matriz. Si ase convirtiera en un puntero en este contexto, sizeof adarí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 asignifica la matriz misma.

Thomas Padron-McCarthy
fuente
55
Se aplica una conversión automática similar a los punteros de función, ambos functionpointer()y (*functionpointer)()significan lo mismo, curiosamente.
Carl Norum
3
No preguntó si las matrices y los punteros son iguales, pero si el nombre de una matriz es un puntero
Ricardo Amores
32
Un nombre de matriz no es un puntero. Es un identificador para una variable de tipo de matriz, que tiene una conversión implícita a puntero de tipo de elemento.
Pavel Minaev
29
Además, aparte del sizeof()otro contexto en el que no hay matriz-> la caída del puntero es operador &: en su ejemplo anterior, &aserá un puntero a una matriz de 7 int, no un puntero a un solo int; es decir, su tipo será int(*)[7], que no es implícitamente convertible a int*. De esta forma, las funciones pueden llevar punteros a matrices de tamaño específico y aplicar la restricción a través del sistema de tipos.
Pavel Minaev
3
@ onmyway133, consulte aquí para obtener una breve explicación y más citas.
Carl Norum
37

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

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 */
pmg
fuente
20

Si una expresión de tipo de matriz (como el nombre de la matriz) aparece en una expresión más grande y no es el operando de &osizeof operadores , entonces el tipo de la expresión de matriz se convierte de "matriz de elementos N de T" a "puntero a T", y el valor de la expresión es la dirección del primer elemento de 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 solo el tamaño de los elementos de la matriz? Entonces, la "cabeza" de la matriz también ocupa espacio con la información sobre la longitud y un puntero (y esto significa que ocupa más espacio que un puntero normal).

Cuando crea una matriz, el único espacio asignado es el espacio para los elementos mismos; 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 se a refiere a toda la matriz, pero no hay ningún objeto a separado de los elementos de la matriz. Por lo tanto, sizeof ale da el tamaño (en bytes) de toda la matriz. La expresión &ale da la dirección de la matriz, que es la misma que la dirección del primer elemento . La diferencia entre &ay &a[0]es el tipo del resultado 1 - char (*)[10]en el primer caso ychar * 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, ielementos de desplazamiento ( no bytes) ) de esa dirección y desreferenciar el resultado.

El problema es que ano es un puntero o una dirección, es todo el objeto de matriz. Por lo tanto, la regla en C dice que cada vez que el compilador ve una expresión de tipo de matriz (como a, que tiene tipo char [10]) y esa expresión no es el operando de los operadores sizeofunarios &, 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 *atiene el mismo tipo y valor que la expresióna[0] ).

C se derivó de un lenguaje anterior llamado B, y en B a había 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 B, pero que no quería meterse con el almacenamiento de objeto de puntero separada. Entonces se deshizo de él. En cambio, el compilador convertirá expresiones de matriz en expresiones de puntero durante la traducción, según sea necesario.

Recuerde que dije que las matrices no almacenan metadatos sobre su tamaño. Tan pronto como esa expresión de matriz "decae" a un puntero, todo lo que tiene es un puntero a un único elemento. Ese elemento puede ser el primero de una secuencia de elementos, o puede ser un solo objeto. No hay forma de saberlo basado en el puntero mismo.

Cuando pasa una expresión de matriz a una función, todo lo que recibe la función es un puntero al primer elemento; no tiene idea de cuán grande es la matriz (es por eso que la getsfunción fue 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 cadenas C) o debe pasar el número de elementos como un parámetro separado.


  1. Qué * puede * afectar la forma en que se interpreta el valor de la dirección, depende de la máquina.
John Bode
fuente
He estado buscando por bastante tiempo esta respuesta. ¡Gracias! Y si lo sabe, ¿podría decir un poco más qué es una expresión de matriz? Si uso sizeof, ¿cuento solo el tamaño de los elementos de la matriz? Entonces, la "cabeza" de la matriz también ocupa espacio con la información sobre la longitud y un puntero (y esto significa que ocupa más espacio que un puntero normal).
Andriy Dmytruk
Y una cosa más. Una matriz de longitud 5 es de tipo int [5]. Entonces, ¿de dónde sabemos la longitud cuando llamamos sizeof (array), de su tipo? ¿Y esto significa que las matrices de diferente longitud son como diferentes tipos de constantes?
Andriy Dmytruk
@AndriyDmytruk: sizeofes un operador y evalúa el número de bytes en el operando (ya sea una expresión que denota un objeto o un nombre de tipo entre paréntesis). Entonces, para una matriz, se sizeofevalúa el número de elementos multiplicado por el número de bytes en un solo elemento. Si an inttiene 4 bytes de ancho, entonces una matriz de 5 elementos intocupa 20 bytes.
John Bode
¿No es [ ]especial el operador también? Por ejemplo, int a[2][3];entonces for x = a[1][2];, aunque se puede reescribir como x = *( *(a+1) + 2 );, aquí ano se convierte a un tipo de puntero int*(aunque si aes un argumento de una función se debe convertir a int*).
Stan
2
@Stan: La expresión atiene tipo int [2][3], que "decae" al escribir int (*)[3]. La expresión *(a + 1)tiene tipo int [3], que "decae" a int *. Por lo tanto, *(*(a + 1) + 2)tendrá tipo int. aapunta a la primera matriz de 3 elementos de int, a + 1apunta a la segunda matriz de 3 elementos de int, *(a + 1) es la segunda matriz de 3 elementos de int, *(a + 1) + 2apunta al tercer elemento de la segunda matriz de int, así *(*(a + 1) + 2) es el tercer elemento de la segunda matriz de int. La forma en que eso se asigna al código de la máquina depende completamente del compilador.
John Bode
5

Una matriz declarada así

int a[10];

asigna memoria por 10 ints. No puede modificar apero 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 ints. Puedes modificarlo:

p = a;

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

p[2] = 5;
a[2] = 5;    // same
*(p+2) = 5;  // same effect
*(a+2) = 5;  // same effect
Grumdrig
fuente
2
Las matrices no siempre se asignan en la pila. Es un detalle de implementación que variará de un compilador a otro. En la mayoría de los casos, las matrices estáticas o globales se asignarán desde una región de memoria diferente a la pila. Las matrices de tipos constantes pueden asignarse desde otra región de memoria
Mark Bessey
1
Creo Grumdrig quería decir "destina un 10 ints con duration` almacenamiento automático.
Luminosidad razas en órbita
4

El nombre de la matriz en sí mismo 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 para apuntar (por ejemplo, agregar / restar un desplazamiento), también puede hacerlo en una matriz:

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

En cuanto al lenguaje, si C no expone la matriz como una especie de "puntero" (pedagógicamente es solo una ubicación de memoria. No puede apuntar a una ubicación arbitraria en la memoria, ni puede ser controlada por el programador). Siempre necesitamos codificar esto:

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

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

#include <stdio.h>
int main()
{
        int a[3] = {9, 10, 11};
        int **b = &a;

        printf("a == &a: %d\n", a == b);
        return 0;
}

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

a == &a: 1

Uy :-)

Entonces, la conclusión es no, la matriz no es un puntero, no está almacenada en la memoria (ni siquiera 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 manera, te han advertido:

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

C ++ rechaza dichos intentos con errores en tiempo de compilación.

Editar:

Esto es lo que quise demostrar:

#include <stdio.h>
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 cy a"apunte" a la misma memoria, puede obtener la dirección del cpuntero, pero no puede obtener la dirección del apuntero.

Palo
fuente
1
"Se compila bien (con 2 advertencias)". Eso no esta bien. Si le dice a gcc que lo compile como C estándar estándar agregando -std=c11 -pedantic-errors, obtendrá un error de compilación al escribir código C no válido. La razón es porque intenta asignar int (*)[3]a una variable de int**, que son dos tipos que no tienen absolutamente nada que ver entre sí. Entonces, lo que se supone que demuestra este ejemplo, no tengo idea.
Lundin
Gracias Lundin por tu comentario. Sabes que hay muchos estándares. Traté de aclarar lo que quise decir en la edición. El int **tipo no es el punto allí, uno debería usar mejor void *para esto.
Palo
-3

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 declaraciones de impresión darán exactamente la misma salida para una máquina. En mi sistema dio:

0x7fff6fe40bc0
Amitesh Ranjan
fuente
-4

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 aplicando un desplazamiento puede acceder al resto de elementos. Un "índice al primer elemento" es de hecho un puntero a una dirección de 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 constante (es similar, no es lo mismo. Vea el comentario de Mark). Pero también que no necesita desreferenciar el nombre de la matriz para obtener el valor si usa la aritmética del puntero:

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 "sí".

Ricardo Amores
fuente
1
Un nombre de matriz no es lo mismo que un puntero constante. Dado: int a [10]; int * p = a; sizeof (p) y sizeof (a) no son lo mismo.
Mark Bessey
1
Hay otras diferencias En general, es mejor atenerse a la terminología utilizada por el Estándar C, que específicamente lo llama "conversión". Cita: "Excepto cuando es el operando del operador sizeof o el operador unario &, o es un literal de cadena utilizado para inicializar una matriz, una expresión que tiene el tipo '' matriz de tipo '' se convierte en una expresión con tipo ' 'puntero para escribir' 'que apunta al elemento inicial del objeto de matriz y no es un valor l. Si el objeto de matriz tiene una clase de almacenamiento de registro, el comportamiento no está definido ".
Pavel Minaev
-5

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

SAQIB SOHAIL BHATTI
fuente