Wie erstelle ich ein Array von Zeichenfolgen in C?

263

Ich versuche, ein Array von Zeichenfolgen in C zu erstellen. Wenn ich diesen Code verwende:

char (*a[2])[14];
a[0]="blah";
a[1]="hmm";

gcc gibt mir "Warnung: Zuweisung vom inkompatiblen Zeigertyp". Was ist der richtige Weg, um dies zu tun?

edit: Ich bin gespannt, warum dies eine Compiler-Warnung geben sollte, da in printf(a[1]);diesem Fall "hmm" korrekt gedruckt wird.


quelle
12
Nur für die Aufzeichnung char (*a[2])[14]ist ein Array von zwei Zeigern auf ein Array von 14 Zeichen.
Avakar
4
Ich dachte, es
wären
74
Der nützlichste Rat, den ich je zum Entschlüsseln von C-Typen gelesen habe: "Beginnen Sie mit dem Namen, lesen Sie rechts, wenn Sie können, links, wenn Sie müssen": char (*a[2])[14]- beginnen Sie a, bewegen Sie sich nach rechts: "Array of Two", bewegen Sie sich nach links: "Zeiger auf", Klammer komplett, also lesen Sie rechts: "Array of Forteen", lesen Sie links: "Char" ... Setzen Sie es zusammen und wir haben "ein Array von zwei Zeigern auf Arrays von Forteen Chars"
Mark K Cowan
4
@dotancohen: Dieser Tipp hat mich schließlich überzeugt, Zeiger als char *strstatt zu schreiben char* str. Ich stamme aus Delphi / Pascal und war sehr an den letzteren Weg gewöhnt, bis ich auf komplexere Typen stieß. Der frühere Weg sieht für mich immer noch hässlich aus, macht aber die Typennotation konsistenter (IMO).
Mark K Cowan
@ MarkKCowan, erstaunlich! Vielen Dank! :)
Dr. Essen

Antworten:

232

Wenn Sie die Zeichenfolgen nicht ändern möchten, können Sie dies einfach tun

const char *a[2];
a[0] = "blah";
a[1] = "hmm";

Wenn Sie dies so tun, weisen Sie ein Array mit zwei Zeigern zu const char. Diese Zeiger werden dann auf die Adressen der statischen Zeichenfolgen "blah"und gesetzt "hmm".

Wenn Sie in der Lage sein möchten, den tatsächlichen Inhalt der Zeichenfolge zu ändern, müssen Sie so etwas tun

char a[2][14];
strcpy(a[0], "blah");
strcpy(a[1], "hmm");

Dadurch werden zwei aufeinanderfolgende Arrays von jeweils 14 chars zugewiesen , wonach der Inhalt der statischen Zeichenfolgen in diese kopiert wird.

Mikael Auno
quelle
185

Es gibt verschiedene Möglichkeiten, ein Array von Zeichenfolgen in C zu erstellen. Wenn alle Zeichenfolgen dieselbe Länge (oder zumindest dieselbe maximale Länge) haben sollen, deklarieren Sie einfach ein 2D-Array von Zeichen und weisen es nach Bedarf zu:

char strs[NUMBER_OF_STRINGS][STRING_LENGTH+1];
...
strcpy(strs[0], aString); // where aString is either an array or pointer to char
strcpy(strs[1], "foo");

Sie können auch eine Liste von Initialisierern hinzufügen:

char strs[NUMBER_OF_STRINGS][STRING_LENGTH+1] = {"foo", "bar", "bletch", ...};

Dies setzt voraus, dass die Größe und Anzahl der Zeichenfolgen im Initialisierer mit Ihren Array-Abmessungen übereinstimmen. In diesem Fall wird der Inhalt jedes Zeichenfolgenliteral (das selbst ein nullterminiertes Array von Zeichen ist) in den Speicher kopiert, der strs zugewiesen ist. Das Problem bei diesem Ansatz ist die Möglichkeit einer internen Fragmentierung; Wenn Sie 99 Zeichenfolgen mit maximal 5 Zeichen, aber 1 Zeichenfolge mit 20 Zeichen haben, enthalten 99 Zeichenfolgen mindestens 15 nicht verwendete Zeichen. Das ist Platzverschwendung.

