En este momento estoy trabajando con sistemas integrados y descubriendo formas de implementar cadenas en un microprocesador sin sistema operativo. Hasta ahora, lo que estoy haciendo es utilizar la idea de tener punteros de caracteres terminados en NULL y tratarlos como cadenas donde NULL significa el final. Sé que esto es bastante común, pero puede que siempre cuento con que este es el caso?
La razón por la que pregunto es que estaba pensando en usar un sistema operativo en tiempo real en algún momento, y me gustaría reutilizar tanto como sea posible mi código actual. Entonces, para las diversas opciones que existen, ¿puedo esperar que las cadenas funcionen igual?
Permítanme ser más específico para mi caso. Estoy implementando un sistema que toma y procesa comandos a través de un puerto serie. ¿Puedo mantener mi código de procesamiento de comando igual y luego esperar que los objetos de cadena creados en el RTOS (que contiene los comandos) se terminen NULL? ¿O sería diferente según el sistema operativo?
Actualizar
Después de que me aconsejaron echar un vistazo a esta pregunta , he determinado que no responde exactamente lo que estoy preguntando. La pregunta en sí es preguntar si siempre se debe pasar la longitud de una cadena, que es completamente diferente de lo que estoy preguntando, y aunque algunas de las respuestas tenían información útil, no son exactamente lo que estoy buscando. Las respuestas allí parecían dar razones de por qué o por qué no terminar una cadena con un carácter nulo. La diferencia con lo que estoy preguntando es si puedo esperar más o menos que las cadenas innatas de diferentes plataformas terminen sus propias cadenas con nulo, sin tener que salir y probar cada plataforma por ahí si eso tiene sentido.
fuente
Respuestas:
Las cosas que se denominan "cadenas C" se anularán en cualquier plataforma. Así es como las funciones estándar de la biblioteca C determinan el final de una cadena.
Dentro del lenguaje C, no hay nada que le impida tener una serie de caracteres que no termina en un valor nulo. Sin embargo, tendrá que usar algún otro método para evitar correr al final de una cadena.
fuente
char
matrices terminadas en nulo ,char
matrices con la longitud codificada en el primer byte (comúnmente conocido como "cadenas Pascal"),wchar_t
versiones basadas en ambos arriba, ychar
matrices que combinan ambos métodos: longitud codificada en el primer byte y un carácter nulo que termina la cadena.La determinación del carácter final depende del compilador para literales y la implementación de la biblioteca estándar para cadenas en general. No está determinado por el sistema operativo.
La convención de
NUL
terminación se remonta al C estándar anterior, y en más de 30 años, no puedo decir que me he encontrado con un entorno que hace cualquier otra cosa. Este comportamiento fue codificado en C89 y continúa siendo parte del estándar del lenguaje C (el enlace es a un borrador de C99):NUL
cadenas terminadas al requerir queNUL
se agregue a los literales de cadena.No hay ninguna razón por la que alguien no pueda escribir funciones que manejen cadenas terminadas por algún otro carácter, pero tampoco hay razón para romper el estándar establecido en la mayoría de los casos a menos que su objetivo sea ajustar los programadores. :-)
fuente
printf("string: \"%s\"\n", "my cool string")
. La única forma de pasar cuatro parámetros en este caso (aparte de algún tipo de byte de terminación) sería definir una cadena para que sea algo así comostd::string
en C ++, que tiene sus propios problemas y limitaciones.NUL
los determinan sin importar qué: "En la fase de traducción 7, un byte o código de valor cero se agrega a cada secuencia de caracteres multibyte que resulta de una cadena literal o literales ". Las funciones de la biblioteca que usan la definición de 7.1.1 se detienen en el primer momentoNUL
que encuentran y no sabrán ni les importará que existan caracteres adicionales más allá.No hay ningún tipo de datos de cadena en el lenguaje C, pero hay literales de cadena .
Si coloca un literal de cadena en su programa, generalmente terminará en NUL (pero vea el caso especial, discutido en los comentarios a continuación). Es decir, si coloca
"foobar"
en un lugar dondeconst char *
se espera un valor, el compilador emitiráfoobar⊘
a la sección / segmento de código / constante de su programa, y el valor de la expresión será un puntero a la dirección donde almacenó elf
carácter. (Nota: estoy usando⊘
para significar el byte NUL).El único otro sentido en el que el lenguaje C tiene cadenas es que tiene algunas rutinas de biblioteca estándar que operan en secuencias de caracteres terminadas en NUL. Esas rutinas de biblioteca no existirán en un entorno de metal desnudo a menos que las porte usted mismo.
Son solo código --- no es diferente del código que usted mismo escribe. Si no los rompe cuando los transfiere, harán lo que siempre hacen (por ejemplo, detenerse en un NUL).
fuente
char foo[4] = "abcd";
es una forma válida de crear una matriz no terminada en nulo de cuatro caracteres.char const *
expresión . Olvidé que los inicializadores C a veces pueden obedecer diferentes reglas.char[4]
. Eso no es una secuencia, pero se inicializó a partir de unastatic
al ejemplo de Ruakh, entonces el compilador puede emitir un "abcd" no terminado en NUL a un segmento de datos inicializado para que el cargador del programa inicialice la variable. Entonces, Ruakh tenía razón: hay al menos un caso en el que la aparición de un literal de cadena en un programa no requiere que el compilador emita una cadena terminada en NUL. (ps, en realidad compilé el ejemplo con gcc 5.4.0, y el compilador no emitió el NUL.)Como otros han mencionado, la terminación nula de cadenas es una convención de la Biblioteca estándar de C. Puede manejar cadenas de la forma que desee si no va a utilizar la biblioteca estándar.
Esto es cierto para cualquier sistema operativo con un compilador 'C' y, además, puede escribir programas 'C' que no se ejecuten bajo un verdadero sistema operativo como menciona en su pregunta. Un ejemplo sería el controlador de una impresora de inyección de tinta que diseñé una vez. En sistemas embebidos, la sobrecarga de memoria de un sistema operativo puede no ser necesaria.
En situaciones de poca memoria, miraría las características de mi compilador frente al conjunto de instrucciones del procesador, por ejemplo. En una aplicación donde las cadenas se procesan mucho, puede ser conveniente utilizar descriptores como la longitud de la cadena. Estoy pensando en un caso en el que la CPU es particularmente eficiente al trabajar con desplazamientos cortos y / o desplazamientos relativos con registros de direcciones.
Entonces, ¿qué es más importante en su aplicación: el tamaño y la eficiencia del código, o la compatibilidad con un sistema operativo o biblioteca? Otra consideración podría ser la mantenibilidad. Cuanto más se aleje de la convención, más difícil será para otra persona mantenerla.
fuente
Otros han abordado el problema de que en C, las cadenas son en gran medida lo que usted hace de ellas. Pero parece haber cierta confusión en su pregunta sobre el terminador mismo, y desde una perspectiva, esto podría ser lo que preocupa a alguien en su posición.
Las cadenas C tienen terminación nula. Es decir, que se terminan con el carácter nulo,
NUL
. No están terminados por el puntero nuloNULL
, que es un tipo de valor completamente diferente con un propósito completamente diferente.NUL
se garantiza que tiene el valor entero cero. Dentro de la cadena, también tendrá el tamaño del tipo de carácter subyacente, que generalmente será 1.NULL
no se garantiza que tenga un tipo entero en absoluto.NULL
está diseñado para su uso en un contexto de puntero, y generalmente se espera que tenga un tipo de puntero, que no debería convertirse en un carácter o un entero si su compilador es bueno. Si bien la definición deNULL
involucra el glifo0
, no se garantiza que tenga ese valor [1], y a menos que su compilador implemente la constante como un carácter#define
(muchos no lo hacen, porqueNULL
realmente no debería ser significativo en un no contexto del puntero), por lo tanto, no se garantiza que el código expandido realmente implique un valor cero (aunque confusamente implique un glifo cero).Si
NULL
se escribe, también será poco probable que tenga un tamaño de 1 (u otro tamaño de caracteres). Esto puede causar problemas adicionales, aunque las constantes de caracteres reales tampoco tienen el tamaño de caracteres en su mayor parte.Ahora, la mayoría de la gente verá esto y pensará: "puntero nulo como algo más que todos los bits cero? ¿Qué tontería?", Pero suposiciones como esa solo son seguras en plataformas comunes como x86. Como ha mencionado explícitamente su interés en apuntar a otras plataformas, debe tener en cuenta este problema, ya que ha separado explícitamente su código de los supuestos sobre la naturaleza de la relación entre punteros y enteros.
Por lo tanto, aunque las cadenas C están terminadas en nulo, no están terminadas por
NULL
, sino porNUL
(generalmente escritas'\0'
). El código que se usa explícitamenteNULL
como un terminador de cadena funcionará en plataformas con una estructura de dirección sencilla e incluso se compilará con muchos compiladores, pero no es absolutamente correcto C.[1] el compilador inserta el valor de puntero nulo real cuando lee un
0
token en un contexto donde se convertiría a un tipo de puntero. Esto no es una conversión del valor entero 0, y no se garantiza que se mantenga si0
se usa algo más que el token en sí, como un valor dinámico de una variable; la conversión tampoco es reversible, y un puntero nulo no tiene que producir el valor 0 cuando se convierte en un entero.fuente
NUL
se garantiza que tiene el valor entero cero". -> C no defineNUL
. En cambio, C define que las cadenas tienen un carácter nulo final , un byte con todos los bits establecidos en 0.He estado usando cadenas en C, significa que los caracteres con terminación nula se llaman cadenas.
No tendrá ningún problema cuando lo use en baremetal o en cualquier sistema operativo como Windows, Linux, RTOS: (FreeRTO, OSE).
En el mundo incrustado, la terminación nula en realidad ayuda a simular más el carácter como una cadena.
He estado usando cadenas en C así en muchos sistemas críticos de seguridad.
Tal vez se pregunte, ¿qué es la cadena realmente en C?
Cadenas de estilo C, que son matrices, también hay literales de cadena, como "this". En realidad, estos dos tipos de cadenas no son más que colecciones de caracteres sentados uno al lado del otro en la memoria.
Por ejemplo, puede declarar y definir una matriz de caracteres e inicializarlo con una constante de cadena:
Respuesta directa: realmente no necesita preocuparse por el uso de caracteres con terminación nula, esto funciona independientemente de cualquier plataforma.
fuente
NUL
se agrega automáticamente.Como otros han dicho, la terminación nula es bastante universal para el estándar C. Pero (como otros también han señalado) no es 100%. Para (otro) ejemplo, el sistema operativo VMS usualmente usaba lo que llamó "descriptores de cadena" http://h41379.www4.hpe.com/commercial/c/docs/5492p012.html accedido en C por #include <descrip.h >
Las cosas a nivel de aplicación pueden usar terminación nula o no, sin embargo, el desarrollador lo considera conveniente. Pero las cosas de VMS de bajo nivel requieren absolutamente descriptores, que no usan terminación nula (ver el enlace anterior para más detalles). Esto es en gran medida para que todos los lenguajes (C, ensamblaje, etc.) que usan directamente componentes internos de VMS puedan tener una interfaz común con ellos.
Por lo tanto, si está anticipando algún tipo de situación similar, es posible que desee ser algo más cuidadoso de lo que sugeriría la "terminación nula universal". Tendría más cuidado si estuviera haciendo lo que está haciendo, pero para mis cosas a nivel de aplicación es seguro asumir una terminación nula. Simplemente no te recomendaría el mismo nivel de seguridad. Es posible que su código tenga que interactuar con el ensamblado y / u otro código de idioma en algún momento futuro, que no siempre se ajusta al estándar C de las cadenas terminadas en nulo.
fuente
En mi experiencia con sistemas embebidos, críticos para la seguridad y en tiempo real, no es raro usar las convenciones de cadenas C y PASCAL, es decir, proporcionar la longitud de las cadenas como primer carácter (que limita la longitud a 255) y finalizar el cadena con al menos un 0x00, (
NUL
), que reduce el tamaño utilizable a 254.Una razón para esto es saber cuántos datos espera después de que se haya recibido el primer byte y otra es que, en tales sistemas, se evitan los tamaños dinámicos del búfer cuando sea posible: la asignación de un tamaño de búfer 256 fijo es más rápido y seguro (no necesita verificar si
malloc
falló). Otra es que los otros sistemas con los que se está comunicando pueden no estar escritos en ANSI-C.En cualquier trabajo integrado, es importante establecer y mantener un Documento de Control de Interfaz (IDC) que defina todas sus estructuras de comunicación, incluidos formatos de cadena, endianness, tamaños enteros, etc., lo antes posible ( idealmente antes de comenzar ), y debe ser su libro sagrado, y todos los equipos, al escribir el sistema: si alguien desea introducir una nueva estructura o formato, primero debe documentarse allí y todos los que puedan verse afectados deben estar informados, posiblemente con la opción de vetar el cambio .
fuente