Comment déclarer un tableau de chaînes en C ++?

89

J'essaye d'itérer sur tous les éléments d'un tableau statique de chaînes de la meilleure façon possible. Je veux pouvoir le déclarer sur une seule ligne et y ajouter / supprimer facilement des éléments sans avoir à garder une trace du numéro. Cela semble vraiment simple, n'est-ce pas?

Non-solutions possibles:

vector<string> v;
v.push_back("abc");
b.push_back("xyz");

for(int i = 0; i < v.size(); i++)
    cout << v[i] << endl;

Problèmes - aucun moyen de créer le vecteur sur une ligne avec une liste de chaînes

Non-solution possible 2:

string list[] = {"abc", "xyz"};

Problèmes - aucun moyen d'obtenir le nombre de chaînes automatiquement (que je connaisse).

Il doit y avoir un moyen simple de le faire.

naumcho
la source
La bibliothèque d'attribution de boost semble être exactement ce que vous recherchez. Cela facilite plus que jamais l'attribution de constantes aux conteneurs.
Craig H

Réponses:

108

C ++ 11 a ajouté des listes d'initialisation pour autoriser la syntaxe suivante:

std::vector<std::string> v = {"Hello", "World"};

La prise en charge de cette fonctionnalité C ++ 11 a été ajoutée au moins dans GCC 4.4 et uniquement dans Visual Studio 2013 .

Anthony Cramp
la source
2018. Je viens de démarrer C ++ et j'ai fait pas mal de recherches sur les tableaux flexibles. J'ai fini par utiliser des vecteurs ...
Robert Molina
37

Vous pouvez initialiser vector<string>de manière concise un à partir d'un char*tableau créé statiquement :

char* strarray[] = {"hey", "sup", "dogg"};
vector<string> strvector(strarray, strarray + 3);

Cela copie toutes les chaînes, en passant, vous utilisez donc deux fois la mémoire. Vous pouvez utiliser la suggestion de Will Dean pour remplacer le nombre magique 3 ici par arraysize (str_array) - bien que je me souvienne qu'il y a eu un cas particulier dans lequel cette version particulière de arraysize pourrait faire quelque chose de mal (désolé, je ne me souviens pas des détails immédiatement) . Mais cela fonctionne très souvent correctement.

De plus, si vous êtes vraiment enthousiasmé par le truc d'une ligne, vous pouvez définir une macro variadique pour qu'une seule ligne telle que DEFINE_STR_VEC(strvector, "hi", "there", "everyone");fonctionne.

Tyler
la source
Puisqu'il se strarraytrouve dans un en-tête, ne violera-t-il pas la règle de définition unique?
jww
22

Problèmes - aucun moyen d'obtenir le nombre de chaînes automatiquement (que je connais).

Il existe une manière standard de faire cela, que beaucoup de gens (y compris MS) définissent des macros comme arraysizepour:

