¿Cuál es la diferencia entre la matriz de caracteres y el puntero de caracteres en C?

10 minutos de lectura

avatar de usuario
diesel

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

  • Esto no sería válido: char p[3] = "hello"; La cadena del inicializador es demasiado larga para el tamaño de la matriz que declara. ¿Error de tipografía?

    – Cody gris

    17 de abril de 2012 a las 7:17

  • O solo char p[]="hello"; ¡bastaría!

    – bucear profundo

    2 de julio de 2014 a las 10:03

  • posible duplicado de C: diferencias entre el puntero char y la matriz

    – sashoalm

    1 de febrero de 2015 a las 18:27

  • posible duplicado de ¿Cuál es la diferencia entre char s[] y char *s en C? Cierto, esto también pregunta específicamente sobre el parámetro de función, pero eso no es char específico.

    – Ciro Santilli Путлер Капут 六四事

    5 de junio de 2015 a las 7:44

  • usted necesita entender que son fundamentalmente diferentes. el único punto en común en esto es que la base de la arry p[] es un puntero const que permitió acceder a la matriz p[] a través de un puntero. pag[] en sí mismo contiene memoria para una cadena, mientras que *p solo apunta a la dirección del primer elemento de UN solo CARÁCTER (es decir, apunta a la base de la cadena ya asignada). Para ilustrar mejor esto, considere lo siguiente: char *cPtr = {‘h’,’e’,’l’,’l’,’o’, ‘\0’}; ==>esto es un error, ya que cPtr es un puntero a solo un carácter char cBuff[] = {‘h’, ‘e’,’l’,’l’,’o’,’\0′}; ==>Esto está bien, porque cBuff en sí mismo es una matriz de caracteres

    – Ilavarasan

    27 de octubre de 2015 a las 3:20


avatar de usuario
Jon

char* y char[] son diferentes tipos, pero no es evidente de inmediato en todos los casos. Esto se debe a que las matrices decaer en punteroslo que significa que si una expresión de tipo char[] se proporciona donde uno de tipo char* se espera, el compilador convierte automáticamente la matriz en un puntero a su primer elemento.

Tu función de ejemplo printSomething espera un puntero, por lo que si intenta pasarle una matriz de esta manera:

char s[10] = "hello";
printSomething(s);

El compilador finge que escribiste esto:

char s[10] = "hello";
printSomething(&s[0]);

  • ¿Algo ha cambiado desde 2012 hasta ahora? Para una matriz de caracteres, “s” imprime toda la matriz… es decir, “hola”

    – Bhanu-Tez

    9 de mayo de 2019 a las 6:48

  • @BhanuTez No, cómo se almacenan los datos y qué se hace con los datos son preocupaciones separadas. Este ejemplo imprime la cadena completa porque así es como printf maneja el %s cadena de formato: comience en la dirección proporcionada y continúe hasta encontrar un terminador nulo. Si quisiera imprimir solo un carácter, podría usar el %c cadena de formato, por ejemplo.

    – jacobq

    17 de junio de 2019 a las 10:59

  • Solo quería preguntar si char *p = "abc"; el caracter NULO \0 se agrega automáticamente como en el caso de char [] ¿formación?

    – ajaysinghnegi

    8 de julio de 2019 a las 17:53

  • por qué puedo configurar char *name; name="123"; pero puede hacer lo mismo con int ¿tipo? Y después de usar %c imprimir name la salida es una cadena ilegible: ?

    – Tom Sawyer

    23 de abril de 2020 a las 19:52

avatar de usuario
JJJ

Veamos:

#include <stdio.h>
#include <string.h>

int main()
{
    char *p = "hello";
    char q[] = "hello"; // no need to count this

    printf("%zu\n", sizeof(p)); // => size of pointer to char -- 4 on x86, 8 on x86-64
    printf("%zu\n", sizeof(q)); // => size of char array in memory -- 6 on both

    // size_t strlen(const char *s) and we don't get any warnings here:
    printf("%zu\n", strlen(p)); // => 5
    printf("%zu\n", strlen(q)); // => 5

    return 0;
}

foo* y foo[] son tipos diferentes y el compilador los maneja de manera diferente (puntero = dirección + representación del tipo del puntero, matriz = puntero + longitud opcional de la matriz, si se conoce, por ejemplo, si la matriz está asignada estáticamente), los detalles pueden ser que se encuentra en la norma. Y a nivel de tiempo de ejecución no hay diferencia entre ellos (en ensamblador, bueno, casi, ver más abajo).

Además, hay una relación pregunta en el Preguntas frecuentes:

q: ¿Cuál es la diferencia entre estas inicializaciones?

char a[] = "string literal";   
char *p  = "string literal";   

Mi programa falla si trato de asignar un nuevo valor a p[i].

UN: Un literal de cadena (el término formal para una cadena entre comillas dobles en fuente C) se puede usar de dos maneras ligeramente diferentes:

  1. Como inicializador de una matriz de char, como en la declaración de char a[] especifica los valores iniciales de los caracteres en esa matriz (y, si es necesario, su tamaño).
  2. En cualquier otro lugar, se convierte en una matriz estática de caracteres sin nombre, y esta matriz sin nombre puede almacenarse en la memoria de solo lectura y, por lo tanto, no necesariamente se puede modificar. En un contexto de expresión, la matriz se convierte inmediatamente en un puntero, como de costumbre (consulte la sección 6), por lo que la segunda declaración inicializa p para señalar el primer elemento de la matriz sin nombre.