Anstatt ein 2D-Array von Zeichen zu verwenden, können Sie ein 2D-Array von Zeigern auf Zeichen speichern:

char *strs[NUMBER_OF_STRINGS];

Beachten Sie, dass Sie in diesem Fall nur Speicher zugewiesen haben, um die Zeiger auf die Zeichenfolgen zu halten. Der Speicher für die Zeichenfolgen selbst muss an anderer Stelle zugewiesen werden (entweder als statische Arrays oder mithilfe von malloc()oder calloc()). Sie können die Initialisierungsliste wie im vorherigen Beispiel verwenden:

char *strs[NUMBER_OF_STRINGS] = {"foo", "bar", "bletch", ...};

Anstatt den Inhalt der Zeichenfolgenkonstanten zu kopieren, speichern Sie einfach die Zeiger darauf. Beachten Sie, dass Zeichenfolgenkonstanten möglicherweise nicht beschreibbar sind. Sie können den Zeiger wie folgt neu zuweisen:

strs[i] = "bar";
strs[i] = "foo"; 

Möglicherweise können Sie den Inhalt der Zeichenfolge jedoch nicht ändern. dh

strs[i] = "bar";
strcpy(strs[i], "foo");

darf nicht erlaubt sein.

Sie können malloc()den Puffer für jede Zeichenfolge dynamisch zuweisen und in diesen Puffer kopieren:

strs[i] = malloc(strlen("foo") + 1);
strcpy(strs[i], "foo");

Übrigens,

char (*a[2])[14];

Deklariert a als 2-Element-Array von Zeigern auf 14-Element-Arrays von char.

John Bode
quelle
3
@ Slater: Ja, wenn es das Ergebnis eines mallocAnrufs ist.
John Bode
Vielen Dank für diese sehr detaillierte Antwort. Das hat mir wirklich geholfen.
Kokedude
1
Warum können wir strcpy nur für String-Arrays verwenden, die als 2D-Array deklariert sind? Warum schlägt die Standardzuweisung fehl?
Andrew S
4
@ Andrews: Die vollständige Antwort passt nicht in einen Kommentar, aber im Grunde ist es ein Artefakt, wie C Array-Ausdrücke behandelt. In den meisten Fällen wird ein Ausdruck vom Typ T [N]in einen Ausdruck vom Typ konvertiert T *, und der Wert des Ausdrucks ist die Adresse des ersten Elements. Wenn Sie also geschrieben haben str = "foo", würden Sie versuchen "foo", dem Array die Adresse des ersten Zeichens zuzuweisen str, was nicht funktioniert. Siehe diese Antwort für weitere Details.
John Bode
@ JohnBode könntest du bitte den kleinen Tweak hinzufügen? char *strs[NUMBER_OF_STRINGS] = {0}; Dies hilft, zukünftige Probleme durch Initialisierung strsauf zu vermeiden NULL. Viele Leute lesen diesen Beitrag, wenn die Google-Suche nach einer Reihe von Zeichenfolgen in C.
Kokedude
94

Bestätigen Sie! Konstante Zeichenfolgen:

const char *strings[] = {"one","two","three"};

Wenn ich mich richtig erinnere.

Oh, und Sie möchten strcpy für die Zuweisung verwenden, nicht den Operator =. strcpy_s ist sicherer, aber weder in C89- noch in C99-Standards.

char arr[MAX_NUMBER_STRINGS][MAX_STRING_SIZE]; 
strcpy(arr[0], "blah");

Update: Thomas sagt, es strlcpyist der richtige Weg.

