Ich habe einen Thread mit dem Titel "strlen vs sizeof" auf CodeGuru gelesen , und eine der Antworten besagt, dass "es sowieso eine schlechte Praxis ist, ein char
Array mit einem String-Literal zu initialisieren ".
Stimmt das, oder ist das nur seine (wenn auch "Elite-Mitglied") Meinung?
Hier ist die ursprüngliche Frage:
#include <stdio.h>
#include<string.h>
main()
{
char string[] = "october";
strcpy(string, "september");
printf("the size of %s is %d and the length is %d\n\n", string, sizeof(string), strlen(string));
return 0;
}
richtig. die größe sollte die länge plus 1 sein ja?
das ist die Ausgabe
the size of september is 8 and the length is 9
größe sollte sicher 10 sein. Es ist wie das Berechnen der Zeichenfolgengröße, bevor es von strcpy geändert wird, aber die Länge danach.
Stimmt etwas mit meiner Syntax nicht oder was?
Hier ist die Antwort :
Es ist sowieso eine schlechte Praxis, ein Zeichen-Array mit einem String-Literal zu initialisieren. Führen Sie daher immer einen der folgenden Schritte aus:
const char string1[] = "october";
char string2[20]; strcpy(string2, "september");
quelle
Antworten:
Der Autor dieses Kommentars rechtfertigt es nie wirklich, und ich finde die Aussage rätselhaft.
In C (und Sie dies als C markiert haben), das ist so ziemlich der einzige Weg zu initialisieren ein Array von
char
mit einem String - Wert (Initialisierung unterscheidet sich von Zuordnung). Sie können entweder schreibenoder
oder
Im ersten Fall wird die Größe des Arrays von der Größe des Initialisierers abgeleitet. String-Literale werden als Arrays
char
mit einem abschließenden 0-Byte gespeichert , sodass die Größe des Arrays 8 ist ('o', 'c', 't', 'o', 'b', 'e', 'r', 0). In den zweiten beiden Fällen wird die Größe des Arrays als Teil der Deklaration angegeben (8 undMAX_MONTH_LENGTH
was auch immer das sein mag).Was Sie nicht tun können, ist etwas zu schreiben
oder
usw. Im ersten Fall, der die Erklärung
string
ist unvollständig , da keine Array - Größe angegeben wurde und es gibt keine Initialisierer von der Größe zu nehmen. In beiden Fällen=
funktioniert das nicht, weil a) ein Array-Ausdruck, wie erstring
möglicherweise nicht das Ziel einer Zuweisung ist, und b) der=
Operator ohnehin nicht definiert ist, den Inhalt eines Arrays in ein anderes zu kopieren.Aus dem gleichen Grund können Sie nicht schreiben
wo
foo
ist ein anderes Array vonchar
. Diese Form der Initialisierung funktioniert nur mit String-Literalen.BEARBEITEN
Ich sollte dies dahingehend ändern, dass Sie Arrays auch so initialisieren können, dass sie eine Zeichenfolge mit einem Initialisierer im Array-Stil enthalten, z
oder
Aber es ist für die Augen einfacher, String-Literale zu verwenden.
BEARBEITEN 2
Um den Inhalt eines Arrays außerhalb einer Deklaration zuzuweisen , müssen Sie entweder
strcpy/strncpy
(für Zeichenfolgen mit 0-Abschluss) odermemcpy
(für jeden anderen Array-Typ) Folgendes verwenden:oder
quelle
strncpy
ist selten die richtige Antwortchar[8] str = "october";
eine schlechte Praxis ist. Ich hatte buchstäblich char mich zu zählen , um sicherzustellen , es war kein Überlauf und es bricht unter Wartung ... zB einen Rechtschreibfehler zu korrigieren von brechen , wenn Größe nicht aktualisiert.seprate
separate
strlen()
nicht das Nullzeichen enthält, mitMAX_MONTH_LENGTH
der maximalen Größe für erforderlich hältchar string[]
oft sieht . Falsch IMO,MAX_MONTH_SIZE
wäre hier besser sein.Das einzige Problem, an das ich mich erinnere, ist das Zuweisen von Zeichenfolgenliteral zu
char *
:Nehmen Sie zum Beispiel dieses Programm:
Dies stürzt auf meiner Plattform (Linux) ab, wenn versucht wird, auf eine Seite zu schreiben, die als schreibgeschützt markiert ist. Auf anderen Plattformen wird möglicherweise "September" usw. gedruckt.
Das heißt, die Initialisierung durch Literal bewirkt, dass der bestimmte Reservierungsbetrag nicht funktioniert:
Aber das wird es
Als letzte Bemerkung - würde ich gar nicht verwenden
strcpy
:Während einige Compiler es in einen sicheren Aufruf umwandeln können, ist es
strncpy
viel sicherer:quelle
strncpy
da der kopierte String nicht durch null beendet wird, wenn length ofsomething_else
größer als istsizeof(buf)
. Normalerweise lege ich das letzte Zeichen festbuf[sizeof(buf)-1] = 0
, um es davor zu schützen, oderbuf
verwende essizeof(buf) - 1
als Kopierlänge, wenn es auf Null gesetzt ist .strlcpy
oderstrcpy_s
oder sogar,snprintf
wenn Sie müssen.strlcpy
undsnprintf
sind auf MSVC nicht direkt verfügbar, zumindest bei Bestellungen undstrcpy_s
nicht auf * nix).Eine Sache, die keiner der Themen aufwirft, ist die folgende:
gegen
Ersterer wird so etwas tun wie:
Letzteres macht nur die memcpy. Der C-Standard besteht darauf, dass, wenn ein Teil eines Arrays initialisiert wird, dies auch der Fall ist. In diesem Fall ist es also besser, es selbst zu tun. Ich denke, das war vielleicht das, worauf Treuss abzielte.
Sicher
ist besser als entweder:
oder
ps Für Bonuspunkte können Sie Folgendes tun:
um eine durch Null geteilte Kompilierzeit zu erhalten, wenn das Array überlaufen soll.
quelle
In erster Linie, weil Sie nicht die Größe von
char[]
in einer Variablen / einem Konstrukt haben, die / das Sie einfach im Programm verwenden können.Das Codebeispiel aus dem Link:
string
wird auf dem Stapel mit einer Länge von 7 oder 8 Zeichen zugewiesen. Ich kann mich nicht erinnern, ob es auf diese Weise nullterminiert wurde oder nicht - der Thread, mit dem Sie verlinkt haben, hat dies bestätigt.Das Kopieren von "September" über diesen String ist ein offensichtlicher Speicherüberlauf.
Eine weitere Herausforderung ergibt sich, wenn Sie an
string
eine andere Funktion übergeben, damit die andere Funktion in das Array schreiben kann. Sie müssen der anderen Funktion mitteilen, wie lang das Array ist, damit es nicht zu einem Überlauf kommt. Sie könntenstring
das Ergebnis von weitergeben,strlen()
aber der Thread erklärt, wie dies explodieren kann, wennstring
es nicht nullterminiert ist.Sie sollten einen String mit einer festen Größe (vorzugsweise als Konstante definiert) zuweisen und dann das Array und die feste Größe an die andere Funktion übergeben. @ John Bodes Kommentar (e) sind korrekt, und es gibt Möglichkeiten, diese Risiken zu mindern. Sie erfordern auch mehr Aufwand von Ihrer Seite, um sie zu verwenden.
Nach meiner Erfahrung ist der Wert, den ich für initialisiert habe
char[]
, normalerweise zu klein für die anderen Werte, die ich dort einfügen muss. Die Verwendung einer definierten Konstante hilft, dieses Problem zu vermeiden.sizeof string
gibt Ihnen die Größe des Puffers (8 Bytes); Verwenden Sie das Ergebnis dieses Ausdrucks, anstatt sichstrlen
Gedanken über das Gedächtnis zu machen.Ebenso können Sie einen Scheck, bevor der Anruf an ,
strcpy
um zu sehen , ob Ihre Zielpuffer groß genug für die Quellzeichenfolge ist:if (sizeof target > strlen(src)) { strcpy (target, src); }
.Ja, wenn Sie das Array an eine Funktion haben, müssen Sie auch seine physische Größe weitergeben müssen:
foo (array, sizeof array / sizeof *array);
. - John Bodequelle
sizeof string
gibt Ihnen die Größe des Puffers (8 Bytes); Verwenden Sie das Ergebnis dieses Ausdrucks, anstatt sichstrlen
Gedanken über das Gedächtnis zu machen. Ebenso können Sie einen Scheck, bevor der Anruf an ,strcpy
um zu sehen , ob Ihre Zielpuffer ausreichend groß für die Quellzeichenfolge ist:if (sizeof target > strlen(src)) { strcpy (target, src); }
. Ja, wenn Sie das Array an eine Funktion haben, müssen Sie auch seine physische Größe weitergeben müssen:foo (array, sizeof array / sizeof *array);
.string
zu einer impliziten Konvertierung inchar*
, die auf das erste Element des Arrays verweist. Dadurch gehen die Informationen zu den Array-Grenzen verloren. Ein Funktionsaufruf ist nur einer von vielen Kontexten, in denen dies geschieht.char *ptr = string;
ist ein anderer. Auchstring[0]
ist ein Beispiel dafür; Der[]
Operator arbeitet mit Zeigern, nicht direkt mit Arrays. Empfohlene Lektüre: § 6 des comp.lang.c FAQ .Ich denke, die Idee der "schlechten Praxis" kommt von der Tatsache, dass diese Form:
macht implizit einen strcpy vom Quellmaschinencode zum Stack.
Es ist effizienter, nur eine Verknüpfung zu dieser Zeichenfolge zu verarbeiten. Wie mit :
oder direkt:
(aber in den meisten Codes spielt es wahrscheinlich keine Rolle)
quelle
char time_buf[] = "00:00";
dem Ändern eines Puffers? Achar *
zu einem Stringliteral initialisiert wird auf die Adresse des ersten Bytes, so versuchen , es führt zu undefiniertem Verhalten zu ändern , da die Methode der Speicher des Stringliteral ist unbekannt (Implementierung definiert), während das Bytes einer Modifizierungchar[]
ist völlig legal , weil Die Initialisierung kopiert die Bytes in einen beschreibbaren Speicherplatz auf dem Stapel. Zu sagen, dass es "weniger effizient" oder "schlechte Praxis" ist, ohne auf die Nuancen von einzugehen,char* vs char[]
ist irreführend.Nie ist wirklich viel Zeit, aber Sie sollten die Initialisierung von char [] to string vermeiden, da "string" const char * ist und Sie es char * zuweisen. Wenn Sie also dieses Zeichen [] an die Methode übergeben, die Daten ändert, kann dies zu einem interessanten Verhalten führen.
Wie gesagt, ich habe ein bisschen char [] mit char * gemischt, das ist nicht gut, da sie sich ein bisschen unterscheiden.
Es ist nichts Falsches daran, einem char-Array Daten zuzuweisen, aber da dieses Array als 'String' (char *) verwendet werden soll, kann leicht vergessen werden, dass Sie dieses Array nicht ändern sollten.
quelle
const
sei denn, Sie definieren es auf diese Weise. (Und String-Literale in C sind es nichtconst
, obwohl jeder Versuch, ein String-Literal zu ändern, ein undefiniertes Verhalten hat.)char *s = "literal";
Hat das Verhalten, von dem Sie sprechen. Es ist besser geschrieben alsconst char *s = "literal";
const
erfordernint n = 42;
, da42
es sich um eine Konstante handelt.c
änderbar ist. Es ist genau so eine Garantie wie die, die1 + 1
bewertet wird2
. Wenn das Programm, mit dem ich oben verlinkt habe, etwas anderes als Drucken ausführtEFGH
, weist dies auf eine nicht konforme C-Implementierung hin.