it-swarm.com.de

Sind C-Strings immer nullterminiert oder hängt es von der Plattform ab?

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, NULL-terminierte Zeichenzeiger zu haben und sie als Zeichenfolgen zu behandeln, wobei NULL das Ende bedeutet. Ich weiß, dass dies ziemlich häufig ist, aber können Sie immer darauf zählen, 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 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 unverändert lassen und dann erwarten, dass die auf RTOS (der die Befehle enthält)) erstellten Zeichenfolgenobjekte alle mit NULL beendet werden? Oder wäre es basierend auf anders das Betriebssystem?

Update

Nachdem mir geraten wurde, einen Blick auf diese Frage zu werfen, habe ich festgestellt, dass sie nicht genau das beantwortet, was ich frage. 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 dort schienen Gründe zu geben, warum oder warum nicht Eine Zeichenfolge mit einem Nullzeichen 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.

13
Snoop

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.

42
Simon B

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 auf Pre-Standard C zurück, 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):

  • In Abschnitt 6.4.5 wird die Bühne für NUL - terminierte Zeichenfolgen festgelegt, indem an Zeichenfolgenliterale ein NUL angehängt werden muss.
  • Abschnitt 7.1.1 bringt dies zu den Funktionen in der Standardbibliothek, indem eine Zeichenfolge als "zusammenhängende Folge von Zeichen definiert wird, die mit dem ersten Nullzeichen abgeschlossen sind und dieses enthalten . "

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. :-)

22
Blrfl

Ich arbeite mit eingebetteten Systemen ... ohne Betriebssystem ... Ich verwende ... die Idee, NULL-terminierte Zeichenzeiger zu haben 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?

In der Sprache C gibt es keinen String-Datentyp, aber String-Literale.

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 einem Ort, an dem ein const char * Wert wird erwartet, der Compiler gibt foobar⊘ auf das const/code-Segment/den Abschnitt Ihres Programms, und der Wert des Ausdrucks ist ein Zeiger auf die Adresse, an der das Zeichen f gespeichert ist. (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. auf einem NUL anhalten).

3
Solomon Slow

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 die Steuerung 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.

2
Hugh Buntu

Andere haben das Problem angesprochen, dass in C Zeichenfolgen größtenteils das sind, was Sie daraus machen. Aber es scheint einige Verwirrung in Ihrer Frage zu geben. der Terminator selbst, 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 NUL abgeschlossen. Sie werden nicht durch den Nullzeiger NULL abgeschlossen, bei dem es sich um eine völlig andere Art von Wert mit einem völlig anderen Zweck handelt.

NUL hat garantiert den ganzzahligen Wert Null. Innerhalb der Zeichenfolge hat sie auch die Größe des zugrunde liegenden Zeichentyps, die normalerweise 1 beträgt.

NULL hat garantiert keinen Integer-Typ. 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 von NULL die Glyphe 0 Enthält, kann nicht garantiert werden, dass sie tatsächlich diesen Wert [1] hat, und es sei denn, Ihr Compiler implementiert die Konstante als ein Zeichen #define (viele nicht, da NULLwirklich in einem Nicht-Zeiger-Kontext nicht sinnvoll sein sollte), kann daher nicht garantiert werden, dass der erweiterte Code tatsächlich einen Nullwert enthält (gerade) obwohl es verwirrenderweise eine Null-Glyphe beinhaltet).

Wenn NULL 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 durch NUL (normalerweise geschrieben '\0') Beendet. Code, der explizit NULL als String-Terminator verwendet, 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 0Token in einem Kontext liest, in dem er in einen Zeigertyp konvertiert wird. Dies ist keine Konvertierung von der Ganzzahl Wert 0 und kann nicht garantiert werden, wenn etwas anderes als das Token 0 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.

1
Leushenko

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.

Wenn Sie eine Zeichenfolge schreiben, die in doppelte Anführungszeichen eingeschlossen ist, erstellt C automatisch ein Array von Zeichen für uns, das diese Zeichenfolge enthält und mit dem Zeichen\0 abgeschlossen ist.

Sie können beispielsweise ein Array von Zeichen deklarieren, definieren und mit einer Zeichenfolgenkonstante initialisieren:

char string[] = "Hello cruel world!";

Unkomplizierte Antwort: Sie müssen sich nicht wirklich um die Verwendung von Zeichen mit Nullterminierung kümmern, dies funktioniert unabhängig von jeder Plattform.

1
danglingpointer

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 zugegriffen wurde von # include <descip.h>

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. Möglicherweise muss Ihr Code 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.

1
John Forkosh

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 einer 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 ist). Ein weiterer Grund ist, dass die anderen Systeme, mit denen Sie kommunizieren, möglicherweise nicht in ANSI-C geschrieben sind.

In jeder eingebetteten Arbeit ist es wichtig, so schnell wie möglich ein Interface Control Document (IDC) einzurichten und zu verwalten, das alle Ihre Kommunikationsstrukturen einschließlich Zeichenfolgenformaten, Endianness, Ganzzahlgrößen usw. definiert ( idealerweise vor dem Start ), und es sollte Ihr und alle Teams sein, heiliges Buch beim Schreiben des Systems - wenn jemand eine neue Struktur einführen oder formatieren möchte muss dort dokumentiert sein zuerst und jeder, der betroffen sein könnte, informiert werden, möglicherweise mit der Option, gegen die Änderung ein Veto einzulegen.

0
Steve Barnes