problema de puntero c char

si declaramos char * p="hello"; luego, como está escrito en la sección de datos, no podemos modificar los contenidos a los que apunta, pero sí podemos modificar el puntero. pero encontré este ejemplo en C Trampas y escollos Andrew Koenig AT&T Bell Laboratories Murray Hill, New Jersey 07974

el ejemplo es

 char *p, *q; p = "xyz"; q = p; q[1] = 'Y'; 

q apuntaría a la memoria que contiene la cadena xYz. Entonces p, porque p y q apuntan a la misma memoria.

¿Cómo es cierto si la primera statement que mencioné también es cierta? De manera similar, ejecuté el siguiente código.

 main() { char *p="hai friends",*p1; p1=p; while(*p!='\0') ++*p++; printf("%s %s",p,p1); } 

y obtuve la salida como ibj!gsjfoet

Por favor explique cómo en estos dos casos podemos modificar los contenidos. gracias por adelantado

Su mismo ejemplo provoca un fallo de segmentación en mi sistema.

Te encuentras con un comportamiento indefinido aquí. .data (tenga en cuenta que la cadena literal también puede estar en .text ) no es necesariamente inmutable; no hay garantía de que la máquina proteja contra escritura esa memoria (a través de las tablas de páginas), dependiendo del sistema operativo y el comstackdor.

Solo su sistema operativo puede garantizar que las cosas en la sección de datos sean de solo lectura, e incluso eso implica establecer límites de segmentos y marcas de acceso y usar punteros lejanos y demás, por lo que no siempre se hace.

C en sí no tiene tal limitación; en un modelo de memoria plana (que casi todos los sistemas operativos de 32 bits usan en estos días), cualquier byte en su espacio de direcciones es potencialmente escribible, incluso cosas en su sección de código. Si tenía un puntero a main () y cierto conocimiento del lenguaje de máquina, y un sistema operativo que tenía las cosas bien configuradas (o mejor dicho, no pudo evitarlo), podría reescribirlo para devolver solo 0. Tenga en cuenta que esto es todo tipo de magia negra, y rara vez se hace intencionalmente, pero es parte de lo que hace que C sea un lenguaje tan poderoso para la progtwigción de sistemas.

Incluso si puedes hacer esto y parece que no hay errores, es una mala idea. Dependiendo del progtwig en cuestión, podría terminar haciéndolo muy fácil para los ataques de desbordamiento de búfer. Un buen artículo explicando esto es:

https://www.securecoding.cert.org/confluence/display/seccode/STR30-C.+Do+not+attempt+to+modify+string+literals

Dependerá del comstackdor para saber si funciona o no.

x86 es una architecture de von Neumann (a diferencia de Harvard ), por lo que no hay una diferencia clara entre la memoria de ‘datos’ y ‘progtwig’ en el nivel básico (es decir, el comstackdor no tiene la obligación de tener diferentes tipos de memoria de progtwig contra datos, y así no necesariamente restringiremos ninguna variable a una u otra).

Así que un comstackdor puede permitir la modificación de la cadena mientras que otro no lo permite.

Mi conjetura es que un comstackdor más indulgente (por ejemplo, cl, el comstackdor MS Visual Studio C ++) lo permitiría, mientras que un comstackdor más estricto (por ejemplo, gcc) no lo permitiría. Si su comstackdor lo permite, es probable que esté cambiando efectivamente su código a algo como:

 ... char p[] = "hai friends"; char *p1 = p; ... // (some disassembly required to really see what it's done though) 

tal vez con la ‘buena intención’ de permitir que los nuevos codificadores C / C ++ codifiquen con menos restricciones / menos errores confusos. (Si esto es una ‘Buena cosa’ depende de mucho debate y mantendré mis opiniones principalmente fuera de este post: P)

Fuera de interés, ¿qué comstackdor usaste?

En la antigüedad, cuando C como lo describe K&R en su libro “El lenguaje de progtwigción C” era el “estándar” ejem, lo que usted describe estaba perfectamente bien. De hecho, algunos comstackdores saltaron a través de aros para hacer que los literales de cadena puedan escribirse. Copiaron laboriosamente las cadenas del segmento de texto al segmento de datos en la inicialización.

Incluso ahora, gcc tiene un indicador para restaurar este comportamiento: -fwritable-strings .

 main() { int i = 0; char *p= "hai friends", *p1; p1 = p; while(*(p + i) != '\0') { *(p + i); i++; } printf("%s %s", p, p1); return 0; } 

