¿El nombre de una matriz es un puntero?

13 minutos de lectura

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

  • No pero formación es el mismo &formación[0]

    usuario166390

    29 de octubre de 2009 a las 6:51

  • @pst: &array[0] produce un puntero, no una matriz;)

    – jalf

    29 de octubre de 2009 a las 6:55

  • @Nava (y pst): formación y &formación[0] no son realmente lo mismo. Caso en punto: tamaño de (matriz) y tamaño de(&matriz[0]) dar resultados diferentes.

    – Thomas Padrón-McCarthy

    29 de octubre de 2009 a las 7:50

  • @Thomas está de acuerdo, pero en términos de punteros, cuando elimina la referencia de 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 Carmón

    29 de octubre de 2009 a las 11:12

  • Estos también pueden ayudarlo a comprender: stackoverflow.com/questions/381542, stackoverflow.com/questions/660752

    – Dina

    29 de octubre de 2009 a las 15:06

avatar de usuario
Thomas Padrón-McCarthy

Una matriz es una matriz y un puntero es un puntero, pero en la mayoría de los casos los nombres de las matrices son convertido a punteros. Un término que se usa a menudo es que ellos decadencia a 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 espacios para números enteros, pero puede apuntar a un espacio para un número entero. Podemos, por ejemplo, configurarlo para que apunte a uno de los lugares en la matriz acomo el primero:

p = &a[0];

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

p = a;

Esto hace no copiar el contenido 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 asignación hace lo mismo que la anterior.

Ahora puedes usar p de manera similar a una matriz:

p[3] = 17;

La razón por la que esto funciona es que el operador de desreferenciación de matriz en C, [ ]se define en términos de punteros. x[y] significa: empezar con el puntero xpaso y elementos hacia adelante después de lo que apunta el puntero, y luego tomar lo que sea que esté allí. Usando la sintaxis aritmética de punteros, x[y] también se puede escribir como *(x+y).

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

Entonces, en resumen, los nombres de las matrices en un programa C se convierten (en la mayoría de los casos) en punteros. Una excepción es cuando usamos el sizeof operador en una matriz. Si a se convirtió 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 en sí.

  • Se aplica una conversión automática similar a los punteros de función, tanto functionpointer() y (*functionpointer)() significan lo mismo, por extraño que parezca.

    –Carl Norum

    29 de octubre de 2009 a las 6:52

  • No preguntó si las matrices y los punteros son lo mismo, pero si el nombre de una matriz es un puntero.

    – Ricardo Amores

    29 de octubre de 2009 a las 6:58

  • Un nombre de matriz no es un puntero. Es un identificador para una variable de tipo matriz, que tiene una conversión implícita a puntero de tipo elemento.

    – Pavel Minaev

    29 de octubre de 2009 a las 7:24

  • También, aparte de sizeof()el otro contexto en el que no hay array->pointer decay es operator & – en tu ejemplo anterior, &a será un puntero a una matriz de 7 intni un puntero a un solo int; es decir, su tipo será int(*)[7]que no es implícitamente convertible a int*. De esta manera, las funciones pueden llevar punteros a arreglos de tamaño específico y hacer cumplir la restricción a través del sistema de tipos.

    – Pavel Minaev

    29 de octubre de 2009 a las 7:25

  • @ onmyway133, comprobar aquí para una breve explicación y más citas.

    –Carl Norum

    12 de febrero de 2015 a las 15:39

avatar de usuario
pmg

Cuando se utiliza 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 */

avatar de usuario
Juan Bode

Si una expresión de tipo matriz (como el nombre de la matriz) aparece en una expresión más grande y no es el operando de ninguno de los & o sizeof operadores, el tipo de la expresión de matriz se convierte de “matriz de N elementos 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 a 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 ocupa más espacio que un puntero normal).

Cuando crea una matriz, el único espacio que se asigna es el espacio para los elementos mismos; no se materializa ningún 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]
   +---+

Él expresión a se refiere a toda la matriz, pero no hay objeto a separados de los propios elementos de la matriz. Por lo tanto, sizeof a le da el tamaño (en bytes) de toda la matriz. La expresion &a le da la dirección de la matriz, que es igual a la dirección del primer elemento. La diferencia entre &a y &a[0] es el tipo del resultado1char (*)[10] en el primer caso y char * 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 acompensar i elementos (no bytes) de esa dirección y desreferenciar el resultado.

El problema es ese a no es un puntero o una dirección, es el objeto de matriz completo. Por lo tanto, la regla en C de que siempre que el compilador vea una expresión de tipo matriz (como aque tiene tipo char [10]) y esa expresión no es el operando de la sizeof o unario & operadores, el tipo de esa expresión se convierte (“descompone”) en 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 matriz de B, pero no quería meterse con el almacenamiento del objeto puntero separado. Así que se deshizo de él. En su lugar, el compilador convertirá las expresiones de matriz en expresiones de puntero durante la traducción, según sea necesario.

