punteros pointer cadenas array c arrays pointers

pointer - ¿Cuál es la diferencia entre char array vs char puntero en C?



punteros en c (7)

Intento entender los indicadores en C pero actualmente estoy confundido con lo siguiente:

  • char *p = "hello"

    Este es un puntero de char apuntando a la matriz de caracteres, comenzando en h .

  • char p[] = "hello"

    Esta es una matriz que almacena hello .

¿Cuál es la diferencia cuando paso ambas variables a esta función?

void printSomething(char *p) { printf("p: %s",p); }


No puedes cambiar el contenido de una constante de cadena, que es a lo que apunta la primera p . La segunda p es una matriz inicializada con una constante de cadena, y puede cambiar su contenido.


Para casos como este, el efecto es el mismo: termina pasando la dirección del primer carácter en una cadena de caracteres.

Las declaraciones obviamente no son lo mismo.

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

char *p = "hello";

Mientras que lo siguiente deja de lado la memoria solo para la cadena. Entonces realmente puede usar menos memoria.

char p[10] = "hello";


Por lo que puedo recordar, una matriz es en realidad un grupo de punteros. Por ejemplo

p[1]== *(&p+1)

es una declaración verdadera


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 forma 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á asignada estáticamente ), los detalles se pueden encontrar en el estándar. Y en el nivel de tiempo de ejecución no hay diferencia entre ellos (en ensamblador, bueno, casi, ver abajo).

Además, hay una question relacionada en las preguntas frecuentes de C :

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

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

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

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

  1. Como iniciador 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 no nombrada de caracteres, y esta matriz sin nombre se puede almacenar en la memoria de solo lectura, y que, por lo tanto, no se puede modificar necesariamente. En un contexto de expresión, la matriz se convierte a la vez en un puntero, como es habitual (ver sección 6), por lo que la segunda declaración inicializa p para apuntar al primer elemento de la matriz sin nombre.

Algunos compiladores tienen un interruptor que controla si los literales de cadena son editables 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 captura de errores).

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

Referencias: K & R2 Sec. 5.5 p. 104

ISO Sec. 6.1.4, Sec. 6.5.7

Justificación Sec. 3.1.4

H & S Sec. 2.7.4 pp. 31-2


char p[3] = "hello" ? debe 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 objeto de ajuste en la memoria. los únicos diferentes son en semántica. mientras que puede cambiar el valor de un puntero para apuntar a una ubicación diferente en la memoria, una matriz, después de creada, siempre apuntará a la misma ubicación.
también cuando se usa una matriz, "nuevo" y "eliminar" se hacen automáticamente por usted.


char* y char[] son tipos diferentes , pero no es inmediatamente evidente en todos los casos. Esto se debe a que las matrices se descomponen en punteros , lo que significa que si se proporciona una expresión de tipo char[] donde se espera una de tipo char* , 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:

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

El compilador pretende que usted escribió esto:

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


Borrador C99 N1256

Hay dos usos completamente diferentes de los literales de matriz:

  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 caracteres puede inicializarse mediante un literal de cadena de caracteres, opcionalmente encerrado entre llaves. Los caracteres sucesivos del literal de cadena de caracteres (incluido el carácter nulo de terminación si hay espacio o si el conjunto es de tamaño desconocido) inicializan los elementos del conjunto.

    Entonces este es solo un atajo para:

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

    Al igual que cualquier otra matriz regular, c puede modificarse.

  2. En todos lados: genera un:

    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 reparto implícito de char[] a char * , que siempre es legal.

    Luego, si modifica c[0] , también modifica __unnamed , 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 literal. La secuencia de caracteres multibyte se usa luego para inicializar una matriz de duración de almacenamiento estático y longitud suficiente para contener la secuencia. Para los literales de cadena de caracteres, los elementos de la matriz tienen 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 dicha matriz, 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 char "simples" s y t cuyos elementos se inicializan con literales de cadena 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 otro lado, la declaración

char *p = "abc";

define p con el tipo "puntero a char" y lo inicializa para apuntar a un objeto con el tipo "array of char" con longitud 4 cuyos elementos se inicializan con un literal de cadena de caracteres. Si se intenta utilizar p para modificar el contenido de la matriz, el comportamiento no está definido.

Implementación GCC 4.8 x86-64 ELF

Programa:

#include <stdio.h> int main() { 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: GCC almacena char* it en la sección de .rodata , 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 tanto, se almacena en la pila (en relación con %rbp ).

Sin embargo, .text en cuenta que el script del enlazador predeterminado coloca .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