2009-09-21 16 views
7

Tenga en cuenta el siguiente código.No se puede modificar la cadena C

 
int main(void) { 
    char * test = "abcdefghijklmnopqrstuvwxyz"; 
    test[5] = 'x'; 
    printf("%s\n", test); 
    return EXIT_SUCCESS; 
} 

En mi opinión, esto debería imprimir abcdexghij. Sin embargo, simplemente termina sin imprimir nada.

 
int main(void) { 
    char * test = "abcdefghijklmnopqrstuvwxyz"; 
    printf("%s\n", test); 
    return EXIT_SUCCESS; 
} 

Sin embargo, esto funciona muy bien, así que ¿no entendí mal el concepto de manipular cadenas de C o algo así? En caso de que sea importante, ejecuto Mac OS X 10.6 y estoy compilando un binario de 32 bits.

+1

Odio decirlo, pero esto realmente debería estar en una pregunta frecuente de C en alguna parte ... ya se ha pedido decenas o cientos de otras veces en Stack Overflow. – ephemient

+0

Lamento que se haya preguntado antes, pero no he podido encontrar una respuesta. Realmente leí la referencia de función y todo primero, pero realmente no vi lo que estaba haciendo mal. ¿Podrías dirigirme a tales preguntas frecuentes sobre C? – fresskoma

+3

@ x3ro: ¿Nadie te respondió acerca de una pregunta frecuente de C en 4 años? Las [preguntas frecuentes comp.lang.c] (http://www.c-faq.com/) son excelentes. La Sección 8 cubre los caracteres y las cadenas, y cuestiona 8.5 puntos a la pregunta 1.32, que aborda su pregunta específica. –

Respuesta

4

El accepted answer es bueno, pero no del todo completo.

char * test = "abcdefghijklmnopqrstuvwxyz"; 

A cadena literal se refiere a una matriz de objetos anónima de tipo char[N] con la duración de almacenamiento estático (lo que significa que existe para toda la ejecución del programa), donde N es la longitud de la cadena más uno para el terminando '\0'. Este objeto no es const, pero cualquier intento de modificarlo tiene un comportamiento indefinido. (Una implementación puede hacer literales de cadena se puede escribir si lo desea, pero la mayoría de los compiladores modernos no lo hacen.)

La declaración anterior crea un objeto tal anónima de tipo char[27], y utiliza la dirección del primer elemento de ese objeto para inicializar test . Por lo tanto, una asignación como test[5] = 'x' intenta modificar la matriz y tiene un comportamiento indefinido; por lo general, bloqueará su programa. (La inicialización utiliza la dirección porque el literal es una expresión del tipo de matriz, que se convierte implícitamente en la mayoría de los contextos en un puntero al primer elemento de la matriz.)

Tenga en cuenta que en C++, los literales de cadena son realmente const, y la declaración anterior sería ilegal. En C o C++, lo mejor es declarar test como un puntero a const char:

const char *test = "abcdefghijklmnopqrstuvwxyz"; 

lo que el compilador le avisará si se intenta modificar la matriz a través de test.

(literales de cadena C son no const por razones históricas. Antes de la norma 1989 ANSI C, no existía la palabra clave const. Exigir que sea utilizado en las declaraciones como la suya habría hecho para el código más seguro, pero habría requerido código existente que se debe modificar, algo que el comité ANSI intentó evitar. Usted debe pretender que los literales de cadena son const, aunque no lo son. Si usted está usando gcc, la opción -Wwrite-strings hará que el compilador trate cadena literales como const - que hace que gcc no se conforme.)

Si desea poder modificar la cadena que test se refiere a, se puede definir así:

char test[] = "abcdefghijklmnopqrstuvwxyz"; 

El compilador busca en el inicializador para determinar qué tan grande es necesario que haya test. En este caso, test será del tipo char[27]. El literal de cadena todavía se refiere a un objeto de matriz anónimo mayormente de solo lectura, pero su valor es copiado en test. (Un literal de cadena en un inicializador utilizado para inicializar un objeto de matriz es uno de los contextos en los que una matriz no "decae" en un puntero; los otros son cuando es el operando deo sizeof). Ya que no hay más referencias a la matriz anónima, el compilador puede optimizarla.

En este caso, test es una matriz que contiene los 26 caracteres que ha especificado, más el terminador '\0'. La vida útil de esa matriz depende de dónde se haya declarado test, lo que puede o no ser importante. Por ejemplo, si usted hace esto:

char *func(void) { 
    char test[] = "abcdefghijklmnopqrstuvwxyz"; 
    return test; /* BAD IDEA */ 
} 

la persona que llama recibirá un puntero a algo que ya no existe. Si necesita hacer referencia a la matriz fuera del ámbito en el que se define test, puede definirlo como static, o puede asignarlo mediante malloc:

char *test = malloc(27); 
if (test == NULL) { 
    /* error handling */ 
} 
strcpy(test, "abcdefghijklmnopqrstuvwxyz"; 

por lo que la serie seguirá existiendo hasta que llame free() . La función no estándar strdup() hace esto (está definido por POSIX pero no por ISO C).

Tenga en cuenta que test puede ser un puntero o una matriz según cómo lo declare. Si pasa test a una función de cadena, oa cualquier función que tome char*, eso no importa, pero algo como sizeof test se comportará de manera muy diferente dependiendo de si test es un puntero o una matriz.

El comp.lang.c FAQ es excelente. La Sección 8 cubre los caracteres y las cadenas, y cuestiona 8.5 puntos a la pregunta 1.32, que aborda su pregunta específica. La Sección 6 cubre la relación a menudo confusa entre arrays y punteros.

27

Los punteros Char definidos con un valor de inicialización entran en un segmento de solo lectura. Para que sean modificables, debe crearlos en el montón (por ejemplo, con new/malloc) o definirlos como una matriz.

no modificables:

char * foo = "abc"; 

modificable:

char foo[] = "abc"; 
+0

Whoops: gracias por la edición. – Joe

+1

foo [0] = 'x' aún segfaults en mi cuadro – pm100

4

Usted debe entrar en el hábito de hacer coincidir el tipo de la variable con el tipo de la initialiser. En este caso:

const char* test = "abcdefghijklmnopqrstuvwxyz"; 

De esta forma obtendrá un error de compilación en lugar de un error de tiempo de ejecución. Poner en marcha el nivel de advertencia del compilador hasta el máximo también puede ayudar a evitar tales riesgos. Por qué esto no es un error en C es probablemente histórico; Los primeros compiladores lo permitieron y rechazarlo podría haber roto demasiado código existente cuando el lenguaje estaba estandarizado. Ahora, sin embargo, los sistemas operativos no lo permiten, por lo que es académico.

3

Los literales de cadena pueden no ser modificables; lo mejor es suponer que no lo son. Vea here para más detalles.

1

hacer:

char * bar = strdup(foo); 
bar[5] = 'x'; 

strdup hace una copia modificable.

Y sí, realmente debería probar que strdup no devolvió NULL.

+0

... y libre (barra) al final si usa strdup()! –

Cuestiones relacionadas