88 votes

Utilisation de char* comme clé dans std::map

J'essaie de comprendre pourquoi le code suivant ne fonctionne pas. Je suppose qu'il s'agit d'un problème lié à l'utilisation de char* comme type de clé, mais je ne sais pas comment le résoudre ni pourquoi il se produit. Toutes les autres fonctions que j'utilise (dans le SDK HL2) utilisent char* donc en utilisant std::string va causer beaucoup de complications inutiles.

std::map<char*, int> g_PlayerNames;

int PlayerManager::CreateFakePlayer()
{
    FakePlayer *player = new FakePlayer();
    int index = g_FakePlayers.AddToTail(player);

    bool foundName = false;

    // Iterate through Player Names and find an Unused one
    for(std::map<char*,int>::iterator it = g_PlayerNames.begin(); it != g_PlayerNames.end(); ++it)
    {
        if(it->second == NAME_AVAILABLE)
        {
            // We found an Available Name. Mark as Unavailable and move it to the end of the list
            foundName = true;
            g_FakePlayers.Element(index)->name = it->first;

            g_PlayerNames.insert(std::pair<char*, int>(it->first, NAME_UNAVAILABLE));
            g_PlayerNames.erase(it); // Remove name since we added it to the end of the list

            break;
        }
    }

    // If we can't find a usable name, just user 'player'
    if(!foundName)
    {
        g_FakePlayers.Element(index)->name = "player";
    }

    g_FakePlayers.Element(index)->connectTime = time(NULL);
    g_FakePlayers.Element(index)->score = 0;

    return index;
}

16 votes

Parfois, faire la bonne chose fait mal au début. Changez votre code pour utiliser std:string une fois, et être heureux après.

1 votes

Quel genre de complications ? il existe une conversion implicite de char* en std::string.

1 votes

Vous ne devez pas utiliser char* comme une clé de carte. Voir ma réponse pourquoi.

154voto

GWW Points 20316

Vous devez donner un foncteur de comparaison à la carte, sinon elle compare le pointeur, et non la chaîne à terminaison nulle vers laquelle il pointe. En général, c'est le cas chaque fois que vous voulez que la clé de votre map soit un pointeur.

Par exemple :

struct cmp_str
{
   bool operator()(char const *a, char const *b) const
   {
      return std::strcmp(a, b) < 0;
   }
};

map<char *, int, cmp_str> BlahBlah;

2 votes

En fait, il peut juste passer le &std::strcmp comme troisième paramètre du modèle

28 votes

Non, strcmp renvoie un nombre entier positif, nul ou négatif. Le foncteur map doit retourner true pour less-than et false sinon.

5 votes

@Armen : Je ne pense pas que cela fonctionne, car le 3ème paramètre du modèle attend quelque chose comme f(a,b) = a<b pas f(a,b) = (-1 if a<b, 1 if a>b, 0 else) .

46voto

Pablo Santa Cruz Points 73944

Vous ne pouvez pas utiliser char* à moins que vous ne soyez absolument sûr à 100% d'accéder à la carte avec la fonction exactement les mêmes pointeurs et non des cordes.

Ejemplo:

char *s1; // pointing to a string "hello" stored memory location #12
char *s2; // pointing to a string "hello" stored memory location #20

Si vous accédez à la carte avec s1 vous obtiendrez un emplacement différent de celui que vous obtiendrez en y accédant avec s2 .

6 votes

Sauf si vous définissez votre propre comparateur, comme expliqué dans la réponse acceptée.

25voto

aschepler Points 23731

Deux chaînes de caractères de style C peuvent avoir le même contenu mais être à des adresses différentes. Et que map compare les pointeurs, pas le contenu.

Le coût de la conversion à std::map<std::string, int> n'est peut-être pas aussi importante que vous le pensez.

Mais si vous avez vraiment besoin d'utiliser const char* comme clés de carte, essayez :

#include <functional>
#include <cstring>
struct StrCompare : public std::binary_function<const char*, const char*, bool> {
public:
    bool operator() (const char* str1, const char* str2) const
    { return std::strcmp(str1, str2) < 0; }
};

typedef std::map<const char*, int, StrCompare> NameMap;
NameMap g_PlayerNames;

0 votes

Merci pour l'information. D'après mon expérience, je recommande vivement de convertir en std::string.

9voto

sbi Points 100828

Vous pouvez le faire fonctionner avec std::map<const char*, int> mais il ne doit pas utiliser des éléments non const (notez l'ajout de const pour la clé), car vous ne devez pas modifier ces chaînes de caractères pendant que la carte s'y réfère en tant que clés. (Alors qu'une map protège ses clés en les rendant const cela ne ferait que confirmer la pointeur et non la chaîne vers laquelle il pointe).

Mais pourquoi ne pas simplement utiliser std::map<std::string, int> ? Il fonctionne sans problème, sans maux de tête.

8voto

Daniel Daranas Points 15123

Vous comparez en utilisant un char * à l'utilisation d'une chaîne de caractères. Ce n'est pas la même chose.

A char * est un pointeur sur un char. En fin de compte, il s'agit d'un type entier dont la valeur est interprétée comme une adresse valide pour un objet de type char .

Une chaîne est une chaîne.

Le conteneur fonctionne correctement, mais en tant que conteneur pour les paires dans lesquelles la clé est un char * et la valeur est un int .

1 votes

Il n'est pas nécessaire qu'un pointeur soit un entier long. Il existe des plateformes (comme win64, si vous en avez déjà entendu parler :-)) où un entier long est plus petit qu'un pointeur, et je crois qu'il existe aussi des plateformes plus obscures où les pointeurs et les entiers sont chargés dans des registres différents et traités différemment d'autres manières. Le C++ exige seulement que les pointeurs soient convertibles en un type intégral et inversement ; notez que cela n'implique pas que vous puissiez convertir n'importe quel entier suffisamment petit en pointeur, seulement ceux que vous obtenez en convertissant un pointeur.

0 votes

@ChristopherCreutzig, merci pour votre commentaire. J'ai modifié ma réponse en conséquence.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X