mpen
quelle
Ist das C99? Ich glaube nicht, dass es in ANSI C möglich ist.
Noldorin
6
Es ist sowohl in C89 als auch in C99 möglich. Es spielt auch keine Rolle, ob es mit const oder ohne ist, obwohl das erstere bevorzugt wird.
Avakar
1
Nun, const ist neu, und Sie mussten früher die Größe des äußeren Arrays angeben (in diesem Fall 3), aber ansonsten ist dies durchaus akzeptabel. K & R C. Ich habe ein altes C-Buch, das 1984 urheberrechtlich geschützt ist und einen Abschnitt enthält, der zeigt, wie es geht mach das. Sie nennen es ein "zerlumptes Array". Natürlich hatte es keine "Operatoren" und strcpy_s ist neu bei mir.
TED
6
strcpy_s ist eine Microsoft-Funktion. Es sollte wahrscheinlich vermieden werden, weil es nicht in Standard C ist.
Cromulent
5
strcpy_s und andere "sichere Funktionen" sind als ISO / IEC TR 24731 standardisiert (es handelt sich um eine von ISO veröffentlichte Norm und ist als solche nicht kostenlos online verfügbar; der neueste Entwurf ist open-std.org/jtc1/sc22/wg14/www /docs/n1225.pdf )
Pavel
14

Hier sind einige Ihrer Optionen:

char a1[][14] = { "blah", "hmm" };
char* a2[] = { "blah", "hmm" };
char (*a3[])[] = { &"blah", &"hmm" };  // only since you brought up the syntax -

printf(a1[0]); // prints blah
printf(a2[0]); // prints blah
printf(*a3[0]); // prints blah

Der Vorteil von a2ist, dass Sie dann mit String-Literalen Folgendes tun können

a2[0] = "hmm";
a2[1] = "blah";

Und für a3Sie können Sie Folgendes tun:

a3[0] = &"hmm";
a3[1] = &"blah";

Denn a1Sie müssen strcpy()(noch besser strncpy()) auch beim Zuweisen von String-Literalen verwenden. Der Grund dafür ist, dass a2und a3sind Arrays von Zeigern, und Sie können ihre Elemente (dh Zeiger) auf einen beliebigen Speicher verweisen lassen, während a1es sich um ein Array von 'Array von Zeichen' handelt und daher jedes Element ein Array ist, das seinen eigenen Speicher "besitzt" ( was bedeutet, dass es zerstört wird, wenn es außerhalb des Gültigkeitsbereichs liegt. Sie können nur Inhalte in den Speicher kopieren.

Dies bringt uns auch zum Nachteil der Verwendung von a2und a3- da sie auf statische Speicherung verweisen (in der Zeichenfolgenliterale gespeichert sind), deren Inhalt nicht zuverlässig geändert werden kann (dh undefiniertes Verhalten), wenn Sie der Liste Nicht-Zeichenfolgenliterale zuweisen möchten Elemente von a2oder a3- Sie müssen zuerst dynamisch genügend Speicher zuweisen und dann ihre Elemente auf diesen Speicher verweisen lassen und dann die Zeichen in diesen Speicher kopieren - und dann müssen Sie sicherstellen, dass Sie den Speicher freigeben, wenn Sie fertig sind.

Bah - ich vermisse C ++ schon;)

ps Lassen Sie mich wissen, wenn Sie Beispiele benötigen.

Faisal Vali
quelle
Ich brauchte String-Arrays für ein Arduino-Projekt. Am Ende habe ich den a2-Stil verwendet. Ich habe zunächst den a1-Stil ausprobiert, der mein String-Array als char a1 [] [2] = {"F3", "G3" ... usw. Definiert. }, da 2 Zeichen lange Zeichenfolgen gespeichert werden sollten. Dies ergab eine unerwartete Ausgabe, da ich vergessen habe, dass der Nullterminator bedeuten würde, dass jede Zeichenfolge eine Größe von mindestens 3 haben sollte, um die 2 Zeichen zu speichern. Bei Verwendung des a2-Stils musste ich die Länge der Saite nicht angeben, und es konnten auch unterschiedliche Saitenlängen
berücksichtigt werden,
char (* a3 []) [] = {& "blah", & "hmm"}; => funktioniert nicht in g ++ Apple LLVM Version 9.1.0, aber es funktioniert in gcc
1234
12

Oder Sie können einen Strukturtyp deklarieren, der eine Zeichen-Arry (1 Zeichenfolge) enthält. Sie erstellen ein Array der Strukturen und damit ein Array mit mehreren Elementen