Algunos compiladores tienen un interruptor que controla si los literales de cadena se pueden escribir o no (para compilar código antiguo), y algunos pueden tener opciones para hacer que los literales de cadena se traten formalmente como matrices de const char (para una mejor detección de errores).

Ver también las preguntas 1.31, 6.1, 6.2, 6.8 y 11.8b.

Referencias: K&R2 Sec. 5,5 págs. 104

Sección ISO. 6.1.4, Sec. 6.5.7

Justificación Sec. 3.1.4

Seguridad y Salud Sec. 2.7.4 págs. 31-2

  • En sizeof(q), ¿por qué q no se convierte en un puntero, como menciona @Jon en su respuesta?

    – garyp

    21 de abril de 2016 a las 19:23

  • @garyp q no se descompone en un puntero porque sizeof es un operador, no una función (incluso si sizeof fuera una función, q decaería solo si la función esperara un puntero char).

    – GiriB

    14 de agosto de 2016 a las 17:07

  • gracias, pero printf(“%u\n” en lugar de printf(“%zu\n” , creo que deberías eliminar z.

    – Zakaria

    17 de febrero de 2018 a las 20:11

avatar de usuario
Ciro Santilli Путлер Капут 六四事

¿Cuál es la diferencia entre la matriz de caracteres y el puntero de caracteres en C?

Proyecto C99 N1256

Hay dos usos diferentes de los literales de cadenas de caracteres:

  1. Inicializar char[]:

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

    Así que esto es solo un atajo para:

    char c[] = {'a', 'b', 'c', '\0'};
    

    Como cualquier otra matriz regular, c se puede modificar

  2. 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 (comportamiento indefinido) si se modifica

    Entonces cuando escribes:

    char *c = "abc";
    

    Esto es similar a:

    /* __unnamed is magic because modifying it gives UB. */
    static char __unnamed[] = "abc";
    char *c = __unnamed;
    

    Tenga en cuenta el elenco implícito de char[] para char *que siempre es legal.

    Entonces si modificas c[0]también modificas __unnamedque 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:

EJEMPLO 8: La declaración

char s[] = "abc", t[3] = "abc";

define objetos de matriz de caracteres “simples” s y t cuyos elementos se inicializan con literales de cadenas de caracteres.

Esta declaración es idéntica a

char s[] = { 'a', 'b', 'c', '\0' },
t[] = { 'a', 'b', 'c' };

El contenido de las matrices es modificable. Por otra parte, la declaración

char *p = "abc";

define p 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 p para modificar el contenido de la matriz, el comportamiento no está definido.

Implementación de GCC 4.8 x86-64 ELF

Programa:

#include <stdio.h>

int main(void) {
    char *s = "abc";
    printf("%s\n", s);
    return 0;
}

Compilar y descompilar:

gcc -ggdb -std=c99 -c main.c
objdump -Sr main.o

La salida contiene:

 char *s = "abc";
8:  48 c7 45 f8 00 00 00    movq   $0x0,-0x8(%rbp)
f:  00 
        c: R_X86_64_32S .rodata

Conclusión: tiendas GCC char* en .rodata sección, no en .text.

Si hacemos lo mismo para char[]:

 char s[] = "abc";

obtenemos:

17:   c7 45 f0 61 62 63 00    movl   $0x636261,-0x10(%rbp)

por lo que se almacena en la pila (en relación con %rbp).

Sin embargo, tenga en cuenta que el script del enlazador predeterminado pone .rodata y .text en el mismo segmento, que tiene permiso de ejecución pero no de escritura. Esto se puede observar con:

readelf -l a.out

que contiene:

 Section to Segment mapping:
  Segment Sections...
   02     .text .rodata

  • @leszek.hanusz Comportamiento indefinido stackoverflow.com/questions/2766731/… Google “C language UB” 😉

    – Ciro Santilli Путлер Капут 六四事

    3 de mayo de 2016 a las 9:47

avatar de usuario
jonathan madera

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.

Lo siguiente reserva memoria para una cadena y también un puntero de carácter, y luego inicializa el puntero para apuntar al primer carácter de la cadena.

char *p = "hello";

Mientras que lo siguiente reserva memoria solo para la cadena. Entonces, en realidad puede usar menos memoria.

char p[10] = "hello";

Desde APUESección 5.14 :

char    good_template[] = "/tmp/dirXXXXXX"; /* right way */
char    *bad_template = "/tmp/dirXXXXXX";   /* wrong way*/

… Para la primera plantilla, el nombre se asigna en la pila, porque usamos una variable de matriz. Para el segundo nombre, sin embargo, usamos un puntero. En este caso, solo la memoria del propio puntero reside en la pila; el compilador organiza que la cadena se almacene en el segmento de solo lectura del ejecutable. Cuando el mkstemp función intenta modificar la cadena, se produce un error de segmentación.

El texto citado coincide con la explicación de @Ciro Santilli.

char p[3] = "hello" ? debiera ser char p[6] = "hello" recuerde 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 ajuste de objetos en la memoria. las únicas s diferentes están en la semántica. mientras que puede cambiar el valor de un puntero para que apunte 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 una matriz, el “nuevo” y “eliminar” se realizan automáticamente por usted.

avatar de usuario
justin

No está permitido cambiar el contenido de una constante de cadena, que es lo primero p puntos a. El segundo p es una matriz inicializada con una constante de cadena, y usted puede cambiar su contenido.

¿Ha sido útil esta solución?