2009-07-19 20 views
17

En C, las cadenas terminan con nulo (\ 0) que causa problemas cuando quiere poner un nulo en una cadena. ¿Por qué no tener un personaje especial escapado como \ $ o algo así?¿Por qué C no termina cadenas con un carácter especial de terminación de cadena escapada?

Soy plenamente consciente de lo tonta que es esta pregunta, pero tenía curiosidad.

+5

¿Qué sucede cuando quiere poner \ $ en una cadena? –

+1

¡Entonces escapas del personaje de escape, por supuesto! –

+4

@Bryan: No se puede escapar de un personaje, solo se puede escapar de la representación del código fuente de un personaje. Cualquiera que sea el personaje que use como terminación no puede usarse dentro de una cadena. – Guffa

Respuesta

39

Terminar con un 0 tiene muchas características de rendimiento, que fueron muy relevantes a finales de los años 60.

CPUs tienen instrucciones de salto condicional en la prueba de 0. De hecho, algunos incluso tienen CPUs instrucciones que iterar/copiar una secuencia de bytes hasta el 0.

Si utilizó un carácter de escape en su lugar, tener dos pruebas DOS bytes diferentes para afirmar el final de la cadena. No solo eso es más lento, sino que también se pierde la capacidad de iterar un byte a la vez, ya que se necesita una vista previa o la posibilidad de dar marcha atrás.

Ahora, otros idiomas (tos, Pascal, tos) usan cadenas en un estilo de conteo/valor. Para ellos, cualquier personaje es válido, pero siempre mantienen un contador con el tamaño de la cadena. La ventaja es clara, pero también hay desventajas para esta técnica.

Por un lado, el tamaño de la cadena está limitado por el número de bytes que toma el conteo. Un byte le da 255 caracteres, dos bytes le da 65535, etc. Hoy podría ser casi irrelevante, pero agregar dos bytes a cada cadena una vez fue bastante costoso.

Editar:

No creo que la pregunta es tonta.En estos días de idiomas de alto nivel con administración de memoria, increíble potencia de CPU y cantidades obscenas de memoria, tales decisiones del pasado bien pueden parecer absurdas. Y, de hecho, PODRÍAN carecer de sentido hoy en día, por lo que es bueno cuestionarlos.

+5

+1 por mencionar la CPU. Sus "algunas CPU" incluyen el conjunto de instrucciones x86 de Intel (aunque tal vez esas instrucciones ya no se usen mucho). – ChrisW

+2

Si define su propia estructura de cadena, puede crear el valor 255 del byte de tamaño, indicar que sigue otro byte de tamaño. –

+2

Las características de rendimiento siguen siendo relevantes hoy en muchas situaciones. Es importante en sistemas embebidos, y en el desarrollo kernel/driver donde aún desea raspar y guardar cada ciclo de CPU que pueda. Por eso, C sigue siendo rey en estas áreas. – Gerald

13

Necesita tener algunos valor de bytes real para terminar una cadena - la forma en que lo representa en el código no es realmente relevante.

Si usó \$ para terminar las cadenas, ¿qué valor de byte tendría en la memoria? ¿Cómo incluirías ese valor de byte en una cadena?

Va a resolver este problema haga lo que haga, si utiliza un carácter especial para terminar las cadenas. La alternativa es usar cadenas contadas, por lo que la representación de una cadena incluye su longitud (por ejemplo, BSTR).

+0

De acuerdo, así \ $ señalaría algún valor que no se utiliza actualmente. – akway

+4

Pero no hay valores de bytes "sin usar". Cualquier byte puede ocurrir en una cadena C; también podría decirse que se eligió \ 0 porque no se utilizó. – RichieHindle

+0

¿Cómo qué? Si está utilizando UTF-8, entonces se usa todo el rango. –

2

Supongo que porque es más rápido de verificar, y es totalmente improbable que ocurra en una secuencia razonable. Además, recuerde que C no tiene ningún concepto de cadenas. Una cadena en C no es algo en sí misma. Es solo una variedad de personajes. El hecho de que se llame y se use como cuerda es puramente incidental y convencional.

1

que causa problemas, pero se puede incrustar un \ 0 ...

const char* hello = "Hello\0World\0\0"; 

Esto causa un problema si pasa esto a una biblioteca de funciones estándar como strlen, pero no de otra manera.

