Будет ли вычисляться strlen несколько раз, если он используется в состоянии цикла?
Я не уверен, может ли следующий код вызвать избыточные вычисления или он специфичен для компилятора?
for (int i = 0; i < strlen(ss); ++i)
{
// blabla
}
Будет ли вычисляться strlen()
каждый раз, когда i
увеличивается?
Ответы
Ответ 1
Да, strlen()
будет оцениваться на каждой итерации. Возможно, что при идеальных обстоятельствах оптимизатор может определить, что значение не изменится, но я лично не буду на это полагаться.
Я бы сделал что-то вроде
for (int i = 0, n = strlen(ss); i < n; ++i)
или, возможно,
for (int i = 0; ss[i]; ++i)
пока строка не изменит длину во время итерации. Если это возможно, вам придется либо вызывать strlen()
каждый раз, либо обрабатывать его с помощью более сложной логики.
Ответ 2
Да, каждый раз, когда вы используете цикл. Затем он будет каждый раз вычислять длину строки.
поэтому используйте его так:
char str[30];
for ( int i = 0; str[i] != '\0'; i++)
{
//Something;
}
В приведенном выше коде str[i]
проверяется только один конкретный символ в строке в местоположении i
каждый раз, когда цикл запускает цикл, поэтому он будет потреблять меньше памяти и будет более эффективным.
Подробнее см. Ссылка.
В приведенном ниже коде каждый раз, когда цикл работает strlen
будет считать длину всей строки, которая менее эффективна, занимает больше времени и занимает больше памяти.
char str[];
for ( int i = 0; i < strlen(str); i++)
{
//Something;
}
Ответ 3
Хороший компилятор не может рассчитывать его каждый раз, но я не думаю, что вы можете быть уверены, что каждый компилятор делает это.
В дополнение к этому компилятор должен знать, что strlen (ss) не изменяется. Это справедливо только в том случае, если ss не изменяется в for-loop.
Например, если вы используете функцию read-only для ss в for-loop, но не объявляете ss-параметр как const, компилятор даже не может знать, что ss не изменяется в цикле и имеет значение вычислять strlen (ss) на каждой итерации
Ответ 4
Если ss
имеет тип const char *
и вы не отбрасываете const
ness в цикле, компилятор может вызывать только strlen
один раз, если оптимизация включена. Но это, конечно, не поведение, на которое можно рассчитывать.
Вы должны сохранить результат strlen
в переменной и использовать эту переменную в цикле. Если вы не хотите создавать дополнительную переменную, в зависимости от того, что вы делаете, вы можете быть эль, чтобы уйти с обратным циклом для повторения итерации назад.
for( auto i = strlen(s); i > 0; --i ) {
// do whatever
// remember value of s[strlen(s)] is the terminating NULL character
}
Ответ 5
Формально да, strlen()
ожидается для каждой итерации.
В любом случае я не хочу отрицать возможность существования некоторой умной оптимизации компилятора, которая оптимизирует любой последовательный вызов strlen() после первого.
Ответ 6
Предикатный код целиком будет выполняться на каждой итерации цикла for
. Чтобы запомнить результат вызова strlen(ss)
, компилятор должен знать, что по крайней мере
- Функция
strlen
свободна от побочных эффектов
- Память, на которую указывает
ss
, не изменяется на время цикла
Компилятор не знает ни одной из этих вещей и, следовательно, не может спокойно запоминать результат первого вызова
Ответ 7
Да, strlen (ss) будет рассчитываться каждый раз при запуске кода...
Ответ 8
Да, strlen(ss)
рассчитает длину на каждой итерации. Если вы каким-то образом увеличиваете ss
, а также увеличиваете i
; был бы бесконечный цикл.
Ответ 9
Да, функция strlen()
вызывается каждый раз, когда цикл оценивается.
Если вы хотите повысить эффективность, всегда помните, чтобы сохранить все в локальных переменных... Это займет время, но это очень полезно..
Вы можете использовать код, как показано ниже:
String str="ss";
int l = strlen(str);
for ( int i = 0; i < l ; i++ )
{
// blablabla
}
Ответ 10
Не распространенный в наши дни, но 20 лет назад на 16-битных платформах я бы рекомендовал следующее:
для (char * p = str; * p; p ++) {}
Если ваш компилятор не очень оптимистичен в оптимизации, приведенный выше код может привести к хорошему ассемблеру.
Ответ 11
Да. Тест не знает, что ss не изменяется внутри цикла. Если вы знаете, что это не изменится, я бы написал:
int stringLength = strlen (ss);
for ( int i = 0; i < stringLength; ++ i )
{
// blabla
}
Ответ 12
Да. strlen будет рассчитываться каждый раз, когда я увеличивается.
Если вы не изменили ss с в цикле, значит, не повлияет на логику, иначе это повлияет.
Безопаснее использовать следующий код.
int length = strlen(ss);
for ( int i = 0; i < length ; ++ i )
{
// blabla
}
Ответ 13
Придерживайтесь, даже при идеальных обстоятельствах, черт возьми!
На сегодняшний день (январь 2018 года) и gcc 7.3 и clang 5.0, если вы скомпилируете:
#include <string.h>
void bar(char c);
void foo(const char* __restrict__ ss)
{
for (int i = 0; i < strlen(ss); ++i)
{
bar(*ss);
}
}
Итак, мы имеем:
-
ss
- постоянный указатель.
-
ss
отмечен __restrict__
- Тело цикла никак не может касаться памяти, на которую указывает
ss
(ну, если она не нарушает __restrict__
).
и все же оба компилятора выполняют strlen()
каждую итерацию этого цикла. Удивительно.
Это также означает, что намеки/принятие желаемого за действительное действий @Praetorian и @JaredPar не выделяются.
Ответ 14
ДА, простыми словами.
И нет редких условий, в которых компилятор хочет, как шаг оптимизации, если обнаруживает, что изменений в ss
вообще нет. Но в безопасном состоянии вы должны думать, что это ДА. Есть некоторая ситуация, например, в multithreaded
и управляемой событиями программе, она может стать неисправной, если вы считаете ее НЕТ.
Играйте в безопасное место, так как оно не улучшит сложность программы.
Ответ 15
Да.
strlen()
рассчитывается каждый раз, когда i
увеличивается и не оптимизируется.
Ниже приведен код, почему компилятор не должен оптимизировать strlen()
.
for ( int i = 0; i < strlen(ss); ++i )
{
// Change ss string.
ss[i] = 'a'; // Compiler should not optimize strlen().
}
Ответ 16
Мы можем легко протестировать его:
char nums[] = "0123456789";
size_t end;
int i;
for( i=0, end=strlen(nums); i<strlen(nums); i++ ) {
putchar( nums[i] );
num[--end] = 0;
}
Условие цикла оценивается после каждого повторения перед перезапуском цикла.
Также будьте осторожны с типом, который вы используете для обработки длины строк. он должен быть size_t
, который был определен как unsigned int
в stdio. сравнение и отбрасывание на int
может вызвать серьезную проблему уязвимости.
Ответ 17
Хорошо, я заметил, что кто-то говорит, что он по умолчанию оптимизирован любым "умным" современным компилятором. Кстати, посмотрите на результаты без оптимизации. Я пробовал:
Минимальный код C:
#include <stdio.h>
#include <string.h>
int main()
{
char *s="aaaa";
for (int i=0; i<strlen(s);i++)
printf ("a");
return 0;
}
Мой компилятор: g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3
Команда для генерации кода сборки: 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 done every time
mov eax, ecx
not eax
sub eax, 1
cmp ebx, eax
setb al
test al, al
jne .L3
mov eax, 0
.....
Ответ 18
Разрабатывая ответы на Prætorian, я рекомендую следующее:
for( auto i = strlen(s)-1; i > 0; --i ) {foo(s[i-1];}
-
auto
, потому что вы не хотите заботиться о том, какой тип strlen возвращается. Компилятор С++ 11 (например, gcc -std=c++0x
, а не полностью С++ 11, но автоматически работает) сделает это для вас.
-
i = strlen(s)
, потому что вы хотите сравнить с 0
(см. ниже)
-
i > 0
, потому что сравнение с 0 (немного) быстрее, чем сравнение с любым другим числом.
Недостатком является то, что для доступа к строковым символам необходимо использовать i-1
.