Je suis tombé sur ces deux méthodes pour concaténer des chaînes:
Partie commune:
char* first= "First";
char* second = "Second";
char* both = malloc(strlen(first) + strlen(second) + 2);
Méthode 1:
strcpy(both, first);
strcat(both, " "); // or space could have been part of one of the strings
strcat(both, second);
Méthode 2:
sprintf(both, "%s %s", first, second);
Dans les deux cas, le contenu de both
serait "First Second"
.
Je voudrais savoir lequel est le plus efficace (je dois effectuer plusieurs opérations de concaténation), ou si vous connaissez une meilleure façon de le faire.
Pour la lisibilité, j'irais avec
char * s = malloc(snprintf(NULL, 0, "%s %s", first, second) + 1);
sprintf(s, "%s %s", first, second);
Si votre plate-forme supporte les extensions GNU, vous pouvez également utiliser asprintf()
:
char * s = NULL;
asprintf(&s, "%s %s", first, second);
Si vous êtes bloqué avec le runtime MS C, vous devez utiliser _scprintf()
pour déterminer la longueur de la chaîne résultante:
char * s = malloc(_scprintf("%s %s", first, second) + 1);
sprintf(s, "%s %s", first, second);
Ce qui suit sera probablement la solution la plus rapide:
size_t len1 = strlen(first);
size_t len2 = strlen(second);
char * s = malloc(len1 + len2 + 2);
memcpy(s, first, len1);
s[len1] = ' ';
memcpy(s + len1 + 1, second, len2 + 1); // includes terminating null
Ne vous souciez pas de l'efficacité: rendez votre code lisible et maintenable. Je doute que la différence entre ces méthodes ait de l'importance dans votre programme.
Voici de la folie pour vous, je suis allé le mesurer. Putain, imaginez ça. Je pense que j'ai eu des résultats significatifs.
J'ai utilisé un P4 double cœur, tournant sous Windows, utilisant mingw gcc 4.4, construisant avec "gcc foo.c -o foo.exe -std = c99 -Wall -O2".
J'ai testé la méthode 1 et la méthode 2 à partir du message original. Initialement gardé le malloc en dehors de la boucle de référence. La méthode 1 était 48 fois plus rapide que la méthode 2. Bizarrement, la suppression de -O2 de la commande de construction rendait le fichier exe résultant 30% plus rapide (je n'ai pas encore cherché pourquoi).
Ensuite, j'ai ajouté un malloc et libre dans la boucle. Cela a ralenti la méthode 1 d'un facteur de 4,4. Méthode 2 ralentie par un facteur de 1,1.
Ainsi, malloc + strlen + free NE domine PAS assez le profil pour éviter le sprintf.
Voici le code que j'ai utilisé (à part que les boucles ont été implémentées avec <au lieu de! = Mais cela a cassé le rendu HTML de ce post):
void a(char *first, char *second, char *both)
{
for (int i = 0; i != 1000000 * 48; i++)
{
strcpy(both, first);
strcat(both, " ");
strcat(both, second);
}
}
void b(char *first, char *second, char *both)
{
for (int i = 0; i != 1000000 * 1; i++)
sprintf(both, "%s %s", first, second);
}
int main(void)
{
char* first= "First";
char* second = "Second";
char* both = (char*) malloc((strlen(first) + strlen(second) + 2) * sizeof(char));
// Takes 3.7 sec with optimisations, 2.7 sec WITHOUT optimisations!
a(first, second, both);
// Takes 3.7 sec with or without optimisations
//b(first, second, both);
return 0;
}
size_t lf = strlen(first);
size_t ls = strlen(second);
char *both = (char*) malloc((lf + ls + 2) * sizeof(char));
strcpy(both, first);
both[lf] = ' ';
strcpy(&both[lf+1], second);
La différence est peu probable:
Comme d'autres affiches l'ont mentionné, il s'agit d'une optimisation prématurée. Concentrez-vous sur la conception de l'algorithme et n'y revenez que si le profilage montre qu'il s'agit d'un problème de performances.
Cela dit ... I suspect la méthode 1 sera plus rapide. Il y a un peu de surcharge - certes petite - pour analyser la chaîne de formatage sprintf. Et strcat est plus probable "inline -able".
Ils devraient être à peu près les mêmes. La différence ne va pas avoir d'importance. J'irais avec sprintf
car il nécessite moins de code.
sprintf () est conçu pour gérer beaucoup plus que des chaînes, strcat () est un spécialiste. Mais je soupçonne que vous transpirez les petites choses. Les chaînes de caractères C sont fondamentalement inefficaces en ce sens que les différences entre ces deux méthodes proposées sont insignifiantes. Lire "Back to Basics" par Joel Spolsky pour les détails sanglants.
C++ présente généralement de meilleures performances que C. La gestion de chaînes lourdes utilisant std :: string sera probablement plus efficace et certainement plus sûre.
[modifier]
[2ème édition] Le code corrigé (trop d'itérations dans l'implémentation de la chaîne C), les temps et la conclusion changent en conséquence
J'ai été surpris par le commentaire d'Andrew Bainbridge selon lequel std :: string était plus lent, mais il n'a pas publié de code complet pour ce scénario de test. J'ai modifié le sien (automatisation du timing) et ajouté un test std :: string. Le test portait sur VC++ 2008 (code natif) avec les options par défaut "Release" (optimisées), Athlon dual core, 2,6 GHz. Résultats:
C string handling = 0.023000 seconds
sprintf = 0.313000 seconds
std::string = 0.500000 seconds
Donc, ici, strcat () est de loin plus rapide (votre kilométrage peut varier en fonction du compilateur et des options), malgré l'inefficacité inhérente à la convention de la chaîne C, et confirme ma suggestion initiale selon laquelle sprintf () transporte beaucoup de bagages non nécessaires à cette fin. . Il reste de loin le moins lisible et le moins sûr, donc, lorsque les performances ne sont pas critiques, a peu de mérite à l’OMI.
J'ai également testé une implémentation std :: stringstream, ce qui était encore beaucoup plus lent, mais le formatage de chaîne complexe a toujours son mérite.
Le code corrigé suit:
#include <ctime>
#include <cstdio>
#include <cstring>
#include <string>
void a(char *first, char *second, char *both)
{
for (int i = 0; i != 1000000; i++)
{
strcpy(both, first);
strcat(both, " ");
strcat(both, second);
}
}
void b(char *first, char *second, char *both)
{
for (int i = 0; i != 1000000; i++)
sprintf(both, "%s %s", first, second);
}
void c(char *first, char *second, char *both)
{
std::string first_s(first) ;
std::string second_s(second) ;
std::string both_s(second) ;
for (int i = 0; i != 1000000; i++)
both_s = first_s + " " + second_s ;
}
int main(void)
{
char* first= "First";
char* second = "Second";
char* both = (char*) malloc((strlen(first) + strlen(second) + 2) * sizeof(char));
clock_t start ;
start = clock() ;
a(first, second, both);
printf( "C string handling = %f seconds\n", (float)(clock() - start)/CLOCKS_PER_SEC) ;
start = clock() ;
b(first, second, both);
printf( "sprintf = %f seconds\n", (float)(clock() - start)/CLOCKS_PER_SEC) ;
start = clock() ;
c(first, second, both);
printf( "std::string = %f seconds\n", (float)(clock() - start)/CLOCKS_PER_SEC) ;
return 0;
}
Je ne sais pas si dans le cas deux il y a vraiment une concaténation faite. Les imprimer dos à dos ne constitue pas une concaténation.
Dis-moi cependant, ce serait plus rapide:
1) a) copier la chaîne A dans le nouveau tampon b) copier la chaîne B dans le tampon c) copier le tampon dans le tampon de sortie
ou
1) copier la chaîne A dans le tampon de sortie b) copier la chaîne b dans le tampon de sortie