Come posso creare una matrice di stringhe in C?


263

Sto cercando di creare una matrice di stringhe in C. Se utilizzo questo codice:

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

gcc mi dà "avvertimento: assegnazione da un tipo di puntatore incompatibile". Qual è il modo corretto per farlo?

modifica: Sono curioso di sapere perché questo dovrebbe dare un avvertimento al compilatore poiché se lo faccio printf(a[1]);, stampa correttamente "hmm".

c  arrays  string 

12
Solo per la cronaca, char (*a[2])[14]è un array di due puntatori a un array di 14 caratteri.
avakar,

4
Ho pensato che fossero quattordici i puntatori alle matrici di due caratteri xD
fortran,

74
Il consiglio più utile che io abbia mai letto per decifrare i tipi C: "Inizia dal nome, leggi a destra quando puoi, a sinistra quando devi": char (*a[2])[14]- inizia a a, sposta a destra: "array di due", sposta a sinistra: "puntatore a", parentesi completa quindi leggi a destra: "array of forteen", leggi a sinistra: "char" ... Metti insieme e abbiamo "a è una matrice di due puntatori ad array di caratteri forteen"
Mark K Cowan

4
@dotancohen: quel suggerimento è ciò che alla fine mi ha convinto a scrivere i puntatori char *strpiuttosto che char* str. Provenendo da un background Delphi / Pascal, ero molto abituato a quest'ultima strada fino a quando non ho incontrato tipi più complessi. Il primo modo mi sembra ancora brutto, ma rende la notazione del tipo più coerente (IMO).
Mark K Cowan,

@MarkKCowan, fantastico! Grazie! :)
Dr. Essen,

Risposte:


232

Se non vuoi cambiare le stringhe, allora puoi semplicemente farlo

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

Quando lo fai in questo modo assegnerai una matrice di due puntatori a const char. Questi puntatori verranno quindi impostati sugli indirizzi delle stringhe statiche "blah"e "hmm".

Se vuoi essere in grado di cambiare il contenuto della stringa attuale, devi fare qualcosa del genere

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

Questo assegnerà due array consecutivi di 14 chars ciascuno, dopodiché il contenuto delle stringhe statiche verrà copiato in essi.


185

Esistono diversi modi per creare una matrice di stringhe in C. Se tutte le stringhe avranno la stessa lunghezza (o almeno avranno la stessa lunghezza massima), è sufficiente dichiarare una matrice 2-d di carattere e assegnare come necessario:

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");

Puoi anche aggiungere un elenco di inizializzatori:

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

Ciò presuppone che le dimensioni e il numero di stringhe nell'inizializzatore corrispondano alle dimensioni dell'array. In questo caso, il contenuto di ciascuna stringa letterale (che è essa stessa una matrice di caratteri con terminazione zero) viene copiato nella memoria allocata su strs. Il problema con questo approccio è la possibilità di frammentazione interna; se hai 99 stringhe di 5 caratteri o meno, ma 1 stringa di 20 caratteri, 99 stringhe avranno almeno 15 caratteri inutilizzati; è uno spreco di spazio.

Invece di utilizzare un array 2-d di char, è possibile memorizzare un array 1-d di puntatori su char:

char *strs[NUMBER_OF_STRINGS];

Si noti che in questo caso, è stata allocata solo memoria per contenere i puntatori alle stringhe; la memoria per le stringhe stesse deve essere allocata altrove (o come array statici o usando malloc()o calloc()). È possibile utilizzare l'elenco di inizializzatori come nell'esempio precedente:

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

Invece di copiare il contenuto delle costanti di stringa, è sufficiente memorizzare i puntatori su di esse. Si noti che le costanti di stringa potrebbero non essere scrivibili; puoi riassegnare il puntatore, in questo modo:

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

Ma potresti non essere in grado di cambiare il contenuto della stringa; vale a dire,

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

potrebbe non essere permesso.

È possibile utilizzare malloc()per allocare dinamicamente il buffer per ogni stringa e copiarlo nel buffer:

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

BTW,

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

Dichiara a come una matrice di 2 elementi di puntatori a matrici di 14 elementi di char.


3
@Slater: sì, se è il risultato di una mallocchiamata.
John Bode,

Grazie per questa risposta molto dettagliata. Questo mi ha davvero aiutato.
cokedude,