#define arraysize(ar)  (sizeof(ar) / sizeof(ar[0]))
Will Dean
la source
1
Alternativement, on pourrait utiliser quelque chose comme ceci: template<typename T, size_t N> inline size_t arraysize(T (&ar)[N]) { return N; } (Le mot clé en ligne n'est pas nécessaire, mais utilisé pour documenter l'intention de la fonction. Un compilateur moderne devrait théoriquement être en mesure de retourner la fonction entière, je crois.
Justin Time - Reinstate Monica
1
Cela échoue pour les pointeurs. Le comptage des éléments du tableau doit être effectué différemment en C ++.
jww
8

Déclarez un tableau de chaînes en C ++ comme ceci: char array_of_strings[][]

Par exemple : char array_of_strings[200][8192];

contiendra 200 chaînes, chaque chaîne ayant la taille 8kb ou 8192 octets.

utilisez strcpy(line[i],tempBuffer); pour mettre des données dans le tableau de chaînes.


la source
Pour info, char array_of_strings [] [] ne peut pas accepter les chaînes C ++, assurez-vous de convertir d'abord en char *. cplusplus.com/reference/string/string/c_str
Luqmaan
Puisqu'il se array_of_stringstrouve dans un en-tête, ne violera-t-il pas la règle de définition unique?
jww
7

Une possibilité est d'utiliser un pointeur NULL comme valeur d'indicateur:

const char *list[] = {"dog", "cat", NULL};
for (char **iList = list; *iList != NULL; ++iList)
{
    cout << *iList;
}
Éclipse
la source
Que signifie réellement char **? En java, serait-ce une liste de chaînes?
IAmGroot
1
@Doomsknight: Dans ce cas, oui. Dans la première ligne, je définis un tableau de char*. En mémoire, cela se présente sous la forme de 3 pointeurs - l'un pointe vers «chien», l'autre pointe vers «chat» et l'autre est laissé NULL. Je peux prendre un pointeur vers ce premier pointeur et obtenir un char**- un pointeur vers un pointeur vers char. Lorsque j'incrémente cela, je déplace le caractère ** pour qu'il pointe vers l'élément suivant dans la liste - un pointeur vers le pointeur qui pointe vers "chat", puis j'incrémente à nouveau et j'obtiens un pointeur qui pointe vers le pointeur NULL, et Je sais que j'ai fini. (
Eclipse du
4

Vous pouvez utiliser les fonctions beginet endde la bibliothèque de plages Boost pour trouver facilement les extrémités d'un tableau primitif, et contrairement à la solution macro, cela donnera une erreur de compilation au lieu d'un comportement cassé si vous l'appliquez accidentellement à un pointeur.

const char* array[] = { "cat", "dog", "horse" };
vector<string> vec(begin(array), end(array));
Ross Smith
la source
3

Vous pouvez utiliser la suggestion de Will Dean [ #define arraysize(ar) (sizeof(ar) / sizeof(ar[0]))] pour remplacer le nombre magique 3 ici par arraysize (str_array) - même si je me souviens qu'il y a eu un cas particulier dans lequel cette version particulière de arraysize pourrait faire quelque chose de mal (désolé, je ne me souviens pas des détails immédiatement). Mais cela fonctionne très souvent correctement.

Le cas où cela ne fonctionne pas est lorsque le "tableau" n'est en réalité qu'un pointeur, pas un tableau réel. De plus, en raison de la façon dont les tableaux sont passés aux fonctions (convertis en pointeur vers le premier élément), cela ne fonctionne pas entre les appels de fonction même si la signature ressemble à un tableau - some_function(string parameter[])c'est vraiment some_function(string *parameter).

Matthew Crumley
la source
3

Voici un exemple:

#include <iostream>
#include <string>
#include <vector>
#include <iterator>

int main() {
    const char* const list[] = {"zip", "zam", "bam"};
    const size_t len = sizeof(list) / sizeof(list[0]);

    for (size_t i = 0; i < len; ++i)
        std::cout << list[i] << "\n";

    const std::vector<string> v(list, list + len);
    std::copy(v.begin(), v.end(), std::ostream_iterator<string>(std::cout, "\n"));
}
Ombre2531
la source
2

Au lieu de cette macro, pourrais-je suggérer celle-ci:

template<typename T, int N>
inline size_t array_size(T(&)[N])
{
    return N;
}

#define ARRAY_SIZE(X)   (sizeof(array_size(X)) ? (sizeof(X) / sizeof((X)[0])) : -1)

1) Nous voulons utiliser une macro pour en faire une constante à la compilation; le résultat de l'appel de fonction n'est pas une constante de compilation.

2) Cependant, nous ne voulons pas utiliser de macro car la macro pourrait être accidentellement utilisée sur un pointeur. La fonction ne peut être utilisée que sur des tableaux au moment de la compilation.

Donc, nous utilisons la définition de la fonction pour rendre la macro "sûre"; si la fonction existe (c'est-à-dire qu'elle a une taille non nulle) alors nous utilisons la macro comme ci-dessus. Si la fonction n'existe pas, nous renvoyons une valeur incorrecte.

DrPizza
la source
2
#include <boost/foreach.hpp>

const char* list[] = {"abc", "xyz"};
BOOST_FOREACH(const char* str, list)
{
    cout << str << endl;
}

la source
1
#include <iostream>
#include <string>
#include <vector>
#include <boost/assign/list_of.hpp>

int main()
{
    const std::vector< std::string > v = boost::assign::list_of( "abc" )( "xyz" );
    std::copy(
        v.begin(),
        v.end(),
        std::ostream_iterator< std::string >( std::cout, "\n" ) );
}
Dominic.wig
la source
1

Vous pouvez déclarer directement un tableau de chaînes comme string s[100];. Ensuite, si vous souhaitez accéder à des éléments spécifiques, vous pouvez l'obtenir directement comme s[2][90]. À des fins d'itération, prenez la taille de la chaîne à l'aide de la s[i].size()fonction.

kajol jain
la source