C: diferencias entre el puntero char y la matriz [duplicate]

8 minutos de lectura

avatar de usuario
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?

  • 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

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:12 pmessageserá la dirección de &amessage , en algún lugar de la pila. Igualmente, amessageserá la dirección de la matriz en la pila, igual que &amessage . Sin embargo, amessagetiene 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 de char** es &amessage – puntero a puntero a char, y el tipo de char(*)[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

14 de diciembre de 2014 a las 14:24
avatar de usuario

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

diferencias entre char pointer y array

Proyecto C99 N1256

  1. Hay dos usos diferentes de los literales de cadenas de caracteres: char[]Inicializar

    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.

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

    Así que esto es solo un atajo para: c Como cualquier otra matriz regular,

  2. 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 de char *para

    que siempre es legal. c[0]Entonces si modificas __unnamedtambién modificas

    que 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 y

cuyos 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 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

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 .textsecció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: %rbppor 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

que contiene:
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

Este código probablemente fallaría en su caja.  Pero puede hacer lo que quiera, porque su comportamiento no está definido.
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. pmessagePS 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

¿Ha sido útil esta solución?