typedef struct name
{
   char name[100]; // 100 character array
}name;

main()
{
   name yourString[10]; // 10 strings
   printf("Enter something\n:);
   scanf("%s",yourString[0].name);
   scanf("%s",yourString[1].name);
   // maybe put a for loop and a few print ststements to simplify code
   // this is just for example 
 }

Einer der Vorteile gegenüber jeder anderen Methode besteht darin, dass Sie direkt in die Zeichenfolge scannen können, ohne sie verwenden zu müssen strcpy.

FutureSci
quelle
10

In ANSI C:

char* strings[3];
strings[0] = "foo";
strings[1] = "bar";
strings[2] = "baz";
Noldorin
quelle
8
@ Zifre: Ich bin völlig anderer Meinung. Es ist sehr viel Teil des Typs - in diesem Fall ein "Zeichenzeiger". Was würden Sie überhaupt sagen ... es ist Teil des Variablennamens? Ich habe viele kompetente Programmierer gesehen, die diesen Stil verwendet haben.
Noldorin
14
Nur für alle anderen, die dies lesen, möchte ich darauf hinweisen, dass Bjarne Stroustrup das * nach Typ setzt ...
MirroredFate
1
@MirroredFate: Richtig. In der Tat wird empfohlen, in C ++ zu üben, soweit ich weiß. Semantisch macht es für mich keinen Sinn, es durch die Kennung zu setzen, weil es so verwendet wird. : /
Noldorin
16
@Noldorin char* foo, bar;was ist die Art von bar?
Masough
10
C wurde 1972 von Dennis Ritchie entwickelt und 1988 veröffentlichten er und Brian Kernighan die zweite Ausgabe von K & R - The C Programming Language, einem Buch, das viele als De-facto-Standard für C halten. Sie setzen das * durch die Kennung.
Marius Lian
10

Wenn die Zeichenfolgen statisch sind, sollten Sie Folgendes tun:

const char *my_array[] = {"eenie","meenie","miney"};

Obwohl dies nicht Teil von ANSI C ist, unterstützt Ihre Umgebung wahrscheinlich die Syntax. Diese Zeichenfolgen sind unveränderlich (schreibgeschützt) und benötigen daher in vielen Umgebungen weniger Overhead als das dynamische Erstellen eines Zeichenfolgenarrays.

Beispielsweise verwendet diese Syntax in kleinen Mikrocontroller-Projekten eher Programmspeicher als (normalerweise) wertvolleren RAM-Speicher. AVR-C ist eine Beispielumgebung, die diese Syntax unterstützt, aber die meisten anderen auch.

Bryce
quelle
10

Wenn Sie die Anzahl der Zeichenfolgen im Array nicht verfolgen und diese durchlaufen möchten, fügen Sie am Ende einfach die NULL-Zeichenfolge hinzu:

char *strings[]={ "one", "two", "three", NULL };

int i=0;
while(strings[i]) {
  printf("%s\n", strings[i]);
  //do something
  i++;
};
Sergey
quelle
Ich glaube, dass dies nur in C ++ gültig ist. In C ist nicht garantiert, dass NULL Null ist, daher kann die Schleife nicht unterbrochen werden, wenn dies der Fall sein sollte. Korrigieren Sie mich, wenn ich falsch liege.
Palec
2
Keine Ahnung :) Sie können mit NULL in while-Anweisung vergleichen, wenn Sie möchten.
Sergey
9

Die String-Literale sind const char *s.

Und Ihre Verwendung von Klammern ist seltsam. Du meinst wahrscheinlich

const char *a[2] = {"blah", "hmm"};

Dies deklariert ein Array von zwei Zeigern auf konstante Zeichen und initialisiert sie so, dass sie auf zwei fest codierte Zeichenfolgenkonstanten zeigen.

dmckee --- Ex-Moderator Kätzchen
quelle
3

Ihr Code erstellt ein Array von Funktionszeigern. Versuchen

char* a[size];

oder

char a[size1][size2];

stattdessen.

