it-swarm.com.de

Ist das Initialisieren eines Zeichens [] mit einem String-Literal eine schlechte Praxis?

Ich habe einen Thread mit dem Titel "strlen vs sizeof" auf CodeGur gelesen und eine der Antworten besagt, dass "es sowieso eine schlechte Praxis ist, einen char zu initialisieren [sic] Array mit einem String-Literal. "

Ist das wahr oder ist das nur seine Meinung (wenn auch ein "Elite-Mitglied")?


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;
}

recht. die größe sollte die länge plus 1 sein ja?

dies ist die Ausgabe

the size of september is 8 and the length is 9

größe sollte sicher 10 sein. Es ist so, als würde man die Größe des Strings berechnen, bevor er durch 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 char-Array mit einem String-Literal zu initialisieren. Führen Sie also immer einen der folgenden Schritte aus:

const char string1[] = "october";
char string2[20]; strcpy(string2, "september");
46
Cole Johnson

Es ist sowieso eine schlechte Praxis, ein char-Array mit einem String-Literal zu initialisieren.

Der Autor dieses Kommentars rechtfertigt es nie wirklich, und ich finde die Aussage rätselhaft.

In C (und Sie haben dies als C markiert) ist dies so ziemlich die einzige Möglichkeit, ein Array von char mit a zu initialisieren Zeichenfolgenwert (Initialisierung unterscheidet sich von Zuweisung). Sie können entweder schreiben

char string[] = "october";

oder

char string[8] = "october";

oder

char string[MAX_MONTH_LENGTH] = "october";

Im ersten Fall wird die Größe des Arrays von der Größe des Initialisierers abgeleitet. String-Literale werden als Arrays von char mit einem abschließenden 0-Byte gespeichert, sodass die Größe des Arrays 8 beträgt ('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 und MAX_MONTH_LENGTH, Was auch immer das sein mag).

Was Sie nicht tun können, ist so etwas zu schreiben

char string[];
string = "october";

oder

char string[8];
string = "october";

usw. Im ersten Fall ist die Deklaration von string unvollständig , da keine Arraygröße angegeben wurde und kein Initialisierer vorhanden ist Größe von. In beiden Fällen funktioniert = Nicht, da a) ein Array-Ausdruck wie string möglicherweise nicht das Ziel einer Zuweisung ist und b) der Operator = Nicht 'ist. t definiert, um den Inhalt eines Arrays trotzdem in ein anderes zu kopieren.

Aus dem gleichen Grund können Sie nicht schreiben

char string[] = foo;

dabei ist foo ein weiteres Array von char. Diese Form der Initialisierung funktioniert nur mit Zeichenfolgenliteralen.

EDIT

Ich sollte dies ändern, um zu sagen, dass Sie Arrays auch initialisieren können, um einen String mit einem Initialisierer im Array-Stil zu halten, wie z

char string[] = {'o', 'c', 't', 'o', 'b', 'e', 'r', 0};

oder

char string[] = {111, 99, 116, 111, 98, 101, 114, 0}; // assumes ASCII

aber es ist für die Augen einfacher, String-Literale zu verwenden.

BEARBEITEN2

Um den Inhalt eines Arrays außerhalb einer Deklaration zuzuweisen, müssten Sie entweder strcpy/strncpy (Für 0-terminierte Zeichenfolgen) verwenden ) oder memcpy (für jeden anderen Array-Typ):

if (sizeof string > strlen("october"))
  strcpy(string, "october");

oder

strncpy(string, "october", sizeof string); // only copies as many characters as will
                                           // fit in the target buffer; 0 terminator
                                           // may not be copied, but the buffer is
                                           // uselessly completely zeroed if the
                                           // string is shorter!
62
John Bode

Das einzige Problem, an das ich mich erinnere, ist die Zuweisung eines Zeichenfolgenliteral zu char *:

char var1[] = "september";
var1[0] = 'S'; // Ok - 10 element char array allocated on stack
char const *var2 = "september";
var2[0] = 'S'; // Compile time error - pointer to constant string
char *var3 = "september";
var3[0] = 'S'; // Modifying some memory - which may result in modifying... something or crash

Nehmen Sie zum Beispiel dieses Programm:

#include <stdio.h>

int main() {
  char *var1 = "september";
  char *var2 = "september";
  var1[0] = 'S';
  printf("%s\n", var2);
}

Dies auf meiner Plattform (Linux) stürzt ab, wenn versucht wird, auf eine als schreibgeschützt gekennzeichnete Seite zu schreiben. Auf anderen Plattformen wird möglicherweise "September" usw. gedruckt.

