web-dev-qa-db-fra.com

Passer un tableau en tant que paramètre de fonction en C++

En C++, les tableaux ne peuvent pas être passés simplement en tant que paramètres. Signification si je crée une fonction comme ceci:

void doSomething(char charArray[])
{
    // if I want the array size
    int size = sizeof(charArray);
    // NO GOOD, will always get 4 (as in 4 bytes in the pointer)
}

Je n'ai aucun moyen de savoir quelle est la taille du tableau, car je n'ai qu'un pointeur sur le tableau.

Sans changer la signature de la méthode, dans quel sens puis-je obtenir la taille du tableau et le parcourir sur ses données?


EDIT: juste un ajout concernant la solution. Si le tableau de caractères, en particulier, a été initialisé comme suit:

char charArray[] = "i am a string";

alors le \0 est déjà ajouté à la fin du tableau. Dans ce cas, la réponse (marquée comme acceptée) fonctionne comme si de rien n'était.

17
Yuval Adam

Sans changer la signature? Ajouter un élément sentinelle. Pour les tableaux de caractères en particulier, il pourrait s'agir du '\0' à terminaison nulle utilisé pour les chaînes C standard. 

void doSomething(char charArray[])
{
    char* p = charArray;
    for (; *p != '\0'; ++p)
    {
         // if '\0' happens to be valid data for your app, 
         // then you can (maybe) use some other value as
         // sentinel
    }
    int arraySize = p - charArray;

    // now we know the array size, so we can do some thing
}

Bien entendu, votre tableau lui-même ne peut pas contenir l'élément sentinel en tant que contenu. Pour d'autres types de tableaux (c'est-à-dire sans caractère), il peut s'agir de toute valeur qui n'est pas une donnée légale. Si aucune valeur de ce type n'existe, cette méthode ne fonctionne pas.

De plus, cela nécessite une coopération côté appelant. Vous devez vraiment vous assurer que l'appelant réserve un tableau d'éléments arraySize + 1 et always définit l'élément sentinel.

Cependant, si vous ne pouvez vraiment pas changer la signature, vos options sont plutôt limitées.

23
Reunanen

Utilisez des modèles. Techniquement, cela ne correspond pas à vos critères, car il modifie la signature, mais le code d'appel n'a pas besoin d'être modifié.

void doSomething(char charArray[], size_t size)
{
   // do stuff here
}

template<size_t N>
inline void doSomething(char (&charArray)[N])
{
    doSomething(charArray, N);
}

Cette technique est utilisée par les fonctions Secure CRT de Microsoft et par le modèle de classe array_proxy de STLSoft.

38
Josh Kelley

En général, lorsque vous travaillez en C ou en C++ de bas niveau, vous pouvez envisager de recycler votre cerveau pour ne jamais envisager d'écrire des paramètres de tableau dans une fonction, car le compilateur C les traitera toujours comme des pointeurs. Essentiellement, en tapant ces crochets, vous vous trompez en pensant qu'un véritable tableau est en train d'être passé, avec les informations de taille. En réalité, en C, vous ne pouvez que transmettre des pointeurs. La fonction

void foo(char a[])
{
    // Do something...
}

est, du point de vue du compilateur C, exactement équivalent à:

void foo(char * a)
{
    // Do something
}

et évidemment ce pointeur de caractères nekkid ne contient aucune information de longueur.

Si vous êtes coincé dans un coin et que vous ne pouvez pas modifier la signature de la fonction, envisagez d'utiliser un préfixe de longueur comme suggéré ci-dessus. Un hack non-portable mais compatible consiste à spécifier la longueur du tableau dans un champ size_t situé before le tableau, à peu près comme ceci:

void foo(char * a)
{
    int cplusplus_len = reinterpret_cast<std::size_t *>(a)[-1];
    int c_len = ((size_t *)a)[-1];
}

Évidemment, votre appelant doit créer les tableaux de la manière appropriée avant de les transmettre à foo. 

Inutile de dire que c'est un piratage horrible, mais cette astuce peut vous éviter des ennuis. 

6
John Källén

Il était en fait une solution assez courante de passer la longueur au premier élément du tableau. Ce type de structure est souvent appelé BSTR (pour «chaîne BASIC»), même si cela dénotait également des types différents (mais similaires).

L'avantage par rapport à la solution acceptée est que la détermination de la longueur à l'aide d'une sentinelle est lente pour les chaînes de grande taille. L’inconvénient est évidemment qu’il s’agit d’un hack de bas niveau qui ne respecte ni les types ni la structure.

Dans la forme donnée ci-dessous, cela ne fonctionne également que pour les chaînes de longueur <= 255. Toutefois, cela peut facilement être étendu en stockant la longueur dans plusieurs octets.

void doSomething(char* charArray)
{
    // Cast unnecessary but I prefer explicit type conversions.
    std::size_t length = static_cast<std::size_t>(static_cast<unsigned char>(charArray[0]));
    // … do something.
}
6
Konrad Rudolph

si elle est nullterminée, strlen () fonctionnerait. 

4
Jimmy

Vous ne pouvez pas déterminer la taille à partir de charArray seule. Cette information n'est pas automatiquement transmise à la fonction.

Bien sûr, si c'est une chaîne terminée par un zéro, vous pouvez utiliser strlen(), mais vous l'avez probablement déjà envisagé!

Envisagez de passer un paramètre std::vector<char> &, ou une paire de pointeurs, ou un pointeur plus un paramètre de taille.

2
finnw

C’est en fait plus du C++ que du C++. En C++, vous préféreriez probablement utiliser un std :: vector. Cependant, en C, il n’ya aucun moyen de connaître la taille d’un tableau. La compilation vous permettra de faire un sizeof si le tableau a été déclaré dans l’étendue actuelle, et seulement s’il a été déclaré explicitement avec une taille (MODIFIER: et "avec une taille", je veux dire qu’il a été soit déclaré avec une taille entière, soit initialisé lors de la déclaration, au lieu d’être passé en paramètre, merci pour le vote négatif).

La solution commune en C consiste à passer un deuxième paramètre décrivant le nombre d'éléments dans le tableau.

MODIFIER:
Désolé, j'ai raté la partie sur le fait de ne pas vouloir changer la signature de la méthode. Ensuite, il n'y a pas de solution, sauf comme décrit également par d'autres. Si des données ne sont pas autorisées dans le tableau, elles peuvent être utilisées comme terminateur (0 dans les chaînes de caractères C, -1 est également assez commun, type de données réel, en supposant que le tableau de caractères est hypothétique)

2
falstro

Pour qu'une fonction connaisse le nombre d'éléments d'un tableau qui lui ont été transmis, vous devez effectuer l'une des opérations suivantes: 

  1. Passer un paramètre de taille 
  2. Mettez les informations de taille dans le tableau en quelque sorte.

Vous pouvez faire ce dernier de plusieurs manières:

  • Terminez-le avec un NULL ou une autre sentinelle qui ne se produira pas dans les données normales.
  • stocke le nombre d'éléments dans la première entrée si le tableau contient des nombres
  • stocker un pointeur sur la dernière entrée si le tableau contient des pointeurs
1
AShelly

essayez d'utiliser strlen (charArray); en utilisant le fichier d'en-tête cstring. cela produira le nombre de caractères, espaces compris, jusqu'à la fermeture ".

0
Percy Stainwall