azul medianoche
Considerar:
char amessage[] = "now is the time";
char *pmessage = "now is the time";
leo de El lenguaje de programación C2da edición que las dos afirmaciones anteriores no hacen lo mismo.
Siempre pensé que una matriz es una forma conveniente de manipular punteros para almacenar algunos datos, pero claramente este no es el caso… ¿Cuáles son las diferencias “no triviales” entre matrices y punteros en C?
Cierto, pero es una diferencia sutil. Esencialmente, el primero:
char amessage[] = "now is the time";
Define una matriz cuyos miembros viven en el espacio de pila del ámbito actual, mientras que:
char *pmessage = "now is the time";
Define un puntero que vive en el espacio de pila del alcance actual, pero que hace referencia a la memoria en otro lugar (en este, “ahora es el momento” se almacena en otro lugar de la memoria, comúnmente una tabla de cadenas).
Además, tenga en cuenta que debido a que los datos que pertenecen a la segunda definición (el puntero explícito) no se almacenan en el espacio de pila del alcance actual, no se especifica exactamente dónde se almacenarán y no se deben modificar.
Editar: como lo señalaron Mark, GMan y Pavel, también hay una diferencia cuando se usa el operador de dirección en cualquiera de estas variables. Por ejemplo, &pmessage devuelve un puntero de tipo char**, o un puntero a un puntero a chars, mientras que &amessage devuelve un puntero de tipo char[16]
-
o un puntero a una matriz de 16 caracteres (que, como un carácter **, debe ser desreferenciado dos veces como señala litb).
Si bien es cierto, esta no es la mayor diferencia. ¿Cuál es la diferencia entre &amessage y &pmessage, por ejemplo?
– Mark Ransom
-
&pmessage
26 de agosto de 2009 a las 16:12pmessage
será la dirección de&amessage
, en algún lugar de la pila. Igualmente,amessage
será la dirección de la matriz en la pila, igual que&amessage
. Sin embargo,amessage
tiene un tipo diferente a.
– GManNickG
-
26 de agosto de 2009 a las 16:24
&pmessage
No, no es indefinido. La diferencia es que el tipo dechar**
es&amessage
– puntero a puntero a char, y el tipo dechar(*)[16]
es– puntero a matriz de 16 caracteres. Esos dos tipos no son compatibles (el segundo, en particular, es simplemente la dirección del primer carácter de la cadena, mientras que el primero es la dirección de la variable que almacena la dirección del primer carácter).
– Pavel Minaev
-
26 de agosto de 2009 a las 16:24
Extraño, supongo que C hace eso. Supuse que &amessage no sería válido ya que amessage se resuelve en un puntero de constante de código. . .
– Walt W.
-
26 de agosto de 2009 a las 16:27
@Bill: No, porque la versión de matriz es en realidad solo un atajo para la creación de instancias de matriz. Entonces, la matriz se asigna en la pila y luego se carga con los datos de la cadena.
– Walt W.
-
26 de agosto de 2009 a las 17:42
Todas las demás respuestas se centraron en “apuntar a la dirección literal de la cadena frente a copiar los caracteres de la cadena en la matriz”, que es válido pero es específico del código de ejemplo del OP. Todos fallaron en mencionar esto (el resultado diferente de sizeof()), que es, en mi opinión, una diferencia muy importante entre matrices y punteros.
– Nicolás Miari
avatar de usuario
Ciro Santilli Путлер Капут 六四事
diferencias entre char pointer y array
Proyecto C99 N1256
-
Hay dos usos diferentes de los literales de cadenas de caracteres:
char[]
Inicializarchar c[] = "abc";
:
Esto es “más mágico”, y se describe en 6.7.8/14 “Inicialización”:
Una matriz de tipo de carácter puede inicializarse mediante una cadena de caracteres literal, opcionalmente encerrada entre llaves. Los caracteres sucesivos del literal de la cadena de caracteres (incluido el carácter nulo de terminación si hay espacio o si la matriz tiene un tamaño desconocido) inicializan los elementos de la matriz.
char c[] = {'a', 'b', 'c', '\0'};
Así que esto es solo un atajo para:
c
Como cualquier otra matriz regular, -
se puede modificar
- En cualquier otro lugar: genera un:
- sin nombre
- array of char ¿Cuál es el tipo de cadenas literales en C y C++?
- con almacenamiento estático
que da UB si se modifica
char *c = "abc";
Entonces cuando escribes:
/* __unnamed is magic because modifying it gives UB. */ static char __unnamed[] = "abc"; char *c = __unnamed;
Esto es similar a:
char[]
Tenga en cuenta el elenco implícito dechar *
paraque siempre es legal.
c[0]
Entonces si modificas__unnamed
también modificasque es UB.
Esto está documentado en 6.4.5 “Literales de cadena”: […]
5 En la fase de traducción 7, se agrega un byte o código de valor cero a cada secuencia de caracteres multibyte que resulta de una cadena literal o literales. La secuencia de caracteres multibyte se usa luego para inicializar una matriz de duración de almacenamiento estático y la longitud suficiente para contener la secuencia. Para los literales de cadenas de caracteres, los elementos de la matriz tienen el tipo char y se inicializan con los bytes individuales de la secuencia de caracteres multibyte.
6 No se especifica si estas matrices son distintas siempre que sus elementos tengan los valores apropiados. Si el programa intenta modificar una matriz de este tipo, el comportamiento no está definido.
6.7.8/32 “Inicialización” da un ejemplo directo:
char s[] = "abc", t[3] = "abc";
EJEMPLO 8: La declaración
s
define objetos de matriz de caracteres “simples”t
ycuyos elementos se inicializan con literales de cadenas de caracteres.
char s[] = { 'a', 'b', 'c', '\0' }, t[] = { 'a', 'b', 'c' };
Esta declaración es idéntica a
char *p = "abc";
El contenido de las matrices es modificable. Por otra parte, la declaración
p
definep
con tipo “puntero a char” y lo inicializa para apuntar a un objeto con tipo “matriz de char” con longitud 4 cuyos elementos se inicializan con una cadena de caracteres literal. Si se intenta utilizar
para modificar el contenido de la matriz, el comportamiento no está definido.
Implementación de GCC 4.8 x86-64 ELF
#include <stdio.h>
int main(void) {
char *s = "abc";
printf("%s\n", s);
return 0;
}
Programa:
gcc -ggdb -std=c99 -c main.c
objdump -Sr main.o
Compilar y descompilar:
char *s = "abc";
8: 48 c7 45 f8 00 00 00 movq $0x0,-0x8(%rbp)
f: 00
c: R_X86_64_32S .rodata
La salida contiene: char*
Conclusión: tiendas GCC .rodata
en .text
sección, no en
. char[]
Si hacemos lo mismo para
char s[] = "abc";
:
17: c7 45 f0 61 62 63 00 movl $0x636261,-0x10(%rbp)
obtenemos: %rbp
por lo que se almacena en la pila (en relación con
). .rodata
Sin embargo, tenga en cuenta que el script del enlazador predeterminado pone .text
y
readelf -l a.out
en el mismo segmento, que tiene permiso de ejecución pero no de escritura. Esto se puede observar con:
Section to Segment mapping:
Segment Sections...
02 .text .rodata
avatar de usuario
Johannes Schaub – litb
Una matriz contiene los elementos. Un puntero los señala.
char amessage[16];
amessage[0] = 'n';
amessage[1] = 'o';
...
amessage[15] = '\0';
La primera es una forma corta de decir
Es decir, es una matriz que contiene todos los caracteres. La inicialización especial lo inicializa por usted y determina su tamaño automáticamente. Los elementos de la matriz son modificables; puede sobrescribir caracteres en ellos.
char *pmessage = "now is the time";
*pmessage="p"; /* undefined behavior! */
La segunda forma es un puntero, que solo apunta a los caracteres. Almacena los caracteres no directamente. Dado que la matriz es un literal de cadena, no puede tomar el puntero y escribir hacia donde apunta
avatar de usuario
norman ramsey No puedo agregar de manera útil a las otras respuestas, pero comentaré que enSecretos profundos de C
, Peter van der Linden cubre este ejemplo en detalle. Si te haces este tipo de preguntas, creo que te encantará este libro. pmessage
PS Puede asignar un nuevo valor a amessage
. No puede asignar un nuevo valor a ; estáinmutable
-
.
@Norman, ¿seguro que hay una versión gratuita del libro?
– Pacerier
Puede que no esté recordando bien esto, pero me gustaría señalar que puedes usar el [] notación en punteros y la notación * en matrices. La única gran diferencia desde el punto de vista del código es que el valor de amessage no puede cambiar, por lo que amessage++ debería fallar (pero creo que *(amessage+1) tendrá éxito. Hay otras diferencias internas, creo, pero en realidad casi nunca importan.
– Bill K.
26 de agosto de 2009 a las 17:25
Ah, y en general (no en los casos que mencionaste), las matrices asignan memoria automáticamente, los punteros tienes que asignar tu propia memoria. El suyo debería apuntar a bloques de memoria que se asignaron como parte de la carga del programa.
– Bill K.
26 de agosto de 2009 a las 17:26
Junto con K&R (que es un gran libro, por cierto) te sugiero que leas pw2.netcom.com/~tjensen/ptr/cpoint.htm – en el ínterin.
– amaterasu
26 de agosto de 2009 a las 17:45
Consulte stackoverflow.com/a/10186799/632951
– Pacerier
14 mayo 2015 a las 13:12
Cerrando esto como duplicado ya que teníamos dos hilos de preguntas frecuentes “canónicos” sobre esta misma pregunta.
– Lundin
10 de septiembre de 2015 a las 11:52