c++ - comparar - ¿Se calculará strlen muchas veces si se usa en una condición de bucle?
comparar cadenas sin funciones (18)
¡Arrgh, lo hará, incluso bajo circunstancias ideales, maldición!
A partir de hoy (enero 2018), y gcc 7.3 y clang 5.0, si compila:
#include <string.h>
void bar(char c);
void foo(const char* __restrict__ ss)
{
for (int i = 0; i < strlen(ss); ++i)
{
bar(*ss);
}
}
Entonces tenemos:
-
ss
es un puntero constante. -
ss
está marcado__restrict__
- El cuerpo del bucle no puede tocar de ninguna manera la memoria apuntada por
ss
(bueno, a menos que viole el__restrict__
).
y aún así , ambos compiladores ejecutan strlen()
cada iteración individual de ese ciclo . Asombroso.
Esto también significa que las alusiones / ilusiones de @Praetorian y @JaredPar no funcionan.
No estoy seguro de si el siguiente código puede causar cálculos redundantes, o es específico del compilador?
for (int i = 0; i < strlen(ss); ++i)
{
// blabla
}
Will strlen()
se calculará cada vez que aumente?
A partir de la respuesta de Prætorian, recomiendo lo siguiente:
for( auto i = strlen(s)-1; i > 0; --i ) {foo(s[i-1];}
-
auto
porque no quiere preocuparse por qué tipo de strlen regresa. Un compilador de C ++ 11 (por ejemplo,gcc -std=c++0x
, no completamente C ++ 11 pero los tipos de auto funcionan) lo hará por usted. -
i = strlen(s)
porque desea comparar con0
(ver a continuación) -
i > 0
porque la comparación a 0 es (ligeramente) más rápida que la comparación con cualquier otro número.
La desventaja es que debe usar i-1
para acceder a los caracteres de cadena.
Bueno, noté que alguien está diciendo que está optimizado por defecto por cualquier compilador moderno "inteligente". Por cierto, mira los resultados sin optimización. Lo intenté:
Código C mínimo:
#include <stdio.h>
#include <string.h>
int main()
{
char *s="aaaa";
for (int i=0; i<strlen(s);i++)
printf ("a");
return 0;
}
Mi compilador: g ++ (Ubuntu / Linaro 4.6.3-1ubuntu5) 4.6.3
Comando para generación de código ensamblador: 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
.....
El código de predicado en su totalidad se ejecutará en cada iteración del ciclo for
. Para memorizar el resultado de la strlen(ss)
el compilador debería saber que al menos
- La función
strlen
fue libre de efectos secundarios - La memoria apuntada por
ss
no cambia durante la duración del ciclo
El compilador no conoce ninguna de estas cosas y por lo tanto no puede memorizar de manera segura el resultado de la primera llamada
Formalmente, sí, se espera que strlen()
se strlen()
para cada iteración.
De todos modos, no quiero negar la posibilidad de la existencia de alguna optimización inteligente del compilador, que optimizará cualquier llamada sucesiva a strlen () después de la primera.
No es común hoy en día, pero hace 20 años en plataformas de 16 bits, lo recomendaría:
for ( char* p = str; *p; p++ ) { /* ... */ }
Incluso si su compilador no es muy inteligente en optimización, el código anterior puede dar como resultado un buen código de ensamblaje.
Podemos probarlo fácilmente:
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 condición del lazo se evalúa después de cada repetición, antes de reiniciar el ciclo.
También tenga cuidado con el tipo que usa para manejar la longitud de las cadenas. debe ser size_t
que se ha definido como unsigned int
en stdio. comparar y convertirlo en int
puede causar un problema de vulnerabilidad grave.
SÍ, en palabras simples. Y hay un pequeño no en condiciones raras en el que el compilador desea, como un paso de optimización si encuentra que no se han realizado cambios en ss
en absoluto. Pero en condiciones seguras deberías pensar que SÍ. Hay situaciones como las de multithreaded
y el programa impulsado por eventos, puede tener errores si lo considera un NO. Juega seguro, ya que no va a mejorar demasiado la complejidad del programa.
Sí, strlen()
se evaluará en cada iteración. Es posible que, en circunstancias ideales, el optimizador pueda deducir que el valor no cambiará, pero yo personalmente no confiaría en eso.
Haría algo como
for (int i = 0, n = strlen(ss); i < n; ++i)
o posiblemente
for (int i = 0; ss[i]; ++i)
siempre que la cadena no cambie de longitud durante la iteración. Si fuera posible, entonces necesitarás llamar a strlen()
cada vez o manejarlo a través de una lógica más complicada.
Sí, strlen(ss)
se calculará cada vez que se ejecute el código.
Sí, cada vez que usas el ciclo. Luego, calculará la longitud de la cuerda cada vez. así que úsalo así:
char str[30];
for ( int i = 0; str[i] != ''/0''; i++)
{
//Something;
}
En el código anterior, str[i]
solo verifica un carácter particular en la cadena en la ubicación i
cada vez que el ciclo inicia un ciclo, por lo tanto, requerirá menos memoria y será más eficiente.
Vea este Link para más información.
En el siguiente código cada vez que el ciclo se ejecuta strlen
contará la longitud de toda la cadena que es menos eficiente, toma más tiempo y lleva más memoria.
char str[];
for ( int i = 0; i < strlen(str); i++)
{
//Something;
}
Sí, el strlen(ss)
calculará la longitud en cada iteración. Si estás aumentando el ss
de alguna manera y también aumentando el i
; habría un ciclo infinito.
Sí, se llama a la función strlen()
cada vez que se evalúa el ciclo.
Si desea mejorar la eficiencia, recuerde siempre guardar todo en las variables locales ... Llevará tiempo, pero es muy útil.
Puede usar el código siguiente:
String str="ss";
int l = strlen(str);
for ( int i = 0; i < l ; i++ )
{
// blablabla
}
Sí. La prueba no sabe que ss no se cambia dentro del ciclo. Si sabes que no cambiará, entonces escribiría:
int stringLength = strlen (ss);
for ( int i = 0; i < stringLength; ++ i )
{
// blabla
}
Sí.
strlen()
calculado cada vez que aumenta y no está optimizado.
El siguiente código muestra por qué el compilador no debe optimizar strlen()
.
for ( int i = 0; i < strlen(ss); ++i )
{
// Change ss string.
ss[i] = ''a''; // Compiler should not optimize strlen().
}
Si ss
es de tipo const char *
y no está descartando la const
dentro del ciclo, el compilador solo puede llamar a strlen
una vez, si las optimizaciones están activadas. Pero esto ciertamente no es un comportamiento con el que se puede contar.
Debería guardar el resultado strlen
en una variable y usar esta variable en el ciclo. Si no desea crear una variable adicional, dependiendo de lo que esté haciendo, puede alejarse con la inversión del ciclo para iterar hacia atrás.
for( auto i = strlen(s); i > 0; --i ) {
// do whatever
// remember value of s[strlen(s)] is the terminating NULL character
}
Un buen compilador puede no calcularlo cada vez, pero no creo que pueda estar seguro de que todo compilador lo hace.
Además de eso, el compilador debe saber que strlen(ss)
no cambia. Esto solo es cierto si ss
no se cambia en el ciclo for
.
Por ejemplo, si usa una función de solo lectura en ss
in for
bucle pero no declara el parámetro ss
como const
, el compilador ni siquiera puede saber que ss
no se cambia en el bucle y tiene que calcular strlen(ss)
en cada iteración.
Sí . strlen se calculará cada vez que aumente.
Si no ha cambiado ss en el ciclo significa que no afectará la lógica, de lo contrario, afectará.
Es más seguro usar el siguiente código.
int length = strlen(ss);
for ( int i = 0; i < length ; ++ i )
{
// blabla
}