web-dev-qa-db-fra.com

Le strlen sera-t-il calculé plusieurs fois s'il est utilisé en boucle?

Je ne sais pas si le code suivant peut provoquer des calculs redondants, ou est-il spécifique au compilateur?

for (int i = 0; i < strlen(ss); ++i)
{
    // blabla
}

strlen() sera-t-elle calculée à chaque fois que i augmentera?

108
daisy

Oui, strlen() sera évaluée à chaque itération. Il est possible que, dans des circonstances idéales, l'optimiseur puisse déduire que la valeur ne changera pas, mais personnellement, je ne m'y fierais pas.

Je ferais quelque chose comme

for (int i = 0, n = strlen(ss); i < n; ++i)

ou peut-être

for (int i = 0; ss[i]; ++i)

tant que la chaîne ne changera pas de longueur pendant l'itération. Si c'est le cas, vous devrez soit appeler strlen() à chaque fois, soit le gérer par une logique plus compliquée.

136
Mike Seymour

Oui, chaque fois que vous utilisez la boucle. Ensuite, il calculera à chaque fois la longueur de la chaîne. alors utilisez-le comme ceci:

char str[30];
for ( int i = 0; str[i] != '\0'; i++)
{
//Something;
}

Dans le code ci-dessus str[i] vérifie uniquement un caractère particulier de la chaîne à l'emplacement i chaque fois que la boucle démarre un cycle, donc cela prendra moins de mémoire et est plus efficace.

Voir ceci Lien pour plus d'informations.

Dans le code ci-dessous chaque fois que la boucle s'exécute strlen comptera la longueur de la chaîne entière qui est moins efficace, prend plus de temps et prend plus de mémoire.

char str[];
for ( int i = 0; i < strlen(str); i++)
{
//Something;
}
14
codeDEXTER

Un bon compilateur peut ne pas le calculer à chaque fois, mais je ne pense pas que vous puissiez être sûr que chaque compilateur le fasse.

En plus de cela, le compilateur doit savoir que strlen(ss) ne change pas. Cela n'est vrai que si ss n'est pas modifié dans la boucle for.

Par exemple, si vous utilisez une fonction en lecture seule sur ss dans la boucle for mais ne déclarez pas le paramètre ss- comme const, le compilateur ne peut même pas savoir que ss n'est pas modifié dans la boucle et doit calculer strlen(ss) à chaque itération.

9
Misch

Si ss est de type const char * et vous ne supprimez pas le constness dans la boucle, le compilateur ne peut appeler strlen qu'une seule fois, si les optimisations sont activées. Mais ce n'est certainement pas un comportement sur lequel on peut compter.

Vous devez enregistrer le résultat strlen dans une variable et utiliser cette variable dans la boucle. Si vous ne voulez pas créer de variable supplémentaire, selon ce que vous faites, vous pourriez être en mesure de vous en sortir en inversant la boucle pour parcourir en arrière.

for( auto i = strlen(s); i > 0; --i ) {
  // do whatever
  // remember value of s[strlen(s)] is the terminating NULL character
}
4
Praetorian

Formellement oui, strlen() devrait être appelé pour chaque itération.

Quoi qu'il en soit, je ne veux pas nier la possibilité de l'existence d'une optimisation intelligente du compilateur, qui optimisera tout appel successif à strlen () après le premier.

3
alk

Le code prédicat dans son intégralité sera exécuté à chaque itération de la boucle for. Pour mémoriser le résultat de l'appel strlen(ss), le compilateur devrait savoir qu'au moins

  1. La fonction strlen était sans effet secondaire
  2. La mémoire pointée par ss ne change pas pendant la durée de la boucle

Le compilateur ne connaît aucune de ces choses et ne peut donc pas mémoriser le résultat du premier appel en toute sécurité

3
JaredPar

Oui, strlen(ss) sera calculée à chaque exécution du code.

2
Hisham Muneer

Oui, la fonction strlen() est appelée à chaque fois la boucle est évaluée.

Si vous voulez améliorer l'efficacité, n'oubliez pas de tout sauvegarder dans les variables locales ... Cela prendra du temps mais c'est très utile ..

Vous pouvez utiliser le code comme ci-dessous:

String str="ss";
int l = strlen(str);

for ( int i = 0; i < l ; i++ )
{
    // blablabla
}
2
Rajan

Oui, la strlen(ss) calculera la longueur à chaque itération. Si vous augmentez le ss d'une certaine manière et augmentez également le i; il y aurait une boucle infinie.

2
Kumar Shorav

Pas courant de nos jours mais il y a 20 ans sur des plateformes 16 bits, je recommanderais ceci:

for ( char* p = str; *p; p++ ) { /* ... */ }

Même si votre compilateur n'est pas très intelligent dans l'optimisation, le code ci-dessus peut encore donner un bon code d'assemblage.

2
Luciano

Oui. strlen sera calculé à chaque fois que j'augmente.