1
Perché possiamo usare strcpy solo su array String dichiarati come array 2D. Perché l'assegnazione standard non riesce?
Andrew S,

4
@AndrewS: la risposta completa non si adatta a un commento, ma fondamentalmente è un artefatto di come C tratta le espressioni di array; nella maggior parte dei casi, un'espressione di tipo T [N]viene convertita in un'espressione di tipo T *e il valore dell'espressione è l'indirizzo del primo elemento. Quindi, se scrivessi str = "foo", proveresti ad assegnare l'indirizzo del primo carattere "foo"dell'array str, che non funziona. Vedi questa risposta per maggiori dettagli.
John Bode,

@JohnBode potresti per favore aggiungere la piccola modifica? char *strs[NUMBER_OF_STRINGS] = {0}; Questo aiuta a prevenire problemi futuri inizializzando strsa NULL. Molte persone leggono questo post quando il do google cerca su una serie di stringhe in C.
cokedude

94

Ack! Stringhe costanti:

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

Se ricordo bene.

Oh, e vuoi usare strcpy per l'assegnazione, non l'operatore =. strcpy_s è più sicuro, ma non è né in C89 né negli standard C99.

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

Aggiornamento: Thomas dice che strlcpyè la strada da percorrere.


Quella è C99? Non credo sia possibile in ANSI C.
Noldorin,

6
È possibile sia in C89 che in C99. Inoltre, non importa se è con const o senza di essa, anche se il primo è preferito.
avakar,

1
Bene, const è nuovo e in passato era necessario specificare la dimensione dell'array esterno (3 in questo caso), ma per il resto questo è perfettamente accettabile K&R C. Ho un vecchio libro C protetto da copyright 1984 che ha una sezione che mostra come Fai questo. Lo chiamano un "array sfilacciato". Ovviamente non aveva "operatori" e strcpy_s è nuovo per me.
TED,

6
strcpy_s è una funzione di Microsoft. Probabilmente dovrebbe essere evitato perché non è nello standard C.
Cromulent,

5
strcpy_s e altre "funzioni sicure" sono standardizzate come ISO / IEC TR 24731 (è uno standard pubblicato ISO e come tale non è disponibile online gratuitamente; la bozza più recente è open-std.org/jtc1/sc22/wg14/www /docs/n1225.pdf )
Pavel Minaev,

14

Ecco alcune delle tue opzioni:

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

Il vantaggio a2è che puoi quindi fare quanto segue con valori letterali stringa

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

E per a3te puoi fare quanto segue:

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

Perché a1dovrai usare strcpy()(ancora meglio strncpy()) anche quando assegni valori letterali a stringa. La ragione è che a2, e a3sono matrici di puntatori e puoi far sì che i loro elementi (cioè puntatori) puntino a qualsiasi memoria, mentre a1è una matrice di "array di caratteri" e quindi ogni elemento è un array che "possiede" il proprio archivio ( il che significa che viene distrutto quando esce dal campo di applicazione) - puoi solo copiare materiale nella sua memoria.

Questo ci porta anche allo svantaggio dell'uso a2e a3- poiché indicano l'archiviazione statica (dove sono archiviati valori letterali stringa) il cui contenuto non può essere modificato in modo affidabile (vale a dire comportamento indefinito), se si desidera assegnare valori letterali non stringa al elementi di a2o a3- dovrete prima allocare dinamicamente memoria sufficiente e quindi far sì che i loro elementi puntino a questa memoria e quindi copiare i caratteri al suo interno - e quindi dovrete essere sicuri di deallocare la memoria al termine.

Bah - Mi manca già C ++;)

ps Fammi sapere se hai bisogno di esempi.


Avevo bisogno di array di stringhe per un progetto Arduino. Alla fine ho usato lo stile a2. Inizialmente ho provato lo stile a1 definendo il mio array di stringhe come char a1 [] [2] = {"F3", "G3" ... ecc. } in quanto era destinato a memorizzare stringhe lunghe di 2 caratteri. Questo ha dato un output inaspettato perché ho dimenticato che il terminatore null significherebbe che ogni stringa dovrebbe avere una dimensione di almeno 3 per memorizzare i 2 caratteri. Usando lo stile a2, non avevo bisogno di specificare la lunghezza della stringa, e poteva adattarmi anche a lunghezze di stringa variabili, quindi ho deciso di
attenermi

