Im Moment arbeite ich mit eingebetteten Systemen und finde heraus, wie Strings auf einem Mikroprozessor ohne Betriebssystem implementiert werden können. Bisher verwende ich nur die Idee, Zeichenzeiger mit NULL-Terminierung zu verwenden und sie als Zeichenfolgen zu behandeln, wobei NULL das Ende bedeutet. Ich weiß, dass dies ziemlich häufig ist, aber können Sie sich immer darauf verlassen, dass dies der Fall ist?
Der Grund, den ich frage, ist, dass ich darüber nachgedacht habe, irgendwann ein Echtzeitbetriebssystem zu verwenden, und ich möchte so viel wie möglich meinen aktuellen Code wiederverwenden. Kann ich also für die verschiedenen Auswahlmöglichkeiten, die es gibt, ziemlich genau erwarten, dass die Saiten gleich funktionieren?
Lassen Sie mich jedoch genauer auf meinen Fall eingehen. Ich implementiere ein System, das Befehle über eine serielle Schnittstelle entgegennimmt und verarbeitet. Kann ich meinen Befehlsverarbeitungscode beibehalten und dann erwarten, dass die auf dem RTOS (das die Befehle enthält) erstellten Zeichenfolgenobjekte alle mit NULL beendet werden? Oder wäre es je nach Betriebssystem anders?
Aktualisieren
Nachdem mir geraten wurde, diese Frage zu prüfen, habe ich festgestellt, dass sie nicht genau das beantwortet, was ich stelle. Die Frage selbst ist, ob die Länge eines Strings immer übergeben werden sollte, was völlig anders ist als das, was ich frage, und obwohl einige der Antworten nützliche Informationen enthielten, sind sie nicht genau das, wonach ich suche. Die Antworten scheint es Gründe zu geben , warum oder warum nicht eine Zeichenfolge mit einem Null - Zeichen zu beenden. Der Unterschied zu dem, was ich frage, besteht darin, ob ich mehr oder weniger erwarten kann, dass die angeborenen Zeichenfolgen verschiedener Plattformen ihre eigenen Zeichenfolgen mit null beenden, ohne dass ich jede einzelne Plattform ausprobieren muss, wenn dies sinnvoll ist.
quelle
Antworten:
Die Dinge, die als "C-Strings" bezeichnet werden, werden auf jeder Plattform mit Null terminiert. Auf diese Weise bestimmen die Standardfunktionen der C-Bibliothek das Ende einer Zeichenfolge.
In der C-Sprache hindert Sie nichts daran, ein Array von Zeichen zu haben, das nicht mit einer Null endet. Sie müssen jedoch eine andere Methode verwenden, um zu vermeiden, dass das Ende einer Zeichenfolge abläuft.
quelle
char
Arrays,char
Arrays mit der im ersten Byte codierten Länge (allgemein als "Pascal-Zeichenfolgen" bezeichnet),wchar_t
basierte Versionen beider oben undchar
Arrays, die beide Methoden kombinieren: Länge, die im ersten Byte codiert ist, und ein Nullzeichen, das die Zeichenfolge beendet.Die Bestimmung des Abschlusszeichens liegt beim Compiler für Literale und der Implementierung der Standardbibliothek für Zeichenfolgen im Allgemeinen. Es wird nicht vom Betriebssystem bestimmt.
Die Konvention der
NUL
Kündigung geht zurück auf C vor dem Standard, und in mehr als 30 Jahren kann ich nicht sagen, dass ich auf eine Umgebung gestoßen bin, die etwas anderes tut. Dieses Verhalten wurde in C89 kodifiziert und ist weiterhin Teil des C-Sprachstandards (Link zu einem Entwurf von C99):NUL
Zeichenfolgen festgelegt, indem verlangt wird, dass aNUL
an Zeichenfolgenliterale angehängt wird.Es gibt keinen Grund, warum jemand keine Funktionen schreiben könnte, die Zeichenfolgen verarbeiten, die von einem anderen Zeichen beendet werden, aber es gibt in den meisten Fällen auch keinen Grund, sich gegen den etablierten Standard zu sträuben, es sei denn, Ihr Ziel ist es, Programmierern Passungen zu geben. :-)
quelle
printf("string: \"%s\"\n", "my cool string")
. Die einzige Möglichkeit, in diesem Fall vier Parameter zu übergeben (abgesehen von einer Art Abschlussbyte), besteht darin, eine Zeichenfolge so zu definieren, dass siestd::string
in C ++ ähnlich ist und ihre eigenen Probleme und Einschränkungen aufweist.NUL
beenden, egal was passiert: "In der Übersetzungsphase 7 ein Byte oder Code Der Wert Null wird an jede Multibyte-Zeichenfolge angehängt, die sich aus einem Zeichenfolgenliteral oder Literalen ergibt. " Bibliotheksfunktionen, die die Definition von 7.1.1 verwenden, hören beim erstenNUL
Auffinden auf und wissen nicht, dass zusätzliche Zeichen darüber hinaus vorhanden sind.In der Sprache C gibt es keinen Zeichenfolgendatentyp, aber Zeichenfolgenliterale .
Wenn Sie ein Zeichenfolgenliteral in Ihr Programm einfügen, wird es normalerweise mit NUL beendet (siehe jedoch den Sonderfall, der in den Kommentaren unten erläutert wird). Das heißt, wenn Sie
"foobar"
an einer Stelleconst char *
einfügen, an der ein Wert erwartet wird, wird der Compiler ausgegebenfoobar⊘
auf das const / code-Segment / den Abschnitt Ihres Programms, und der Wert des Ausdrucks ist ein Zeiger auf die Adresse, an der dasf
Zeichen gespeichert wurde . (Hinweis: Ich verwende⊘
, um das NUL-Byte zu kennzeichnen.)Der einzige andere Sinn, in dem die C-Sprache Zeichenfolgen enthält, besteht darin, dass sie einige Standardbibliotheksroutinen enthält, die mit NUL-terminierten Zeichenfolgen arbeiten. Diese Bibliotheksroutinen existieren in einer Bare-Metal-Umgebung nur, wenn Sie sie selbst portieren.
Sie sind nur Code - nicht anders als der Code, den Sie selbst schreiben. Wenn Sie sie beim Portieren nicht beschädigen, tun sie das, was sie immer tun (z. B. halten Sie an einem NUL an).
quelle
char foo[4] = "abcd";
eine gültige Methode ist, um ein nicht nullterminiertes Array mit vier Zeichen zu erstellen.char const *
Ausdruck erwartet wird. Ich habe vergessen, dass C- Initialisierer manchmal anderen Regeln folgen können.char[4]
. Das ist keine Zeichenfolge, aber sie wurde von einerstatic
zu Ruakh des Beispiel, dann wird der Compiler kann emittieren ein nicht NUL „ABCD“ zu einem initialisierten Datensegment beendet , so dass der Variable vom Programm - Loader initialisiert wird. Ruakh hatte also Recht: Es gibt mindestens einen Fall, in dem das Erscheinen eines String-Literal in einem Programm nicht erfordert, dass der Compiler einen NUL-terminierten String ausgibt. (ps, ich habe das Beispiel tatsächlich mit gcc 5.4.0 kompiliert, und der Compiler hat die NUL nicht ausgegeben.)Wie andere bereits erwähnt haben, ist die Nullterminierung von Zeichenfolgen eine Konvention der C-Standardbibliothek. Sie können mit Zeichenfolgen beliebig umgehen, wenn Sie die Standardbibliothek nicht verwenden.
Dies gilt für jedes Betriebssystem mit einem C-Compiler. Sie können auch C-Programme schreiben, die nicht unter einem echten Betriebssystem ausgeführt werden, wie Sie in Ihrer Frage erwähnt haben. Ein Beispiel wäre der Controller für einen Tintenstrahldrucker, den ich einmal entworfen habe. In eingebetteten Systemen ist der Speicheraufwand eines Betriebssystems möglicherweise nicht erforderlich.
In speicherarmen Situationen würde ich zum Beispiel die Eigenschaften meines Compilers gegenüber dem Befehlssatz des Prozessors betrachten. In einer Anwendung, in der Zeichenfolgen häufig verarbeitet werden, kann es wünschenswert sein, Deskriptoren wie die Zeichenfolgenlänge zu verwenden. Ich denke an einen Fall, in dem die CPU besonders effizient mit kurzen Offsets und / oder relativen Offsets mit Adressregistern arbeitet.
Was ist in Ihrer Anwendung wichtiger: Codegröße und -effizienz oder Kompatibilität mit einem Betriebssystem oder einer Bibliothek? Eine weitere Überlegung könnte die Wartbarkeit sein. Je weiter Sie von der Konvention abweichen, desto schwieriger wird es für andere, diese aufrechtzuerhalten.
quelle
Andere haben das Problem angesprochen, dass in C Zeichenfolgen größtenteils das sind, was Sie daraus machen. Aber Ihre Frage bezüglich des Terminators selbst scheint etwas verwirrend zu sein, und aus einer Perspektive könnte dies das sein, worüber sich jemand in Ihrer Position Sorgen macht.
C-Strings sind nullterminiert. Das heißt, sie werden durch das Nullzeichen abgeschlossen
NUL
. Sie werden nicht durch den Nullzeiger abgeschlossenNULL
, der eine völlig andere Art von Wert mit einem völlig anderen Zweck darstellt.NUL
ist garantiert, den ganzzahligen Wert Null zu haben. Innerhalb der Zeichenfolge hat sie auch die Größe des zugrunde liegenden Zeichentyps, die normalerweise 1 beträgt.NULL
Es wird nicht garantiert, dass es einen ganzzahligen Typ gibt.NULL
ist für die Verwendung in einem Zeigerkontext vorgesehen und es wird allgemein erwartet, dass es einen Zeigertyp hat, der nicht in ein Zeichen oder eine Ganzzahl konvertiert werden sollte, wenn Ihr Compiler gut ist. Während die Definition vonNULL
das Glyphen beinhaltet0
, ist nicht garantiert, dass es tatsächlich diesen Wert hat [1], und es sei denn, Ihr Compiler implementiert die Konstante als ein Zeichen#define
(viele nicht, weil es in einem Nicht -ZeichenNULL
wirklich nicht sinnvoll sein sollte Zeigerkontext), es ist daher nicht garantiert, dass der erweiterte Code tatsächlich einen Nullwert enthält (obwohl er verwirrenderweise einen Null-Glyphen enthält).Wenn
NULL
es eingegeben wird, ist es auch unwahrscheinlich, dass es eine Größe von 1 (oder eine andere Zeichengröße) hat. Dies kann möglicherweise zusätzliche Probleme verursachen, obwohl die tatsächlichen Zeichenkonstanten zum größten Teil auch keine Zeichengröße haben.Jetzt werden die meisten Leute dies sehen und denken: "Nullzeiger als etwas anderes als All-Null-Bits? Was für ein Unsinn" - aber solche Annahmen sind nur auf gängigen Plattformen wie x86 sicher. Da Sie ausdrücklich ein Interesse an der Ausrichtung auf andere Plattformen erwähnt haben, müssen Sie dieses Problem berücksichtigen, da Sie Ihren Code explizit von Annahmen über die Art der Beziehung zwischen Zeigern und Ganzzahlen getrennt haben.
Während C-Zeichenfolgen nullterminiert sind, werden sie daher nicht durch
NULL
, sondern durchNUL
(normalerweise geschrieben'\0'
) terminiert . Code, der explizitNULL
als String-Terminator verwendet wird, funktioniert auf Plattformen mit einer einfachen Adressstruktur und wird sogar mit vielen Compilern kompiliert, ist jedoch absolut nicht korrekt. C.[1] Der tatsächliche Nullzeigerwert wird vom Compiler eingefügt, wenn er ein
0
Token in einem Kontext liest, in dem es in einen Zeigertyp konvertiert wird. Dies ist keine Konvertierung vom ganzzahligen Wert 0 und kann nicht garantiert werden, wenn etwas anderes als das Token0
selbst verwendet wird, z. B. ein dynamischer Wert aus einer Variablen. Die Konvertierung ist auch nicht umkehrbar, und ein Nullzeiger muss bei der Konvertierung in eine Ganzzahl nicht den Wert 0 ergeben.quelle
NUL
ist garantiert, dass der ganzzahlige Wert Null ist." -> C definiert nichtNUL
. Stattdessen definiert C, dass Strings einen endgültigen Null-Chracter haben , ein Byte, bei dem alle Bits auf 0 gesetzt sind.Ich habe eine Zeichenfolge in C verwendet. Dies bedeutet, dass Zeichen mit Nullterminierung als Zeichenfolgen bezeichnet werden.
Es gibt keine Probleme, wenn Sie in Baremetal oder in Betriebssystemen wie Windows, Linux, RTOS: (FreeRTO, OSE) verwenden.
In der eingebetteten Welt hilft die Nullterminierung tatsächlich mehr, das Zeichen als Zeichenfolge zu kennzeichnen.
Ich habe in vielen sicherheitskritischen Systemen solche Zeichenfolgen in C verwendet.
Sie fragen sich vielleicht, was ist eigentlich ein String in C?
C-artige Strings, die Arrays sind, gibt es auch String-Literale wie "this". In Wirklichkeit sind diese beiden Zeichenfolgentypen lediglich Sammlungen von Zeichen, die im Speicher nebeneinander sitzen.
Sie können beispielsweise ein Array von Zeichen deklarieren, definieren und mit einer Zeichenfolgenkonstante initialisieren:
Unkomplizierte Antwort: Sie müssen sich nicht wirklich um die Verwendung von Zeichen mit Nullterminierung kümmern, dies funktioniert unabhängig von einer Plattform.
quelle
NUL
automatisch angehängt wird.Wie andere gesagt haben, ist die Nullterminierung für Standard C ziemlich universell. Aber (wie andere auch betont haben) nicht 100%. Für (ein anderes) Beispiel verwendete das VMS-Betriebssystem normalerweise sogenannte "String-Deskriptoren" http://h41379.www4.hpe.com/commercial/c/docs/5492p012.html, auf die in C über #include <descip.h zugegriffen wurde >
Inhalte auf Anwendungsebene können eine Nullterminierung verwenden oder nicht, der Entwickler hält dies jedoch für richtig. Für VMS-Inhalte auf niedriger Ebene sind jedoch unbedingt Deskriptoren erforderlich, die überhaupt keine Nullterminierung verwenden (Einzelheiten siehe Link oben). Dies ist weitgehend so, dass alle Sprachen (C, Assembly usw.), die direkt VMS-Interna verwenden, eine gemeinsame Schnittstelle mit ihnen haben können.
Wenn Sie also eine ähnliche Situation erwarten, sollten Sie etwas vorsichtiger sein, als es eine "universelle Nullterminierung" vermuten lässt. Ich wäre vorsichtiger, wenn ich das tun würde, was Sie tun, aber für meine Sachen auf Anwendungsebene ist es sicher, eine Nullbeendigung anzunehmen. Ich würde Ihnen einfach nicht das gleiche Maß an Sicherheit vorschlagen. Ihr Code muss möglicherweise zu einem späteren Zeitpunkt mit Assembly- und / oder anderem Sprachcode verbunden werden, was möglicherweise nicht immer dem C-Standard für nullterminierte Zeichenfolgen entspricht.
quelle
Nach meiner Erfahrung mit eingebetteten, sicherheitskritischen und Echtzeitsystemen ist es nicht ungewöhnlich, sowohl die C- als auch die PASCAL-Zeichenfolgenkonvention zu verwenden, dh die Zeichenfolgenlänge als erstes Zeichen anzugeben (was die Länge auf 255 begrenzt) und das zu beenden Zeichenfolge mit mindestens einem 0x00 (
NUL
), wodurch die verwendbare Größe auf 254 reduziert wird.Ein Grund dafür ist zu wissen, wie viele Daten Sie nach dem Empfang des ersten Bytes erwarten, und ein anderer Grund ist, dass in solchen Systemen dynamische Puffergrößen nach Möglichkeit vermieden werden - die Zuweisung einer festen Puffergröße von 256 ist schneller und sicherer (Nr müssen überprüfen, ob
malloc
fehlgeschlagen). Ein weiterer Grund ist, dass die anderen Systeme, mit denen Sie kommunizieren, möglicherweise nicht in ANSI-C geschrieben sind.Bei jeder eingebetteten Arbeit ist es wichtig, so schnell wie möglich ( idealerweise vor dem Start ) ein Interface Control Document (IDC) einzurichten und zu verwalten, das alle Ihre Kommunikationsstrukturen einschließlich Zeichenfolgenformaten, Endianness, Ganzzahlgrößen usw. definiert. und es sollte Ihr und alle Teams heiliges Buch sein, wenn Sie das System schreiben - wenn jemand eine neue Struktur oder ein neues Format einführen möchte, muss es zuerst dort dokumentiert und jeder, der betroffen sein könnte, informiert werden, möglicherweise mit der Option, gegen die Änderung ein Veto einzulegen .
quelle