En ce moment, je travaille avec des systèmes embarqués et je trouve des moyens d'implémenter des chaînes sur un microprocesseur sans système d'exploitation. Jusqu'à présent, ce que je fais est simplement d'utiliser l'idée d'avoir des pointeurs de caractères terminés par NULL et de les traiter comme des chaînes où NULL signifie la fin. Je sais que c'est assez courant, mais pouvez-vous toujours compter sur cela pour être le cas?
La raison pour laquelle je demande, c'est que je pensais peut-être à un moment donné utiliser un système d'exploitation en temps réel, et j'aimerais réutiliser autant que possible mon code actuel. Donc, pour les différents choix qui existent, puis-je m'attendre à peu près à ce que les chaînes fonctionnent de la même manière?
Permettez-moi cependant d'être plus précis pour mon cas. J'implémente un système qui prend et traite les commandes sur un port série. Puis-je conserver mon code de traitement des commandes de la même manière, puis m'attendre à ce que les objets chaîne créés sur le RTOS (qui contient les commandes) soient tous terminés NULL? Ou serait-ce différent en fonction du système d'exploitation?
Mise à jour
Après avoir été invité à jeter un œil à cette question, j'ai déterminé qu'elle ne répond pas exactement à ce que je demande. La question elle-même demande si une longueur de chaîne doit toujours être transmise, ce qui est entièrement différent de ce que je demande, et bien que certaines des réponses contenaient des informations utiles, elles ne sont pas exactement ce que je recherche. Les réponses semblent donner des raisons pour lesquelles ou pourquoi ne pas terminer une chaîne avec un caractère nul. La différence avec ce que je demande, c'est si je peux plus ou moins m'attendre à ce que les chaînes innées de différentes plates-formes terminent leurs propres chaînes avec null, sans avoir à sortir et essayer chaque plate-forme là-bas si cela a du sens.
Réponses:
Les choses appelées "chaînes C" seront terminées par null sur n'importe quelle plate-forme. C'est ainsi que les fonctions de bibliothèque C standard déterminent la fin d'une chaîne.
Dans le langage C, rien ne vous empêche d'avoir un tableau de caractères qui ne se termine pas par un null. Cependant, vous devrez utiliser une autre méthode pour éviter d'exécuter la fin d'une chaîne.
la source
char
tableaux à terminaison nulle , deschar
tableaux dont la longueur est codée dans le premier octet (communément appelés "chaînes Pascal"), deswchar_t
versions basées sur les deux ci-dessus et deschar
tableaux qui combinent les deux méthodes: longueur codée dans le premier octet et un caractère nul terminant la chaîne.La détermination du caractère de terminaison dépend du compilateur pour les littéraux et de l'implémentation de la bibliothèque standard pour les chaînes en général. Il n'est pas déterminé par le système d'exploitation.
La convention de
NUL
résiliation remonte au C pré-standard, et dans plus de 30 ans, je ne peux pas dire que j'ai rencontré un environnement qui fait autre chose. Ce comportement a été codifié en C89 et continue de faire partie de la norme du langage C (lien vers un projet de C99):NUL
des chaînes terminées en exigeant que aNUL
soit ajouté aux littéraux de chaîne.Il n'y a aucune raison pour que quelqu'un ne puisse pas écrire des fonctions qui gèrent des chaînes terminées par un autre caractère, mais il n'y a également aucune raison de contourner la norme établie dans la plupart des cas, sauf si votre objectif est de donner aux programmeurs des ajustements. :-)
la source
printf("string: \"%s\"\n", "my cool string")
. La seule façon de passer quatre paramètres dans ce cas (autre qu'un certain octet de terminaison) serait de définir une chaîne comme quelque chose commestd::string
en C ++, qui a ses propres problèmes et limites.NUL
terminez-les quoi qu'il arrive: "Dans la phase de traduction 7, un octet ou un code de la valeur zéro est ajoutée à chaque séquence de caractères multi-octets qui résulte d'un ou plusieurs littéraux de chaîne. " Les fonctions de bibliothèque utilisant la définition de 7.1.1 s'arrêtent dèsNUL
qu'elles trouvent et ne savent pas ou ne se soucient pas que des caractères supplémentaires existent au-delà.Il n'y a pas de type de données de chaîne dans le langage C, mais il existe des littéraux de chaîne .
Si vous mettez un littéral de chaîne dans votre programme, il se terminera généralement par NUL (mais voyez le cas spécial, discuté dans les commentaires ci-dessous.) Autrement dit, si vous mettez
"foobar"
à un endroit où uneconst char *
valeur est attendue, le compilateur émettrafoobar⊘
vers le segment / section const / code de votre programme, et la valeur de l'expression sera un pointeur vers l'adresse où il a stocké lef
caractère. (Remarque: j'utilise⊘
pour signifier l'octet NUL.)Le seul autre sens dans lequel le langage C a des chaînes est qu'il a des routines de bibliothèque standard qui fonctionnent sur des séquences de caractères terminées par NUL. Ces routines de bibliothèque n'existeront pas dans un environnement sans système d'exploitation, sauf si vous les portez vous-même.
Ce n'est que du code --- pas différent du code que vous écrivez vous-même. Si vous ne les cassez pas lorsque vous les portez, ils feront ce qu'ils font toujours (par exemple, arrêtez-vous sur un NUL.)
la source
char foo[4] = "abcd";
est un moyen valide de créer un tableau de quatre caractères non terminé par null.char const *
expression est attendue. J'ai oublié que les initialiseurs C peuvent parfois obéir à des règles différentes.char[4]
. Ce n'est pas une chaîne, mais elle a été initialisée à partir d'unestatic
à l'exemple de Ruakh, alors le compilateur peut émettre un "abcd" non terminé par NUL vers un segment de données initialisé afin que la variable soit initialisée par le chargeur de programme. Donc, Ruakh avait raison: il y a au moins un cas où l'apparition d'un littéral de chaîne dans un programme ne nécessite pas que le compilateur émette une chaîne terminée par NUL. (ps, j'ai en fait compilé l'exemple avec gcc 5.4.0, et le compilateur n'a pas émis le NUL.)Comme d'autres l'ont mentionné, la terminaison nulle des chaînes est une convention de la bibliothèque standard C. Vous pouvez gérer les chaînes comme vous le souhaitez si vous n'utilisez pas la bibliothèque standard.
Cela est vrai de tout système d'exploitation avec un compilateur «C», et vous pouvez également écrire des programmes «C» qui ne sont pas exécutés sous un véritable système d'exploitation, comme vous le mentionnez dans votre question. Un exemple serait le contrôleur d'une imprimante à jet d'encre que j'ai conçue une fois. Dans les systèmes embarqués, la surcharge de mémoire d'un système d'exploitation peut ne pas être nécessaire.
Dans des situations où la mémoire est limitée, je regarderais par exemple les caractéristiques de mon compilateur par rapport au jeu d'instructions du processeur. Dans une application où les chaînes sont beaucoup traitées, il peut être souhaitable d'utiliser des descripteurs tels que la longueur des chaînes. Je pense à un cas où le CPU est particulièrement efficace pour travailler avec des décalages courts et / ou des décalages relatifs avec des registres d'adresses.
Alors, qu'est-ce qui est le plus important dans votre application: taille et efficacité du code, ou compatibilité avec un système d'exploitation ou une bibliothèque? Une autre considération pourrait être la maintenabilité. Plus vous vous éloignez de la convention, plus il sera difficile à quelqu'un de maintenir.
la source
D'autres ont abordé le problème qu'en C, les chaînes sont en grande partie ce que vous en faites. Mais il semble qu'il y ait une certaine confusion dans votre question par rapport au terminateur lui-même, et d'un point de vue, cela pourrait inquiéter quelqu'un dans votre position.
Les chaînes C sont terminées par null. Autrement dit, ils sont terminés par le caractère nul,
NUL
. Ils ne sont pas terminés par le pointeur nulNULL
, qui est un type de valeur complètement différent avec un objectif complètement différent.NUL
est garanti d'avoir la valeur entière zéro. Dans la chaîne, il aura également la taille du type de caractère sous-jacent, qui sera généralement 1.NULL
n'est pas du tout garanti d'avoir un type entier.NULL
est destiné à être utilisé dans un contexte de pointeur, et devrait généralement avoir un type de pointeur, qui ne devrait pas être converti en caractère ou entier si votre compilateur est bon. Bien que la définition deNULL
implique le glyphe0
, il n'est pas garanti d'avoir réellement cette valeur [1], et à moins que votre compilateur implémente la constante comme un caractère#define
(beaucoup ne le font pas, carNULL
vraiment ne devrait pas avoir de sens dans un non contexte du pointeur), le code développé n'est donc pas garanti d'impliquer réellement une valeur zéro (même s'il implique de manière confuse un zéro glyphe).Si
NULL
est tapé, il est également peu probable qu'il ait une taille de 1 (ou une autre taille de caractère). Cela peut éventuellement entraîner des problèmes supplémentaires, bien que les constantes de caractères réelles n'aient pas de taille de caractère pour la plupart.Maintenant, la plupart des gens verront cela et penseront, "pointeur nul comme autre chose que des bits zéro? Quelle absurdité" - mais des hypothèses comme celles-ci ne sont sûres que sur des plates-formes courantes comme x86. Étant donné que vous avez explicitement mentionné un intérêt pour le ciblage d'autres plates-formes, vous devez prendre en compte ce problème, car vous avez explicitement séparé votre code des hypothèses sur la nature de la relation entre les pointeurs et les entiers.
Par conséquent, bien que les chaînes C soient terminées par null, elles ne sont pas terminées par
NULL
, mais parNUL
(généralement écrit'\0'
). Le code qui utilise explicitementNULL
comme terminateur de chaîne fonctionnera sur les plates-formes avec une structure d'adresse simple et sera même compilé avec de nombreux compilateurs, mais ce n'est absolument pas correct C.[1] la valeur réelle du pointeur nul est insérée par le compilateur lorsqu'il lit un
0
jeton dans un contexte où il serait converti en un type de pointeur. Il ne s'agit pas d'une conversion à partir de la valeur entière 0 et il n'est pas garanti de la conserver si autre chose que le jeton0
lui-même est utilisé, comme une valeur dynamique à partir d'une variable; la conversion n'est pas non plus réversible, et un pointeur nul n'a pas à donner la valeur 0 lorsqu'il est converti en entier.la source
NUL
est garanti d'avoir la valeur entière zéro." -> C ne définit pasNUL
. Au lieu de cela, C définit que les chaînes ont un dernier caractère nul , un octet avec tous les bits mis à 0.J'utilise une chaîne en C, cela signifie que les caractères avec une terminaison nulle s'appellent des chaînes.
Il n'aura aucun problème lorsque vous utilisez dans baremetal ou dans tout système d'exploitation tel que Windows, Linux, RTOS: (FreeRTO, OSE).
Dans le monde intégré, la terminaison nulle aide en fait plus à marquer le caractère sous forme de chaîne.
J'ai utilisé des chaînes en C comme ça dans de nombreux systèmes critiques pour la sécurité.
Vous vous demandez peut-être, quelle est la chaîne en C?
Les chaînes de style C, qui sont des tableaux, il existe également des littéraux de chaîne, tels que "this". En réalité, ces deux types de chaînes ne sont que des ensembles de caractères assis côte à côte en mémoire.
Par exemple, vous pouvez déclarer et définir un tableau de caractères et l'initialiser avec une constante de chaîne:
Réponse simple: vous n'avez pas vraiment à vous soucier de l'utilisation de caractères avec une terminaison nulle, ce travail indépendamment de toute plate-forme.
la source
NUL
est automatiquement ajouté.Comme d'autres l'ont dit, la terminaison nulle est à peu près universelle pour le standard C. Mais (comme d'autres l'ont également souligné) pas à 100%. Pour (un autre) exemple, le système d'exploitation VMS utilise généralement ce qu'il appelle des "descripteurs de chaîne" http://h41379.www4.hpe.com/commercial/c/docs/5492p012.html accessible en C par #include <descrip.h >
Les éléments au niveau de l'application peuvent utiliser une terminaison nulle ou non, mais le développeur le juge approprié. Mais les trucs VMS de bas niveau nécessitent absolument des descripteurs, qui n'utilisent pas du tout de terminaison nulle (voir le lien ci-dessus pour plus de détails). C'est en grande partie pour que tous les langages (C, assemblage, etc.) qui utilisent directement les internes VMS puissent avoir une interface commune avec eux.
Donc, si vous prévoyez tout type de situation similaire, vous voudrez peut-être être un peu plus prudent que ce que la "terminaison nulle universelle" pourrait suggérer est nécessaire. Je serais plus prudent si je faisais ce que vous faites, mais pour mes trucs au niveau de l'application, il est sûr de supposer une terminaison nulle. Je ne vous proposerais tout simplement pas le même niveau de sécurité. Votre code pourrait bien avoir à s'interfacer avec l'assembly et / ou un autre code de langue à un moment ultérieur, qui ne sera peut-être pas toujours conforme à la norme C des chaînes à terminaison nulle.
la source
D'après mon expérience des systèmes embarqués, critiques pour la sécurité et en temps réel, il n'est pas rare d'utiliser à la fois les conventions de chaîne C et PASCAL, c'est-à-dire de fournir la longueur des chaînes comme premier caractère (ce qui limite la longueur à 255) et de mettre fin à la chaîne avec au moins un 0x00, (
NUL
), ce qui réduit la taille utilisable à 254.Une raison à cela est de savoir combien de données vous attendez après la réception du premier octet et une autre est que, dans de tels systèmes, les tailles de mémoire tampon dynamique sont évitées dans la mesure du possible - l'allocation d'une taille de mémoire tampon 256 fixe est plus rapide et plus sûre, (non besoin de vérifier en cas d'
malloc
échec). Un autre est que les autres systèmes avec lesquels vous communiquez peuvent ne pas être écrits en ANSI-C.Dans tout travail intégré, il est important d'établir et de maintenir un document de contrôle d'interface (IDC), qui définit toutes vos structures de communication, y compris les formats de chaîne, l'endianité, les tailles entières, etc., dès que possible ( idéalement avant de commencer ), et cela devrait être votre livre sacré, ainsi que toutes les équipes, lors de l'écriture du système - si quelqu'un souhaite introduire une nouvelle structure ou un nouveau format, il doit d' abord y être documenté et toutes les personnes susceptibles d'être impactées doivent être informées, éventuellement avec une option de veto sur le changement .
la source