Una solución mejor que cualquier cadena de caracteres de terminación podría ser anteponer la longitud de la cadena como ...

const char* hello = "\x0BHello World"; 

... que es la forma en que algunos otros idiomas hacen.

+1

Buenos ejemplos, pero es posible que desee que la longitud de cadena prefijada en su ejemplo refleje realmente la longitud de la cadena. (Creo que se olvidó de contar el espacio) – jerryjvl

+0

Gracias por notar eso. Le conté a C, volví a contar y decidí que C era uno de más, y luego escribí erróneamente A como si C menos 1 fuera A. Lo he corregido ahora. – ChrisW

+1

Me recuerda a los viejos tiempos con constantes de Hollerith en FORTRAN, por lo que tendrías una cuerda como 16HTHIS IS A STRING. ¡Ay de ti si cuentas mal! Las nuevas cadenas citadas que aparecieron más tarde fueron mucho más agradables. –

-1

No hay ninguna razón para que un carácter nul sea parte de una cadena excepto como terminador; no tiene representación gráfica, por lo que no lo vería ni actúa como un personaje de control. En lo que respecta al texto, se trata de un valor fuera de banda que puede obtenerse sin utilizar una representación diferente (por ejemplo, un valor multibyte como 0xFFFF).

Para volver a formular la pregunta de Michael, ¿cómo esperaría que se manejara "Hello \ 0World \ 0"?

+0

¿Cómo se representa en la memoria una bolsa de datos binarios, que podría contener un NUL? La respuesta C, básicamente, es "usar rutinas mem *". Y si necesita almacenar la longitud, continúa con "luego invente su propia forma de almacenar longitudes, si es necesario, y escriba wrappers para las funciones * de memoria que necesita". – Blaisorblade

+0

Hay muchas razones por las que puede tener un byte cero en una matriz de bytes, o bien, dado que C usa 'char' en lugar de 'byte', una matriz de caracteres. Solo recuerda no tratar esto como una cuerda y estarás bien. Una "cadena de C" es una matriz de caracteres terminada en nulo, aunque en realidad no es su propio tipo de datos. Esa puede ser la fuente de confusión. –

0

Si las funciones de biblioteca estándar como strlen o printf podrían (en su caso) buscar un marcador de fin de cadena \ 777 (como alternativa a \ 000), podría tener una cadena de caracteres constante que contenga \ 0s:

const char* hello = "Hello\0World\0\0\777"; 
printf("%s\n", hello); 

Por cierto, si usted desea enviar un \ 0 a la salida estándar (también conocido como -print0) se pueden utilizar:

putchar(0); 
0

lo mismo ocurre en las razones históricas.

Los creadores de std :: string en C++ reconocieron este defecto, por lo que std :: string puede incluir el carácter nulo. (Pero tenga cuidado constructing a std::string with a null character!)

Si desea tener un C-string (o más bien, un cuasi-C-string) con un carácter nulo, tendrá que hacer para hacer su propia estructura.

typedef struct { 
    size_t length; 
    char[] data; //C99 introduced the flexible array member 
} my_string; 

O tendrá que hacer un seguimiento de la longitud de la cuerda de alguna otra manera y pasarla a cada función de secuencia que escriba.

0

No se puede necro-publicar deliberadamente, pero esto sigue siendo muy relevante para SQL incorporado.

Si está tratando con datos binarios en C, debería crear un objeto binario en una estructura de datos. Si puede pagarlo, bastará con una batería de carbón. Probablemente no sea una cadena de todos modos, ¿verdad?

Para los valores hash/digest, es común "HEX" en miembros de {'0', .., 'F'}. Estos pueden ser "ANULADOS" durante la operación de la base de datos.

Para las operaciones de archivos, considere una secuencia binaria, con una longitud de registro lógica.

Escaparlos usted solo es realmente seguro si puede garantizar la codificación. De hecho, esto se puede ver en una descarga MYSQLDUMP (SQL) donde los binarios se escapan correctamente para decir UTF-8, y el esquema de instalación es 'empujado' para la carga y 'reventado' después.

No defiendo el uso de una llamada de dbms para lo que debería ser una función de biblioteca tampoco, pero lo he visto hecho. (seleccione de real_escape_string ($ string)).

Y hay base64, que es otra lata de gusanos. Google UUENCODE.

Así que sí, mem * funciones si sus caracteres son de ancho fijo.

Cuestiones relacionadas