andrystepa
Помогающий
Offline
|
|
« : 11-10-2006 18:58 » |
|
Надо написать код, удаляющий символ из строки. В книге У.Савича прочел, что в классе string есть функция remove, которая удаляет подстроку из строки. Попробовал вызвать функцию так: str.remove(i, 1) // i - позиция, начиная с которой удаляется подстрока
При компиляции g++ выдает, что у str нет члена remove. Может, я ошибся в синтаксисе вызова? Или наличие этой функции зависит от реализации компилятора? В MSDN нашел фукцию - член класса string - erase. На нее g++ не ругается, но и не работает она. Вызываю так: MyString=MyString.erase(i, 1) Ничего из строки не удаляется. Что не так? Помогите.
|
|
|
Записан
|
|
|
|
Finch
Спокойный
Администратор
Offline
Пол:
Пролетал мимо
|
|
« Ответ #1 : 11-10-2006 21:04 » |
|
andrystepa, Полный код приведи. Кстати почему нельзя просто MyString.erase(i, 1); . без присваивания?
|
|
|
Записан
|
Не будите спашяго дракона. Джаффар (Коша)
|
|
|
andrystepa
Помогающий
Offline
|
|
« Ответ #2 : 12-10-2006 08:14 » |
|
andrystepa, Полный код приведи. Кстати почему нельзя просто MyString.erase(i, 1); . без присваивания?
Согласно MSDN функция erase возвращает ссылку на измененную строку. Ее синтаксис такой: basic_string& erase( size_type _Pos = 0, size_type _Count = npos ); Полный код приведу позже - сейчас его нет с собой.
|
|
|
Записан
|
|
|
|
Джон
просто
Администратор
Offline
Пол:
|
|
« Ответ #3 : 12-10-2006 10:08 » |
|
Сорри что не в тему - а что MSDN и для UNIX есть?
|
|
|
Записан
|
Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома. "Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash "Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman "All science is either physics or stamp collecting." Ernest Rutherford "Wer will, findet Wege, wer nicht will, findet Gründe."
|
|
|
Serg79
|
|
« Ответ #4 : 12-10-2006 10:38 » |
|
Да . В MSDN совсем другой класс String и c STL-им классом string он ни как не связан. Что то непонятно, как ты умудряещься програмить в *UNIX и смотреть при этом MSDN или ты комп все время перезагружаещь? А может я чего не понимаю?
|
|
|
Записан
|
|
|
|
andrystepa
Помогающий
Offline
|
|
« Ответ #5 : 12-10-2006 15:36 » |
|
Да . В MSDN совсем другой класс String и c STL-им классом string он ни как не связан. Что то непонятно, как ты умудряещься програмить в *UNIX и смотреть при этом MSDN или ты комп все время перезагружаещь? А может я чего не понимаю? Вообще-то догадаться не сложно Я запускаю Линух в WmWare Workstation, и стараюсь писать код, который компилировался и работал бы в обоих ОСях. Почему смотрю MSDN тоже нетрудно догадаться. В Линуксе аналога MSDN нету, справочников по библиотекам C++, в том числе и по STL у меня не густо - в основном черпаю информацию из книжек, вроде "Язык C++" Страуструпа, то есть из учебников, а она там довольно куцая. Поэтому приходится пользоваться справочной системой Visual C++, MSDN, Borland Developer Studio. Теперь о коде: #include<iostream> #include<cstring> #include<fstream> using namespace std; void delchar(string& str, char c) { for(int i=0; i<str.length; i++) { if(str ==c) str=str.erase(i, 1);} }
........ int main(int argc, char *argv[]) { string Mystr1; char sel, forDel; ifstream filein(argv[1]); ofstream fileout(argv[2]); if(!filein)..... ........ while (!filein.eof()) { filein.getline(Mystr, 82); delchar(Mystr, forDel); fileout<<Mystr; } ....... return 0; }
Извиняюсь, если все писать, длинновато получится
|
|
« Последнее редактирование: 12-10-2006 15:39 от andrystepa »
|
Записан
|
|
|
|
Джон
просто
Администратор
Offline
Пол:
|
|
« Ответ #6 : 12-10-2006 15:43 » |
|
Вообще-то догадаться не сложно Действительно - всё просто как три копейки. И как мы сразу не догадались? зы Лучше бы ты её открыл в "ANSI C++", а ты её открыл специально в UNIX. Вот и результат. Так тебе инфы по STL не хватает?
|
|
|
Записан
|
Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома. "Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash "Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman "All science is either physics or stamp collecting." Ernest Rutherford "Wer will, findet Wege, wer nicht will, findet Gründe."
|
|
|
RXL
|
|
« Ответ #7 : 12-10-2006 16:25 » |
|
andrystepa, "rpm -qd libstdc++-devel" раскажет, где хелпы попрятались.
|
|
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
andrystepa
Помогающий
Offline
|
|
« Ответ #8 : 12-10-2006 18:47 » |
|
Вообще-то догадаться не сложно Действительно - всё просто как три копейки. И как мы сразу не догадались? зы Лучше бы ты её открыл в "ANSI C++", а ты её открыл специально в UNIX. Вот и результат. Так тебе инфы по STL не хватает? Как видите, не хватает! Кстати, так никто и не ответил на вопрос-то!! Правильный или неправильный синтаксис вызова функции erase? И если не правильный, то какой верный? Да и вообще, почему в одной книге функция называется erase, а в другой - remove? Где ответ-то искать. Спасибо RXL хоть он подсказал! А то все рассуждают, почему я в MSDN смотрю - извиняйте, читаю что есть!
|
|
|
Записан
|
|
|
|
RXL
|
|
« Ответ #9 : 12-10-2006 19:05 » |
|
andrystepa, только не обольщайся - MSDN к libstdc++ не прилагается - лишь несколько html-файлов.
|
|
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
andrystepa
Помогающий
Offline
|
|
« Ответ #10 : 12-10-2006 19:28 » |
|
andrystepa, только не обольщайся - MSDN к libstdc++ не прилагается - лишь несколько html-файлов.
А жаль!! Конечно, хотелось бы побольше , но, тем не менее, я все-таки выяснил, что не зря смотрел в MSDN - моя функция delchar в части удаления символа была абсолютно правильной, просто до удаления дело не доходило - оператор if был записан неверно, и символ в строке не находился! ) Приведенный пример я печатал по памяти, а когда пришел домой, и сравнил его с тем, что есть - тут то ошибочка и обнаружилась! Эх, никак не освою Линуксовый дебаггер!
|
|
|
Записан
|
|
|
|
RXL
|
|
« Ответ #11 : 12-10-2006 19:56 » |
|
andrystepa, если ты взялся писать не под win, то стоит сперва смотреть в стандарты, потом в местные доки, что идут с ОС, компилятором или с конкретной библиотекой (если они есть), но в MSDN искать ответ - последнее дело. Это как в повареной книге искать формулы ионообменных реакций: химия одна, а кухня разная.
|
|
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
Джон
просто
Администратор
Offline
Пол:
|
|
« Ответ #12 : 12-10-2006 21:46 » |
|
в MSDN искать ответ - последнее дело. Это как в повареной книге искать формулы ионообменных реакций: химия одна, а кухня разная.
5+
|
|
|
Записан
|
Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома. "Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash "Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman "All science is either physics or stamp collecting." Ernest Rutherford "Wer will, findet Wege, wer nicht will, findet Gründe."
|
|
|
Serg79
|
|
« Ответ #13 : 13-10-2006 04:43 » |
|
Извините что не в тему. Но меня всегда интересовал такой момент. Вот есть код: for(int i=0; i<str.length; i++) { if(str==c) str=str.erase(i, 1);} } как сказывается на быстродействии выполнения, такая конструкция: for( unsigned int i=0; i < str.length(); ++i ) ведь для проверки условия цыкла, все время вызывается метод *.length(). А как мы знаем, расходы на вызов функции приличные. Я обычно предпочитаю выделять дополнительную переменную для этого: unsigned int size = str.length(); for( unsigned int i=0; i < size; ++i ) А может, это просто параноя, компилятор сам оптимизирует код и вместо вызова просто подставит код (хотя навряд ли, ведь все реализации функций находяться в *.lib). Да и подсчет количества символов в строке так же требует цикла. Так что подстановка в блок условия цикла вызова *.length(), я думаю существенно снижает производительность кода. А может сегодня это уже и не актуально с современными гигагерцовыми процессорами?
|
|
|
Записан
|
|
|
|
Антон (LogRus)
|
|
« Ответ #14 : 13-10-2006 05:00 » |
|
по документации: 1. msdn отлично описывает std::string(и прочие объекты STD) если искать описание по нему, а не по String 2. лично я предпочитаю использовать дополнительно документацию по stlport 3. используем и на Win32 и на Linux STLPort, что бы не было platform specific проблем, например удаление из map в VisualStudio возвращает вылидный итератор, а на Linux ничего не возвращает что говорю знаю потому, что участвую в разработке софта компилирующегося по Linux и Windows Serg79, в конструкфиях for гду нужны проверки конца обычно используем такую конструкцию for(int i=0,iend=str.length();i =! iend; ++i) for(iterator it=cont.begin(),it_end=cont.end();it =! it_end; ++it)
|
|
|
Записан
|
Странно всё это....
|
|
|
Serg79
|
|
« Ответ #15 : 13-10-2006 05:18 » |
|
Да LogRus, я это и имею ввиду. Проводить проверку условия цикла именно с константным выражением, а не вызывать для этого все время функцию для получения значения (конечно если это возможно). Но не все так делают. Иногда приходилось видеть такой код: /* C */ for( i=0; i < strlen(str); ++i ) И я не скажу, что так пищут только начинающие. Такое поподается и у людей, которые давно занимаються программированием.
|
|
|
Записан
|
|
|
|
Антон (LogRus)
|
|
« Ответ #16 : 13-10-2006 05:44 » |
|
Serg79, я не думаю что компилер догадается, о том что эта функция всегда возвращает одно итоже между итерациями цикла. хотя size отрабатывает очень быстро, так что можно и забить. провёл пару тестов разницы никакой.
|
|
|
Записан
|
Странно всё это....
|
|
|
Serg79
|
|
« Ответ #17 : 13-10-2006 06:06 » |
|
Serg79, я не думаю что компилер догадается, о том что эта функция всегда возвращает одно итоже между итерациями цикла. хотя size отрабатывает очень быстро, так что можно и забить. провёл пару тестов разницы никакой.
Вот тото и оно, при современных гигагерц может это и не актуально. А для встроенных систем с тактом в 10 МГц и если этот код вызывается очень часто, думаю разницу будет видно хорошо (на осциллографе )
|
|
|
Записан
|
|
|
|
andrystepa
Помогающий
Offline
|
|
« Ответ #18 : 13-10-2006 06:14 » |
|
Сам не проверял, но во всех книгах, что я читал написано, что класс string хранит внутри себя длину строки. То, есть, возможно есть закрытая переменная, хранящая это значение. В таком случае вызов функции length() будет тривиальной подстановкой значения закрытой переменной, и не должен вызывать никаких дополнительных накладных расходов. Ну, если, конечно, компилятор догадается оптимизировать этот вызов (то есть не начнет переключать контекст задачи, для настоящего вызова функции). Что же касаемо MSDN - я полез туда от безысходности, так как не мог найти нужной документации. Более того, как я уже говорил, в разных книгах были разные названия этой самой функции, да и ведь, в конце концов, информация в MSDN оказалась верной и для Линуксового компилятора! Стандарт есть стандарт, хотя частенько его и коверкают разные там мелкомягкие.
|
|
|
Записан
|
|
|
|
Антон (LogRus)
|
|
« Ответ #19 : 13-10-2006 06:34 » |
|
andrystepa, у меня компилятор не доготался в аcсеблерном коде стоит такая строчка call std::string<char ....>::size
|
|
|
Записан
|
Странно всё это....
|
|
|
andrystepa
Помогающий
Offline
|
|
« Ответ #20 : 13-10-2006 07:36 » |
|
В Visual Studio 2005 ассемблерный код для такой строчки : cout<<"The length of string is: "<<mystr.length() <<endl; Выглядит так:
mov esi, esp mov eax, DWORD PTR __imp_?endl@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@1@AAV21@@Z push eax mov edi, esp lea ecx, DWORD PTR _mystr$[ebp] call DWORD PTR __imp_?length@?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QBEIXZ cmp edi, esp call __RTC_CheckEsp mov edi, esp push eax push OFFSET ??_C@_0BK@MDOLNBG@The?5length?5of?5string?5is?3?5?$AA@ mov ecx, DWORD PTR __imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A push ecx call ??$?6U?$char_traits@D@std@@@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@0@AAV10@PBD@Z ; std::operator<<<std::char_traits<char> > add esp, 8 mov ecx, eax call DWORD PTR __imp_??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@I@Z cmp edi, esp call __RTC_CheckEsp mov ecx, eax call DWORD PTR __imp_??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z cmp esi, esp call __RTC_CheckEsp
Как видно, тут достаточно инструкций call, каждая из которых вызывает сохранение контекста задачи. Вызов же строчки: len=mystr.length();
выглядит так:
mov esi, esp lea ecx, DWORD PTR _mystr$[ebp] call DWORD PTR __imp_?length@?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QBEIXZ cmp esi, esp call __RTC_CheckEsp mov DWORD PTR _len$[ebp], eax То есть в цикле компилятор вызывет функцию полностью, а это значит, что никакой оптимизации нет! Правда компилировал я с установками по умолчанию.
|
|
|
Записан
|
|
|
|
Serg79
|
|
« Ответ #21 : 13-10-2006 08:36 » |
|
Как видно, тут достаточно инструкций call, каждая из которых вызывает сохранение контекста задачи. Ты зря применяешь термин (контекст задачи) при разговоре об инструкции ассемблера call. Данная команда не переключает контекст задачи в процессорах семейства i80х86, она всего то сохраняет адрес следующей после нее команды в стеке и загружает в регистр EIP новый адрес с которого начнут выполняться команды. А команда ret извлекает из стека значение (для i80x86 32-разрядное) и помешает его в регистр указателя команды EIP. Вот и все и некакого переключения контекста задачи. Переключение контекста задачи необходимо для реализации аппаратной поддержки со стороны процессора паралельных вычислений, т.н. многозадочности. Данное переключение ведет вообще к смене сегмента состояния задачи ( TSS), что в свою очередь приводит и к смене виртуального адресного пространства. Это происходит например когда ОС останавливает выполнение твоей программы, забирает у твоей программы центральный процессор и отдает его скажем Блокноту и разрешает выполнение его кода, и так по кругу для всех потоков сушествующих в системе. Так что термин (контекст задачи), andrystepa ты не подходящий выбрал.
|
|
|
Записан
|
|
|
|
andrystepa
Помогающий
Offline
|
|
« Ответ #22 : 13-10-2006 12:52 » |
|
Да, конечно, термин неверный. Просто я спешил на работу, поэтому приплел первое, что пришло на ум. Извиняюсь. Вообще говоря, сама по себе инструкция call не отнимает много времени у процессора. Ведь он только сохраняет адрес возврата, и делает jmp. В основном время отнимает загрузка переменных в стек ( для Линукса обычно в регистры) и очистка стека (регистров). Да и перезагрузка конвейра процессора при дальнем jmp тоже время отнимает. P.S. Приведенный мною в предыдущем посте пример не совсем корректен! Все-таки прямой вызов функции length(), и вызов ее в условии цикла for могут компилятором интертрепироваться по-разному. При просмотре в дебаггере от VS, функция length() состояла из одной строчки кода: return length; Корректнее было, конечно дизассемблировать пример с циклом. Приду домой - попробую!
|
|
|
Записан
|
|
|
|
andrystepa
Помогающий
Offline
|
|
« Ответ #23 : 13-10-2006 15:23 » |
|
К сожалению, компилятор у Visual C++ оказался глуповатым оптимизация проверки условия цикла при стандартных опциях компилятора хреновая! В ассемблерном листинге строчку: : for(i=0; i<mystr.length(); i++) if(mystr[i]==tofind) num++;
Мелкомягкий компилятор развернул как:
mov DWORD PTR _i$[ebp], 0 jmp SHORT $LN4@main $LN3@main: mov eax, DWORD PTR _i$[ebp] add eax, 1 mov DWORD PTR _i$[ebp], eax $LN4@main: mov esi, esp lea ecx, DWORD PTR _mystr$[ebp] call DWORD PTR __imp_?length@?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QBEIXZ cmp esi, esp call __RTC_CheckEsp cmp DWORD PTR _i$[ebp], eax jae SHORT $LN2@main mov esi, esp mov eax, DWORD PTR _i$[ebp] push eax lea ecx, DWORD PTR _mystr$[ebp] call DWORD PTR __imp_??A?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAEAADI@Z cmp esi, esp call __RTC_CheckEsp movsx ecx, BYTE PTR [eax] movsx edx, BYTE PTR _tofind$[ebp] cmp ecx, edx jne SHORT $LN1@main mov eax, DWORD PTR _num$20171[ebp] add eax, 1 mov DWORD PTR _num$20171[ebp], eax $LN1@main:
собственно таким вот кривым способом закодированы следующие действия: вычисление длины строки ( 1 раз сделать call на функцию length) и загрузка ее в ecx; создание цикла для проверки всех символов строки, пока ecx(куда загрузили длину строки) не станет равным 0; К сожалению я не помню, может кто подскажет опцию компилятора g++, создающую ассемблерный листинг?
|
|
« Последнее редактирование: 13-10-2006 15:47 от Алексей1153 »
|
Записан
|
|
|
|
Finch
Спокойный
Администратор
Offline
Пол:
Пролетал мимо
|
|
« Ответ #24 : 13-10-2006 16:05 » |
|
andrystepa, Не ожидай от компилятора большего, чем он может сделать. Ты можеш один и тот же кусок текста написать тысячью способами. Компилятор не должен знать все эти тысячи способов, он использует универсальный (как правило более громоздкий способ). Иногда, то что выглядит элегантно в сишном коде, преврашается в машинном коде в сплошное убожество. Насчет разных оптимизаций в коде самим компилятором, у меня есть некоторая аллергия. Лет восемь назад писал код, и затем долго искал ошибку в 10 строчках. Оказалось, что компилятор с оптимизировал код и всю мою логику послал подальше. Ситуация была такая: У меня вычислялся массив чисел. Следуюший элемент массива зависил от предыдушего. Я сделал цикл по возрастаюшей. Компилятор сумничал, сделал цикл ниспадаюший. Так как ниспадаюший цикл работает чуть быстрее.
|
|
|
Записан
|
Не будите спашяго дракона. Джаффар (Коша)
|
|
|
RXL
|
|
« Ответ #25 : 13-10-2006 20:53 » |
|
Но не все так делают. Иногда приходилось видеть такой код: /* C */ for( i=0; i < strlen(str); ++i ) Это офтоп: в теме обсуждается объект string, а не char*! Попрошу не смешивать C и C++! Для разясянения: тут проверяется длина строки, кторая _может_ изменяться в цикле. Конечно, если она не изменяется, то это лишнее время процессора, но некоторая экономия времени программиста. На мой взгляд такое подход, в случае непреднамереного изменения строки, с большей вероятностью приведет к ошибке при отладке, а не в процессе экспуатации.
|
|
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
Serg79
|
|
« Ответ #26 : 16-10-2006 09:31 » |
|
собственно таким вот кривым способом закодированы следующие действия: andrystepa, ну ты даещь. Почему кривым способом, самый что ни есть нормальный способ. Я уверен на 99%, что и g++ так же сделает (ну может местами только поменяет разные блоки). вычисление длины строки ( 1 раз сделать call на функцию length) и загрузка ее в ecx; создание цикла для проверки всех символов строки, пока ecx(куда загрузили длину строки) не станет равным 0; Ты бы лучше не фантазировал, а попытался бы разобраться в ассемблерном листинге: : for(i=0; i<mystr.length(); i++) if(mystr[i]==tofind) num++;
Мелкомягкий компилятор развернул как: /*******************************************/ т.к. в x86 регистров мало, все переменные храняться в памяти а не в регистрах
регистр ebp содержит смещение на участок в стеке выделенный для внутренних переменных функции. Обычно в начале функции мы видем что то подобное: push ebp mov ebp, esp sub esp, 4 // выделяется для int (4 байта) /*******************************************/
/******************************************/ инициализация цикла i = 0; mov DWORD PTR _i$[ebp], 0 jmp SHORT $LN4@main // уходим к проверке условия цикла /*****************************************/
$LN3@main: /*****************************************/ приращение цикла i++; // через ebp происходит косвенная адресация к i mov eax, DWORD PTR _i$[ebp] // загружаем i в eax add eax, 1 // увеличиваем на 1 mov DWORD PTR _i$[ebp], eax // отправляем обратно в память /*****************************************/
$LN4@main: /*****************************************/ условие цикла i < mystr.length(); // подготовка к вызову функции mystr.length(); mov esi, esp lea ecx, DWORD PTR _mystr$[ebp] // вызов функции mystr.length(); call DWORD PTR __imp_?length@?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QBEIXZ cmp esi, esp // отладочная функция в VC++ 6.0 для этих целей используется __chkesp call __RTC_CheckEsp cmp DWORD PTR _i$[ebp], eax // дословно: i - mystr.length() jae SHORT $LN2@main // уйти если не выполняется условие, установлен флаг нуля /******************************************/
/******************************************/ сдесь проверяется условие if(mystr[i]==tofind) mov esi, esp // выполняется функция mystr[i] mov eax, DWORD PTR _i$[ebp] // берем i push eax // заталкиваем в стек lea ecx, DWORD PTR _mystr$[ebp] // вызываем перегруженный оператор [] для класса string call DWORD PTR __imp_??A?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAEAADI@Z cmp esi, esp call __RTC_CheckEsp // кстати, сдесь мы видим, что перегруженный оператор [] возвращает не значение // соответствующее индесу i, а указатель на это значение. // // В принципе так и должно быть, что бы можно было использовать такую конструкцию // mystr[i] = 'A'; movsx ecx, BYTE PTR [eax] // получаем значение mystr[i] movsx edx, BYTE PTR _tofind$[ebp] // получаем значение tofind cmp ecx, edx // сравниваем их jne SHORT $LN1@main // переходим если не установлен флаг нуля /*********************************************/
/*********************************************/ выполняем оператор num++; // сдесь я думаю все понятно, если нет смотри i++ mov eax, DWORD PTR _num$20171[ebp] add eax, 1 mov DWORD PTR _num$20171[ebp], eax /*********************************************/
$LN1@main: /*******************************************/ Это то, что ты забыл нам показать jmp SHORT $LN3@main /*******************************************/
$LN2@main: уйти
Так что andrystepa, объясни что ты имел в виду под этими словами: К сожалению, компилятор у Visual C++ оказался глуповатым оптимизация проверки условия цикла при стандартных опциях компилятора хреновая!
|
|
« Последнее редактирование: 17-12-2007 16:58 от Алексей1153++ »
|
Записан
|
|
|
|
andrystepa
Помогающий
Offline
|
|
« Ответ #27 : 31-10-2006 17:44 » |
|
Так что andrystepa, объясни что ты имел в виду под этими словами: К сожалению, компилятор у Visual C++ оказался глуповатым оптимизация проверки условия цикла при стандартных опциях компилятора хреновая! Вообще-то ты и сам уже объяснил, что я имел ввиду под этими словами. Если посмотришь выше, речь шла о том, вызывает ли компилятор функцию lenght() на каждой итерации внутри цикла, или вычисляет длину строки один раз. В приведенном тобою листинге: $LN3@main: /*****************************************/ приращение цикла i++; // через ebp происходит косвенная адресация к i mov eax, DWORD PTR _i$[ebp] // загружаем i в eax add eax, 1 // увеличиваем на 1 mov DWORD PTR _i$[ebp], eax // отправляем обратно в память /*****************************************/
$LN4@main: /*****************************************/
условие цикла i < mystr.length(); // подготовка к вызову функции mystr.length(); mov esi, esp lea ecx, DWORD PTR _mystr$[ebp] // вызов функции mystr.length(); call DWORD PTR __imp_?length@?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QBEIXZ cmp esi, esp // отладочная функция в VC++ 6.0 для этих целей используется __chkesp call __RTC_CheckEsp cmp DWORD PTR _i$[ebp], eax // дословно: i - mystr.length() jae SHORT $LN2@main // уйти если не выполняется условие, установлен флаг нуля ............ ........... movsx ecx, BYTE PTR [eax] // получаем значение mystr[i] movsx edx, BYTE PTR _tofind$[ebp] // получаем значение tofind cmp ecx, edx // сравниваем их jne SHORT $LN1@main // переходим если не установлен флаг нуля /*********************************************/ ......... ......... $LN1@main: /*******************************************/ Это то, что ты забыл нам показать jmp SHORT $LN3@main //переходим на i++ - приращение счетчика цикла
совершенно ясно видно, что mystr.length() вызывается на каждной итерации, и, более того, как известно, при оптимизации цикла желательно, чтобы счетчик цикла не увеличивался, а уменьшался. Тогда можно избежать большой инструкции cmp DWORD PTR _i$[ebp], eax // дословно: i - mystr.length() а ограничиться лишь инструкцией jz $LN2@main после i--. Вот в этом то и хреновость оптимизации.
|
|
|
Записан
|
|
|
|
Антон (LogRus)
|
|
« Ответ #28 : 01-11-2006 05:51 » |
|
andrystepa, я думаю ты не прав. Компилятор не знает о побочных эффектах внутри итерации цикла, т.е. возможно функцию внутри цикла надо вызывать именно при монотонно возрастающем i, иначе логика развалится, в большенстве случаев это так кроме того не известно, что происходит внутри функции length, если она конечно не inline я лично вполне согласен с компилятором боюсь представить даже, что бы могло произойти при с некоторыми алгоритмами при оптимизации описанной тобой Это же ранняя оптимизация.
|
|
|
Записан
|
Странно всё это....
|
|
|
Антон (LogRus)
|
|
« Ответ #29 : 01-11-2006 06:29 » |
|
andrystepa,зря ты ругаешь оптимизатор string s("test_test_test"); 00401833 lea eax,[esp+0Bh] 00401837 push eax 00401838 push offset string "test_test_test" (427190h) 0040183D lea ecx,[esp+18h] 00401841 call stlp_std::basic_string<char,stlp_std::char_traits<char>,stlp_std::allocator<char> >::basic_string<char,stlp_std::char_traits<char>,stlp_std::allocator<char> > (401780h)
for(std::string::iterator i=s.begin(),ie =s.end();i!=ie;++i) 00401846 mov eax,dword ptr [esp+24h] 0040184A lea ecx,[esp+20h] 0040184E cmp eax,ecx 00401850 mov dword ptr [esp+34h],0 00401858 lea esi,[esp+10h] 0040185C je main+52h (401862h) 0040185E mov esi,dword ptr [esp+10h] 00401862 mov edi,dword ptr [esp+20h] 00401866 cmp esi,edi 00401868 je main+7Ah (40188Ah) 0040186A lea ebx,[ebx] { cout << *i; 00401870 mov dl,byte ptr [esi] 00401872 mov byte ptr [esp+0Ch],dl 00401876 mov eax,dword ptr [esp+0Ch] 0040187A push eax 0040187B mov ecx,offset stlp_std::cout (42F218h) 00401880 call stlp_std::basic_ostream<char,stlp_std::char_traits<char> >::_M_put_char (401360h) 00401885 inc esi 00401886 cmp esi,edi 00401888 jne main+60h (401870h) } cout << "\n"; 0040188A push offset string "\n" (42718Ch) 0040188F mov ecx,offset stlp_std::cout (42F218h) 00401894 call stlp_std::basic_ostream<char,stlp_std::char_traits<char> >::_M_put_nowiden (401580h) return 0;
обрати внимание на строки 00401885 inc esi 00401886 cmp esi,edi 00401888 jne main+60h (401870h) никакой рабты с память одни регистры, сдвиг итератора и проверка правильной код правильно оптимизируется
|
|
|
Записан
|
Странно всё это....
|
|
|
|