Я не уверен, может ли следующий код вызвать избыточные вычисления или он специфичен для компилятора?
for (int i = 0; i < strlen(ss); ++i)
{
// blabla
}
Будет ли вычисляться strlen()
каждый раз, когда i
увеличивается?
Я не уверен, может ли следующий код вызвать избыточные вычисления или он специфичен для компилятора?
for (int i = 0; i < strlen(ss); ++i)
{
// blabla
}
Будет ли вычисляться strlen()
каждый раз, когда i
увеличивается?
Да, strlen()
будет оцениваться на каждой итерации. Возможно, что при идеальных обстоятельствах оптимизатор может определить, что значение не изменится, но я лично не буду на это полагаться.
Я бы сделал что-то вроде
for (int i = 0, n = strlen(ss); i < n; ++i)
или, возможно,
for (int i = 0; ss[i]; ++i)
пока строка не изменит длину во время итерации. Если это возможно, вам придется либо вызывать strlen()
каждый раз, либо обрабатывать его с помощью более сложной логики.
Да, каждый раз, когда вы используете цикл. Затем он будет каждый раз вычислять длину строки. поэтому используйте его так:
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;
}
Хороший компилятор не может рассчитывать его каждый раз, но я не думаю, что вы можете быть уверены, что каждый компилятор делает это.
В дополнение к этому компилятор должен знать, что strlen (ss) не изменяется. Это справедливо только в том случае, если ss не изменяется в for-loop.
Например, если вы используете функцию read-only для ss в for-loop, но не объявляете ss-параметр как const, компилятор даже не может знать, что ss не изменяется в цикле и имеет значение вычислять strlen (ss) на каждой итерации
Если 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
}
Формально да, strlen()
ожидается для каждой итерации.
В любом случае я не хочу отрицать возможность существования некоторой умной оптимизации компилятора, которая оптимизирует любой последовательный вызов strlen() после первого.
Предикатный код целиком будет выполняться на каждой итерации цикла for
. Чтобы запомнить результат вызова strlen(ss)
, компилятор должен знать, что по крайней мере
strlen
свободна от побочных эффектовss
, не изменяется на время циклаКомпилятор не знает ни одной из этих вещей и, следовательно, не может спокойно запоминать результат первого вызова
Да, strlen (ss) будет рассчитываться каждый раз при запуске кода...
Да, strlen(ss)
рассчитает длину на каждой итерации. Если вы каким-то образом увеличиваете ss
, а также увеличиваете i
; был бы бесконечный цикл.
Да, функция strlen()
вызывается каждый раз, когда цикл оценивается.
Если вы хотите повысить эффективность, всегда помните, чтобы сохранить все в локальных переменных... Это займет время, но это очень полезно..
Вы можете использовать код, как показано ниже:
String str="ss";
int l = strlen(str);
for ( int i = 0; i < l ; i++ )
{
// blablabla
}
Не распространенный в наши дни, но 20 лет назад на 16-битных платформах я бы рекомендовал следующее:
для (char * p = str; * p; p ++) {}
Если ваш компилятор не очень оптимистичен в оптимизации, приведенный выше код может привести к хорошему ассемблеру.
Да. Тест не знает, что ss не изменяется внутри цикла. Если вы знаете, что это не изменится, я бы написал:
int stringLength = strlen (ss);
for ( int i = 0; i < stringLength; ++ i )
{
// blabla
}
Да. strlen будет рассчитываться каждый раз, когда я увеличивается.
Если вы не изменили ss с в цикле, значит, не повлияет на логику, иначе это повлияет.
Безопаснее использовать следующий код.
int length = strlen(ss);
for ( int i = 0; i < length ; ++ i )
{
// blabla
}
На сегодняшний день (январь 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 не выделяются.
ДА, простыми словами.
И нет редких условий, в которых компилятор хочет, как шаг оптимизации, если обнаруживает, что изменений в ss
вообще нет. Но в безопасном состоянии вы должны думать, что это ДА. Есть некоторая ситуация, например, в multithreaded
и управляемой событиями программе, она может стать неисправной, если вы считаете ее НЕТ.
Играйте в безопасное место, так как оно не улучшит сложность программы.
Да.
strlen()
рассчитывается каждый раз, когда i
увеличивается и не оптимизируется.
Ниже приведен код, почему компилятор не должен оптимизировать strlen()
.
for ( int i = 0; i < strlen(ss); ++i )
{
// Change ss string.
ss[i] = 'a'; // Compiler should not optimize strlen().
}
Мы можем легко протестировать его:
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
может вызвать серьезную проблему уязвимости.
Хорошо, я заметил, что кто-то говорит, что он по умолчанию оптимизирован любым "умным" современным компилятором. Кстати, посмотрите на результаты без оптимизации. Я пробовал:
Минимальный код 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
.....
Разрабатывая ответы на 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
.