Recuerde que dije que las matrices no almacenan ningún metadato sobre su tamaño. Tan pronto como esa expresión de matriz “decae” 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 manera de saber basado en el propio puntero.

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 qué tan grande es la matriz (es por eso que la gets función era una gran 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 la cantidad de elementos como un parámetro separado.


  1. Lo que *puede* afectar la forma en que se interpreta el valor de la dirección, depende de la máquina.

  • He estado buscando durante mucho tiempo esta respuesta. ¡Gracias! Y si lo sabe, ¿podría explicar un poco más qué es una expresión de matriz? 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 ocupa más espacio que un puntero normal).

    –Andriy Dmytruk

    7 dic 2017 a las 21:51

  • Y una cosa más. Una matriz de longitud 5 es de tipo int[5]. Entonces, de ahí es de donde 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

    9 de diciembre de 2017 a las 13:09

  • @AndriyDmytruk: sizeof es un operador, y se evalúa como el número 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, sizeof evalúa el número de elementos multiplicado por el número de bytes en un solo elemento. Si una int tiene 4 bytes de ancho, entonces una matriz de 5 elementos de int ocupa 20 bytes.

    – Juan Bode

    9 dic 2017 a las 23:40

  • no es el operador [ ] especial también? Por ejemplo, int a[2][3];entonces para x = a[1][2];aunque se puede reescribir como x = *( *(a+1) + 2 );aquí a no se convierte en un tipo de puntero int* (aunque si a es un argumento de una función en la que debe convertirse int*).

    – Stan

    21 de junio de 2018 a las 17:17

  • @Stan: La expresión a tiene tipo int [2][3]que “decae” para escribir int (*)[3]. La expresion *(a + 1) tiene tipo int [3]que “decae” a int *. Por lo tanto, *(*(a + 1) + 2) tendrá tipo int. a apunta a la primera matriz de 3 elementos de int, a + 1 apunta a la segunda matriz de 3 elementos de int, *(a + 1) es la segunda matriz de 3 elementos de int, *(a + 1) + 2 apunta al tercer elemento de la segunda matriz de intasi que *(*(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.

    – Juan Bode

    21 de junio de 2018 a las 21:03


avatar de usuario
grumdrig

Una matriz declarada así

int a[10];

asigna memoria para 10 ints. no puedes modificar a pero puedes hacer aritmética de punteros con a.

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

int *p;

No asigna ninguna ints. 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

avatar de usuario
miguel buen

El nombre de la matriz por 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 con el puntero (por ejemplo, agregar/restar un desplazamiento), también puede hacer con una matriz:

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

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

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

  • ¿Este código no causa UB cuando sizeof (int*) != sizeof (void*)? Para ser justos, no conozco ningún sistema donde este sea el caso.

    – 12431234123412341234123

    30 de julio de 2021 a las 20:16

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

ups 🙂

Entonces, la conclusión es que no, la matriz no es un puntero, no se almacena en la memoria (ni siquiera de solo lectura) como un puntero, aunque parece que lo es, ya que puede obtener su dirección con el operador & . Pero, vaya, ese operador no funciona :-)), de cualquier manera, se le advirtió:

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

A pesar de c y a “apuntar” a la misma memoria, puede obtener la dirección de la c puntero, pero no puede obtener la dirección del a puntero.

  • ¿Este código no causa UB cuando sizeof (int*) != sizeof (void*)? Para ser justos, no conozco ningún sistema donde este sea el caso.

    – 12431234123412341234123

    30 de julio de 2021 a las 20:16

avatar de usuario
Cosmo Sterin

El siguiente ejemplo proporciona una diferencia concreta entre un nombre de matriz y un puntero. Digamos que desea representar una línea 1D con una dimensión máxima dada, puede hacerlo con una matriz o un puntero:

typedef struct {
   int length;
   int line_as_array[1000];
   int* line_as_pointer;
} Line;

Ahora veamos el comportamiento del siguiente código:


void do_something_with_line(Line line) {
   line.line_as_pointer[0] = 0;
   line.line_as_array[0] = 0;
}

void main() {
   Line my_line;
   my_line.length = 20;
   my_line.line_as_pointer = (int*) calloc(my_line.length, sizeof(int));

   my_line.line_as_pointer[0] = 10;
   my_line.line_as_array[0] = 10;

   do_something_with_line(my_line);

   printf("%d %d\n", my_line.line_as_pointer[0], my_line.line_as_array[0]);
};

Este código generará:

0 10

Eso es porque en la llamada de función a do_something_with_line el objeto fue copiado así:

  1. el puntero line_as_pointer todavía contiene la misma dirección a la que apuntaba
  2. la matriz line_as_array se copió a una nueva dirección que no supera el alcance de la función

Entonces, mientras que los arreglos no están dados por valores cuando los ingresas directamente a las funciones, cuando los encapsulas en estructuras, están dados por valor (es decir, copiados), lo que describe aquí una gran diferencia en el comportamiento en comparación con la implementación que usa punteros.

¿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