Este código dará salida: amigos hai amigos hai

Modificar los literales de cadena es una mala idea, pero eso no significa que no funcione.

Una buena razón para no hacerlo: a su comstackdor se le permite tomar múltiples instancias de la misma cadena literal y hacer que apunten al mismo bloque de memoria. Por lo tanto, si se definió “xyz” en algún otro lugar de su código, podría romper accidentalmente otro código que esperaba que fuera constante.

Su progtwig también funciona en mi sistema (windows + cygwin). Sin embargo, la norma dice que no debe hacer eso, aunque la consecuencia no está definida.

El siguiente extracto del libro C: A Reference Manual 5 / E, página 33,

Nunca debe intentar modificar la memoria que contiene los caracteres de una cadena constante, ya que puede ser de solo lectura.

 char p1[] = "Always writable"; char *p2 = "Possibly not writable"; const char p3[] = "Never writable"; 

La línea p1 siempre funcionará; la línea p2 puede funcionar o puede causar un error en tiempo de ejecución ; p3 siempre causará un error en tiempo de comstackción.

Si bien es posible modificar una cadena literal en su sistema, eso es una peculiaridad de su plataforma, en lugar de una garantía del idioma. El lenguaje C real no sabe nada sobre las secciones .data o las secciones .text. Eso es todo detalle de implementación.

En algunos sistemas integrados, ni siquiera tendrá un sistema de archivos para contener un archivo con una sección .text. En algunos de estos sistemas, sus cadenas literales se almacenarán en la ROM, y tratar de escribir en la ROM solo bloqueará el dispositivo.

Si escribe un código que depende de un comportamiento indefinido y solo funciona en su plataforma, se le puede garantizar que, tarde o temprano, alguien pensará que es una buena idea trasladarlo a algún dispositivo nuevo que no funcione de la manera que esperaba. . Cuando eso suceda, un grupo de desarrolladores incrustados te enojará y te apuñalará.

p está apuntando efectivamente a la memoria de sólo lectura. El resultado de asignar a la matriz p puntos es probablemente un comportamiento indefinido. El hecho de que el comstackdor le permita salirse con la suya no significa que esté bien.

Eche un vistazo a esta pregunta de la C-FAQ: comp.lang.c lista de preguntas frecuentes · Pregunta 1.32

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

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

Mi progtwig se bloquea si bash asignar un nuevo valor a p [i].

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

  1. Como inicializador para una matriz de caracteres, como en la statement de caracteres 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 de caracteres sin nombre, estática, y esta matriz sin nombre se puede almacenar en la memoria de solo lectura, y 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 (consulte la sección 6), por lo que la segunda statement inicializa p para que apunte al primer elemento de la matriz sin nombre.

Algunos comstackdores tienen un interruptor que controla si los literales de cadena se pueden escribir o no (para comstackr el código antiguo), y algunos pueden tener opciones para hacer que los literales de cadena se traten formalmente como matrices de caracteres const (para una mejor captura de errores).

Creo que estás creando una gran confusión sobre un concepto general muy importante que debes entender al usar C, C ++ u otros lenguajes de bajo nivel. En un lenguaje de bajo nivel hay una suposición implícita de que el progtwigdor sabe lo que está haciendo y no comete ningún error de progtwigción .

Esta suposición permite a los implementadores del lenguaje simplemente ignorar lo que debería suceder si el progtwigdor está violando las reglas. El efecto final es que en C o C ++ no hay garantía de “error de tiempo de ejecución” … si hace algo malo simplemente NO ESTÁ DEFINIDO (“comportamiento indefinido” es el término legal) lo que va a suceder. Puede ser un choque (si tiene mucha suerte), o puede que aparentemente no sea nada (desafortunadamente, la mayoría de las veces … puede ser un choque en un lugar perfectamente válido un millón de instrucciones ejecutadas más adelante).

Por ejemplo, si accede fuera de una matriz PUEDE SER RECIBIDO, se producirá un locking, puede que no, o incluso un demonio saldrá de su nariz (este es el “demonio nasal” que puede encontrar en Internet). Simplemente no es algo que a quien escribió el comstackdor le importara pensar.

Solo nunca hagas eso (si te importa escribir progtwigs decentes).

Una carga adicional sobre quién usa los idiomas de bajo nivel es que debe aprender todas las reglas muy bien y nunca debe violarlas. Si violas una regla, no puedes esperar que un “ángel de error de tiempo de ejecución” te ayude … solo los “demonios de comportamiento indefinido” están presentes ahí abajo.