Estoy tratando de entender los punteros en C pero actualmente estoy confundido con lo siguiente:
char *p = "hello"
Este es un puntero de caracteres que apunta a la matriz de caracteres, comenzando en h .
char p[] = "hello"
Esta es una matriz que almacena hola .
¿Cuál es la diferencia cuando paso ambas variables a esta función?
void printSomething(char *p)
{
printf("p: %s",p);
}
char p[3] = "hello";
la cadena del inicializador es demasiado larga para el tamaño de la matriz que declara. ¿Error de tipografía?char p[]="hello";
sería suficiente!char
específico.Respuestas:
char*
ychar[]
son de diferentes tipos , pero no es inmediatamente aparente en todos los casos. Esto se debe a que las matrices decaen en punteros , lo que significa que sichar[]
se proporciona una expresión de tipo dondechar*
se espera una de tipo , el compilador convierte automáticamente la matriz en un puntero a su primer elemento.Su función de ejemplo
printSomething
espera un puntero, por lo que si intenta pasarle una matriz de esta manera:El compilador finge que escribiste esto:
fuente
printf
maneja la%s
cadena de formato: comience en la dirección proporcionada y continúe hasta encontrar un terminador nulo. Si desea imprimir solo un carácter, puede utilizar la%c
cadena de formato, por ejemplo.char *p = "abc";
el carácter NULL\0
se agrega automáticamente como en el caso de la matriz char []?char *name; name="123";
pero puedo hacer lo mismo con elint
tipo? Y después de usar%c
para imprimirname
, la salida es una cadena ilegible�
:?Veamos:
foo * y foo [] son tipos diferentes y el compilador los maneja de manera diferente (puntero = dirección + representación del tipo de puntero, matriz = puntero + longitud opcional de la matriz, si se conoce, por ejemplo, si la matriz está estáticamente asignada ), los detalles se pueden encontrar en el estándar. Y a nivel de tiempo de ejecución no hay diferencia entre ellos (en ensamblador, bueno, casi, ver más abajo).
Además, hay un relacionado pregunta en las preguntas frecuentes de C :
fuente
C99 N1256 draft
Hay dos usos diferentes de los literales de cadena de caracteres:
Inicializar
char[]
:Esto es "más mágico", y se describe en 6.7.8 / 14 "Inicialización":
Entonces esto es solo un atajo para:
Al igual que cualquier otra matriz regular,
c
se puede modificar.En todas partes: genera un:
Entonces cuando escribes:
Esto es similar a:
Tenga en cuenta la conversión implícita de
char[]
achar *
, que siempre es legal.Luego, si modifica
c[0]
, también modifica__unnamed
, que es UB.Esto se documenta en 6.4.5 "Literales de cadena":
6.7.8 / 32 "Inicialización" da un ejemplo directo:
Implementación de GCC 4.8 x86-64 ELF
Programa:
Compilar y descompilar:
La salida contiene:
Conclusión: GCC lo almacena
char*
en.rodata
sección, no en.text
.Si hacemos lo mismo para
char[]
:obtenemos:
por lo que se almacena en la pila (en relación con
%rbp
).Sin embargo , tenga en cuenta que la secuencia de comandos del vinculador predeterminado coloca
.rodata
y.text
en el mismo segmento, que tiene permiso de ejecución pero no de escritura. Esto se puede observar con:que contiene:
fuente
No está permitido cambiar el contenido de una constante de cadena, que es a lo que
p
apunta el primero . El segundop
es una matriz inicializada con una constante de cadena, y puede cambiar su contenido.fuente
Para casos como este, el efecto es el mismo: terminas pasando la dirección del primer carácter en una cadena de caracteres.
Sin embargo, las declaraciones obviamente no son las mismas.
A continuación se reserva memoria para una cadena y también un puntero de caracteres, y luego se inicializa el puntero para que apunte al primer carácter de la cadena.
Mientras que lo siguiente reserva memoria solo para la cadena. Por lo tanto, en realidad puede usar menos memoria.
fuente
Hasta donde puedo recordar, una matriz es en realidad un grupo de punteros. Por ejemplo
es una afirmación verdadera
fuente
*(arr + 1)
te lleva al segundo miembro dearr
. Si*(arr)
apunta a una dirección de memoria de 32 bits, por ejemplobfbcdf5e
,*(arr + 1)
apunta abfbcdf60
(el segundo byte). Por lo tanto, por qué salir del alcance de una matriz conducirá a resultados extraños si el sistema operativo no falla por defecto. Siint a = 24;
está en la direcciónbfbcdf62
, entonces el accesoarr[2]
podría volver24
, suponiendo que no ocurra primero un segfault.De APUE , Sección 5.14:
El texto citado coincide con la explicación de @Ciro Santilli.
fuente
char p[3] = "hello"
? debechar p[6] = "hello"
recordarse que hay un carácter '\ 0' al final de una "cadena" en C.de todos modos, la matriz en C es solo un puntero al primer objeto de un objeto de ajuste en la memoria. Las únicas diferencias son en semántica. Si bien puede cambiar el valor de un puntero para apuntar a una ubicación diferente en la memoria, una matriz, una vez creada, siempre apuntará a la misma ubicación.
también cuando se usa la matriz, "nuevo" y "eliminar" se hacen automáticamente por usted.
fuente