Das heißt - die Initialisierung durch Literal macht den spezifischen Betrag der Reservierung so, dass dies nicht funktioniert:

char buf[] = "May";
strncpy(buf, "September", sizeof(buf)); // Result "Sep"

Aber das wird

char buf[32] = "May";
strncpy(buf, "September", sizeof(buf));

Als letzte Bemerkung - ich würde strcpy überhaupt nicht verwenden:

char buf[8];
strcpy(buf, "very long string very long string"); // Oops. We overwrite some random memory

Während einige Compiler es in einen sicheren Aufruf ändern können, ist strncpy viel sicherer:

char buf[1024];
strncpy(buf, something_else, sizeof(buf)); // Copies at most sizeof(buf) chars so there is no possibility of buffer overrun. Please note that sizeof(buf) works for arrays but NOT pointers.
buf[sizeof(buf) - 1] = '\0';
11

In erster Linie, weil Sie nicht die Größe von char[] In einer Variablen/einem Konstrukt haben, die Sie problemlos im Programm verwenden können.

Das Codebeispiel aus dem Link:

 char string[] = "october";
 strcpy(string, "september");

string wird auf dem Stapel als 7 oder 8 Zeichen lang zugewiesen. Ich kann mich nicht erinnern, ob es auf diese Weise nullterminiert ist oder nicht - der Thread, mit dem Sie verlinkt haben, gab an, dass dies der Fall ist.

Das Kopieren von "September" über diese Zeichenfolge ist eine offensichtliche Speicherüberschreitung.

Eine weitere Herausforderung ergibt sich, wenn Sie string an 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 keinen Überlauf erzeugt. Sie könnten string zusammen mit dem Ergebnis von strlen() übergeben, aber der Thread erklärt, wie dies explodieren kann, wenn string nicht nullterminiert ist.

Es ist besser, einen String mit einer festen Größe (vorzugsweise als Konstante definiert) zuzuweisen und dann das Array und die feste Größe an die andere Funktion zu übergeben. Die Kommentare von @John Bode 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, mit dem ich char[] Initialisiert habe, 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 die Größe des Puffers an (8 Byte). Verwenden Sie das Ergebnis dieses Ausdrucks anstelle von strlen, wenn Sie sich Gedanken über den Speicher machen.
Ebenso können Sie vor dem Aufruf von strcpy überprüfen, ob Ihr Zielpuffer groß genug für die Quellzeichenfolge ist: if (sizeof target > strlen(src)) { strcpy (target, src); }.
Ja, wenn Sie das Array an eine Funktion übergeben müssen, müssen Sie auch seine physische Größe übergeben: foo (array, sizeof array / sizeof *array);. - John Bode

6
user53019

Eine Sache, die keiner der Threads anspricht, ist folgende:

char whopping_great[8192] = "foo";

vs.

char whopping_great[8192];
memcpy(whopping_great, "foo", sizeof("foo"));

Ersteres wird so etwas tun wie:

memcpy(whopping_great, "foo", sizeof("foo"));
memset(&whopping_great[sizeof("foo")], 0, sizeof(whopping_great)-sizeof("foo"));

Letzteres macht nur das memcpy. Der C-Standard besteht darauf, dass alles initialisiert wird, wenn ein Teil eines Arrays initialisiert wird. In diesem Fall ist es besser, es selbst zu tun. Ich denke, das war vielleicht das, worauf Treuss hinaus wollte.

Sicher

char whopping_big[8192];
whopping_big[0] = 0;

ist besser als beides:

char whopping_big[8192] = {0};

oder

char whopping_big[8192] = "";

p.s. Für Bonuspunkte können Sie Folgendes tun:

memcpy(whopping_great, "foo", (1/(sizeof("foo") <= sizeof(whopping_great)))*sizeof("foo"));

wenn Sie das Array überlaufen möchten, wird eine Kompilierungszeit durch den Fehler Null geteilt.

6
Richard Fife

Ich denke, die Idee der "schlechten Praxis" kommt von der Tatsache, dass diese Form:

char string[] = "october is a Nice month";

macht implizit einen Strcpy vom Quellcomputercode zum Stack.

Es ist effizienter, nur einen Link zu dieser Zeichenfolge zu verarbeiten. Wie mit :

char *string = "october is a Nice month";

oder direkt:

strcpy(output, "october is a Nice month");

(aber natürlich spielt es in den meisten Codes wahrscheinlich keine Rolle)

2
toto