char (* a3 []) [] = {& "blah", & "hmm"}; => non funziona in g ++ Apple LLVM versione 9.1.0, ma funziona in gcc
1234

12

Oppure puoi dichiarare un tipo di struttura, che contiene un carattere arry (1 stringa), creano una matrice delle strutture e quindi una matrice multi-elemento

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 
 }

Uno dei vantaggi di questo rispetto a qualsiasi altro metodo è che questo ti permette di scansionare direttamente nella stringa senza dover usare strcpy;


10

In ANSI C:

char* strings[3];
strings[0] = "foo";
strings[1] = "bar";
strings[2] = "baz";

8
@Zifre: non sono completamente d'accordo. Fa molto parte del tipo - in questo caso un "puntatore a caratteri". Cosa diresti comunque ... fa parte del nome della variabile? Ho visto molti programmatori competenti usare questo stile.
Noldorin,

14
Solo per chiunque legga questo, vorrei sottolineare che Bjarne Stroustrup mette il * per tipo ...
MirroredFate

1
@MirroredFate: Corretto. In effetti, è una pratica raccomandata in C ++ da quello che so. Semanticamente non ha senso per me metterlo dall'identificatore, a causa del modo in cui viene utilizzato. : /
Noldorin

16
@Noldorin di char* foo, bar;che tipo è bar?
mASOUD,

10
C è stato sviluppato da Dennis Ritchie nel 1972 e nel 1988 lui e Brian Kernighan hanno pubblicato la seconda edizione di K&R - The C Programming Language, un libro che molti considerano di fatto standard per C. Hanno messo il * con l'identificatore.
Marius Lian,

10

Se le stringhe sono statiche, è meglio con:

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

Sebbene non faccia parte dell'ANSI C di base, è probabile che il tuo ambiente supporti la sintassi. Queste stringhe sono immutabili (sola lettura) e quindi in molti ambienti utilizzano un sovraccarico minore rispetto alla creazione dinamica di un array di stringhe.

Ad esempio in piccoli progetti di microcontrollori, questa sintassi utilizza la memoria del programma anziché (solitamente) una memoria RAM più preziosa. AVR-C è un ambiente di esempio che supporta questa sintassi, ma lo stesso vale per la maggior parte degli altri.


10

Se non si desidera tenere traccia del numero di stringhe nell'array e si desidera iterare su di esse, è sufficiente aggiungere la stringa NULL alla fine:

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

int i=0;
while(strings[i]) {
  printf("%s\n", strings[i]);
  //do something
  i++;
};

Credo che questo sia valido solo in C ++. In C, NULL non è garantito per essere zero, quindi il loop potrebbe non interrompersi quando dovrebbe. Correggimi se sbaglio.
Palec,

2
Nessuna idea :) Se lo desideri, puoi confrontare con NULL nell'istruzione while.
Sergey

9

I letterali stringa sono const char *s.

E il tuo uso della parentesi è strano. Probabilmente intendi

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

che dichiara una matrice di due puntatori a caratteri costanti e li inizializza in modo che puntino a due costanti di stringa codificate.


3

Il tuo codice sta creando una matrice di puntatori a funzione. Provare

char* a[size];

o

char a[size1][size2];

anziché.

Vedi wiki per array e puntatori


1
cappello per il tuo approccio diverso ... Le persone come te fanno stack per traboccare ...
Sahu V Kumar

1

ciao puoi provare questo qui sotto:

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

un bell'esempio di utilizzo, array di stringhe in c se lo si desidera

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

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

Qui prova questo !!!


1
puoi spiegare perché hai bisogno del ciclo for con la variabile j, ovvero for (j = 0; j <1; j ++)?
SouvikMaji,

0

Mi mancava in qualche modo una matrice più dinamica di stringhe, in cui la quantità di stringhe poteva essere variata in base alla selezione del runtime, ma per il resto le stringhe dovevano essere riparate.

Ho finito per codificare lo snippet di codice in questo modo:

#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** evraccoglie il puntatore alle stringhe di array e conteggio raccoglie la quantità di stringhe usando la _countoffunzione. (Simile a sizeof(arr) / sizeof(arr[0])).

E c'è Ansi extra per convertire unicode usando la A2Tmacro, ma potrebbe essere facoltativo per il tuo caso.


-6

Un buon modo è quello di definire una stringa te stesso.

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

È davvero così semplice.


4
Ti manca un ;, e come si crea una serie di stringhe ?
keyser,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.