@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.
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.
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 */
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
La expresión sea refiere a toda la matriz, pero no hay ningún objetoa 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óna 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 ahabí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.
Qué * puede * afectar la forma en que se interpreta el valor de la dirección, depende de la máquina.
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
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]);
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.
"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:
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:
chararray="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'
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.
&array[0]
produce un puntero, no una matriz;)Respuestas:
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:
a
contiene espacio para siete enteros, y puede poner un valor en uno de ellos con una asignación, como esta:Aquí hay un puntero:
p
no 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 matriza
, como el primero:Lo que puede ser confuso es que también puedes escribir esto:
Esto no copia el contenido de la matriz
a
en el punterop
(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
p
de manera similar a una matriz: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 punterox
, avancey
elementos 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 nombrea
ena[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
sizeof
operador en una matriz. Sia
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 casoa
significa la matriz misma.fuente
functionpointer()
y(*functionpointer)()
significan lo mismo, curiosamente.sizeof()
otro contexto en el que no hay matriz-> la caída del puntero es operador&
: en su ejemplo anterior,&a
será un puntero a una matriz de 7int
, no un puntero a un soloint
; es decir, su tipo seráint(*)[7]
, que no es implícitamente convertible aint*
. 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.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.
fuente
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:
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
lo que obtienes en la memoria es
La expresión se
a
refiere a toda la matriz, pero no hay ningún objetoa
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 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óna
,i
elementos de desplazamiento ( no bytes) ) de esa dirección y desreferenciar el resultado.El problema es que
a
no 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 (comoa
, que tiene tipochar [10]
) y esa expresión no es el operando de los operadoressizeof
unarios&
, 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óna
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ó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 matriza[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
gets
funció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.fuente
sizeof
es 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, sesizeof
evalúa el número de elementos multiplicado por el número de bytes en un solo elemento. Si anint
tiene 4 bytes de ancho, entonces una matriz de 5 elementosint
ocupa 20 bytes.[ ]
especial el operador también? Por ejemplo,int a[2][3];
entonces forx = a[1][2];
, aunque se puede reescribir comox = *( *(a+1) + 2 );
, aquía
no se convierte a un tipo de punteroint*
(aunque sia
es un argumento de una función se debe convertir aint*
).a
tiene tipoint [2][3]
, que "decae" al escribirint (*)[3]
. La expresión*(a + 1)
tiene tipoint [3]
, que "decae" aint *
. Por lo tanto,*(*(a + 1) + 2)
tendrá tipoint
.a
apunta a la primera matriz de 3 elementos deint
,a + 1
apunta a la segunda matriz de 3 elementos deint
,*(a + 1)
es la segunda matriz de 3 elementos deint
,*(a + 1) + 2
apunta al tercer elemento de la segunda matriz deint
, así*(*(a + 1) + 2)
es el tercer elemento de la segunda matriz deint
. La forma en que eso se asigna al código de la máquina depende completamente del compilador.Una matriz declarada así
asigna memoria por 10
int
s. No puede modificara
pero puede hacer aritmética de puntero cona
.Un puntero como este asigna memoria solo para el puntero
p
:No asigna ningún
int
s. Puedes modificarlo:y use subíndices de matriz como pueda con un:
fuente
int
s con duration` almacenamiento automático.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:
Y otras cosas ingeniosas que puede hacer para apuntar (por ejemplo, agregar / restar un desplazamiento), también puede hacerlo en una matriz:
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:
fuente
Creo que este ejemplo arroja algo de luz sobre el tema:
Se compila bien (con 2 advertencias) en gcc 4.9.2 e imprime lo siguiente:
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:
C ++ rechaza dichos intentos con errores en tiempo de compilación.
Editar:
Esto es lo que quise demostrar:
Aunque
c
ya
"apunte" a la misma memoria, puede obtener la dirección delc
puntero, pero no puede obtener la dirección dela
puntero.fuente
-std=c11 -pedantic-errors
, obtendrá un error de compilación al escribir código C no válido. La razón es porque intenta asignarint (*)[3]
a una variable deint**
, 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.int **
tipo no es el punto allí, uno debería usar mejorvoid *
para esto.El nombre de la matriz se comporta como un puntero y apunta al primer elemento de la matriz. Ejemplo:
Ambas declaraciones de impresión darán exactamente la misma salida para una máquina. En mi sistema dio:
fuente
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:
Entonces la respuesta es un "sí".
fuente
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.
fuente