Siehe Wikibooks zu Arrays und Zeigern

Dario
quelle
1
Hut ab für Ihren anderen Ansatz ... Leute wie Sie machen Stapel zum Überlaufen ...
Sahu V Kumar
1

Hallo, du kannst diesen Balg probieren:

 char arr[nb_of_string][max_string_length]; 
 strcpy(arr[0], "word");

Ein schönes Beispiel für die Verwendung eines Arrays von Zeichenfolgen in c, wenn Sie es möchten

#include <stdio.h>
#include <string.h>


int main(int argc, char *argv[]){

int i, j, k;

// to set you array
//const arr[nb_of_string][max_string_length]
char array[3][100];

char temp[100];
char word[100];

for (i = 0; i < 3; i++){
    printf("type word %d : ",i+1);
    scanf("%s", word);
    strcpy(array[i], word);
}

for (k=0; k<3-1; k++){
    for (i=0; i<3-1; i++)
    {
        for (j=0; j<strlen(array[i]); j++)
        {
            // if a letter ascii code is bigger we swap values
            if (array[i][j] > array[i+1][j])
            {
                strcpy(temp, array[i+1]);
                strcpy(array[i+1], array[i]);
                strcpy(array[i], temp);

                j = 999;
            }

            // if a letter ascii code is smaller we stop
            if (array[i][j] < array[i+1][j])
            {
                    j = 999;
            }

        }
    }
}

for (i=0; i<3; i++)
{
    printf("%s\n",array[i]);
}

return 0;
}
Aominé
quelle
0
char name[10][10]
int i,j,n;//here "n" is number of enteries
printf("\nEnter size of array = ");
scanf("%d",&n);
for(i=0;i<n;i++)
{
    for(j=0;j<1;j++)
    {
        printf("\nEnter name = ");
        scanf("%s",&name[i]);
    }
}
//printing the data
for(i=0;i<n;i++)
{
    for(j=0;j<1;j++)
    {
        printf("%d\t|\t%s\t|\t%s",rollno[i][j],name[i],sex[i]);
    }
    printf("\n");
}

Hier versuchen Sie das !!!

Aditya
quelle
1
Können Sie erklären, warum Sie die for-Schleife mit der Variablen j benötigen, dh für (j = 0; j <1; j ++)?
SouvikMaji
0

Mir fehlte irgendwie ein dynamischeres Array von Strings, bei dem die Anzahl der Strings je nach Laufzeitauswahl variiert werden konnte, aber ansonsten sollten Strings korrigiert werden.

Ich habe am Ende Code-Snippet wie folgt codiert:

#define INIT_STRING_ARRAY(...)          \
    {                                   \
        char* args[] = __VA_ARGS__;     \
        ev = args;                      \
        count = _countof(args);         \
    }

void InitEnumIfAny(String& key, CMFCPropertyGridProperty* item)
{
    USES_CONVERSION;
    char** ev = nullptr;
    int count = 0;

    if( key.Compare("horizontal_alignment") )
        INIT_STRING_ARRAY( { "top", "bottom" } )

    if (key.Compare("boolean"))
        INIT_STRING_ARRAY( { "yes", "no" } )

    if( ev == nullptr )
        return;

    for( int i = 0; i < count; i++)
        item->AddOption(A2T(ev[i]));

    item->AllowEdit(FALSE);
}

char** evNimmt den Zeiger auf Array-Zeichenfolgen auf und zählt die Anzahl der Zeichenfolgen mithilfe der _countofFunktion. (Ähnlich wie sizeof(arr) / sizeof(arr[0])).

Und es gibt zusätzliche Konvertierung von Ansi in Unicode mithilfe eines A2TMakros, aber das kann für Ihren Fall optional sein.

TarmoPikaro
quelle
-6

Ein guter Weg ist es, einen String selbst zu definieren.

#include <stdio.h>
typedef char string[]
int main() {
    string test = "string";
    return 0;
}

So einfach ist das wirklich.

IceCodr
quelle
4
Ihnen fehlt ein ;, und wie wird dadurch ein Array von Zeichenfolgen erstellt ?
Keyser