Будет ли strlen вычисляться несколько раз при использовании в состоянии цикла?



Я не уверен, что следующий код может вызвать избыточные вычисления, или это зависит от компилятора?



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


будет strlen() вычисляется каждый раз, когда i увеличивается?

568   18  

18 ответов:

да 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] != ''; i++)
{
//Something;
}

В приведенном выше коде str[i] проверяет только один конкретный символ в строке в местоположении i каждый раз, когда цикл начинает цикл, таким образом, это займет меньше памяти и является более эффективным.

посмотреть этот ссылке для получения дополнительной информации.

в коде ниже каждый раз, когда цикл выполняется strlen будет считать длина всей строки, которая менее эффективна, занимает больше времени и занимает больше памяти.

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

хороший компилятор не может вычислить его каждый раз, но я не думаю, что вы можете быть уверены, что каждый компилятор это делает.

кроме того, компилятор должен знать, что strlen(ss) не меняется. Это верно только в том случае, если ss не изменяется в for петли.

например, если вы используете только для чтения, функция ss in for цикл, но не объявляйте ss - параметр as const компилятор не может даже знать, что ss не изменяется в цикл и должен вычислить strlen(ss) на каждой итерации.

если ss типа const char * и вы не отбрасываете constness в цикле компилятор может вызывать только strlen раз, если оптимизация включена. Но это, конечно, не то поведение, на которое можно рассчитывать.

вы должны сохранить strlen результат в переменной и использовать эту переменную в цикле. Если вы не хотите создавать дополнительную переменную, в зависимости от того, что вы делаете, вы можете быть ale, чтобы уйти с обратным циклом для итерации назад.

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) вызов компилятора должен был бы знать, что по крайней мере

  1. функции strlen был побочный эффект бесплатно
  2. память, на которую указывает ss не изменяется в течение всего цикла

компилятор не знает ни одной из этих вещей и, следовательно, не может безопасно запомнить результат первого звоните

Да strlen(ss) рассчитаем длину на каждой итерации. Если вы увеличиваете ss каким-то образом, а также увеличивая i; там будет бесконечный цикл.

да

да strlen(ss) будет вычисляться каждый раз при запуске кода.

не распространено в настоящее время, но 20 лет назад на 16-битных платформах, я бы рекомендовал этот:

for ( 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
}

Arrgh, это будет, даже при идеальных обстоятельствах, черт возьми!

на сегодняшний день (январь 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;
}

условие цикла вычисляется после каждого повторения, перед перезапуском цикла .

Также будьте осторожны с типом, который вы используете для обработки длины строк . он должен быть!--1--> который был определен как 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'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
     .....

развивая ответ Преториана, я рекомендую следующее:

for( auto i = strlen(s)-1; i > 0; --i ) {foo(s[i-1];}
  • auto потому что вы не хотите заботиться о том, какой тип возвращает функция strlen. Компилятор C++11 (например,gcc -std=c++0x, не полностью C++11, но авто типы работают) сделает это за вас.
  • i = strlen(s) потому что вы хотите сравнить в 0 (см. ниже)
  • i > 0 потому что сравнение с 0 (немного) быстрее, чем сравнение с любым другим число.

недостатком является то, что вы должны использовать i-1 для доступа к строковым символам.

Comments

    Ничего не найдено.