Si vous n'a pas changé ss avec dans la boucle signifie cela n'affectera pas la logique sinon cela affectera.

Il est plus sûr d'utiliser le code suivant.

int length = strlen(ss);

for ( int i = 0; i < length ; ++ i )
{
 // blabla
}
2
Kalai Selvan Ravi

Arrgh, ça va, même dans des circonstances idéales, bon sang!

À compter d'aujourd'hui (janvier 2018), et gcc 7.3 et clang 5.0, si vous compilez:

#include <string.h>

void bar(char c);

void foo(const char* __restrict__ ss) 
{
    for (int i = 0; i < strlen(ss); ++i) 
    {
        bar(*ss);
    }
}    

Donc nous avons:

  • ss est un pointeur constant.
  • ss est marqué __restrict__
  • Le corps de la boucle ne peut en aucun cas toucher la mémoire pointée par ss (enfin, à moins qu'il ne viole le __restrict__).

et toujours , les deux compilateurs exécutent strlen()à chaque itération de cette boucle . Incroyable.

Cela signifie également que les allusions/vœux pieux de @Praetorian et @JaredPar ne se déroulent pas.

1
einpoklum

Oui. Le test ne sait pas que ss n'est pas modifié à l'intérieur de la boucle. Si vous savez que cela ne changera pas, j'écrirais:

int stringLength = strlen (ss); 
for ( int i = 0; i < stringLength; ++ i ) 
{
  // blabla 
} 
1
DanS

eh bien, j'ai remarqué que quelqu'un dit qu'il est optimisé par défaut par n'importe quel compilateur moderne "intelligent". Soit dit en passant, regardez les résultats sans optimisation. J'ai essayé:
Code C minimal:

#include <stdio.h>
#include <string.h>

int main()
{
 char *s="aaaa";

 for (int i=0; i<strlen(s);i++)
  printf ("a");
 return 0;
}

Mon compilateur: g ++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3
Commande pour la génération du code d'assembly: g ++ -S -masm = intel test.cpp

Gotten Assembly code at the output:
    ...
    L3:
mov DWORD PTR [esp], 97
call    putchar
add DWORD PTR [esp+40], 1
    .L2:
     THIS LOOP IS HERE
    **<b>mov    ebx, DWORD PTR [esp+40]
mov eax, DWORD PTR [esp+44]
mov DWORD PTR [esp+28], -1
mov edx, eax
mov eax, 0
mov ecx, DWORD PTR [esp+28]
mov edi, edx
repnz scasb</b>**
     AS YOU CAN SEE it's done every time
mov eax, ecx
not eax
sub eax, 1
cmp ebx, eax
setb    al
test    al, al
jne .L3
mov eax, 0
     .....
0
Tebe

En élaborant la réponse de Prætorian, je recommande ce qui suit:

for( auto i = strlen(s)-1; i > 0; --i ) {foo(s[i-1];}
  • auto parce que vous ne voulez pas vous soucier du type de retour de strlen. Un compilateur C++ 11 (par exemple gcc -std=c++0x, Pas complètement C++ 11 mais les types automatiques fonctionnent) le fera pour vous.
  • i = strlen(s) parce que vous voulez comparer à 0 (voir ci-dessous)
  • i > 0 Car la comparaison avec 0 est (légèrement) plus rapide que la comparaison avec n'importe quel autre nombre.

l'inconvénient est que vous devez utiliser i-1 pour accéder aux caractères de la chaîne.

0
steffen

OUI, en termes simples. Et il y a un petit non dans un état rare dans lequel le compilateur souhaite, comme étape d'optimisation s'il constate qu'aucune modification n'a été apportée à ss. Mais dans un état sûr, vous devriez le penser comme OUI. Il y a des situations comme dans multithreaded et programme piloté par événements, il peut devenir bogué si vous le considérez comme NON. Jouez prudemment car cela n'améliorera pas trop la complexité du programme.

0
Pervez Alam

Oui.

strlen() calculé à chaque fois que i augmente et n'est pas optimisé.

Le code ci-dessous montre pourquoi le compilateur ne doit pas optimiser strlen().

for ( int i = 0; i < strlen(ss); ++i )
{
   // Change ss string.
   ss[i] = 'a'; // Compiler should not optimize strlen().
}
0
Amir Saniyan

Nous pouvons facilement le tester:

char nums[] = "0123456789";
size_t end;
int i;
for( i=0, end=strlen(nums); i<strlen(nums); i++ ) {
    putchar( nums[i] );
    num[--end] = 0;
}

La condition de boucle est évaluée après chaque répétition, avant de redémarrer la boucle.

Faites également attention au type que vous utilisez pour gérer la longueur des cordes. ça devrait être size_t qui a été défini comme unsigned int in stdio. le comparer et le convertir en int peut entraîner de sérieux problèmes de vulnérabilité.

0
Rsh