Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1] 2  Все   Вниз
  Печать  
Автор Тема: Удаление символа из строки  (Прочитано 67681 раз)
0 Пользователей и 4 Гостей смотрят эту тему.
andrystepa
Помогающий

ru
Offline 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
Спокойный
Администратор

il
Offline Offline
Пол: Мужской
Пролетал мимо


« Ответ #1 : 11-10-2006 21:04 » 

andrystepa, Полный код приведи. Кстати почему нельзя просто MyString.erase(i, 1); . без присваивания?
Записан

Не будите спашяго дракона.
             Джаффар (Коша)
andrystepa
Помогающий

ru
Offline 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
);
Полный код приведу позже - сейчас его нет с собой.
Записан
Джон
просто
Администратор

de
Offline 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
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #4 : 12-10-2006 10:38 » 

Да Улыбаюсь .

В MSDN совсем другой класс String и c STL-им классом string он ни как не связан.

Что то непонятно, как ты умудряещься програмить в *UNIX и смотреть при этом MSDN или ты комп все время перезагружаещь?   Класс!

А может я чего не понимаю?  Здесь была моя ладья...
Записан
andrystepa
Помогающий

ru
Offline 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 » Записан
Джон
просто
Администратор

de
Offline 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
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #7 : 12-10-2006 16:25 » 

andrystepa, "rpm -qd libstdc++-devel" раскажет, где хелпы попрятались.
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
andrystepa
Помогающий

ru
Offline Offline

« Ответ #8 : 12-10-2006 18:47 » 

Вообще-то догадаться не сложно Ага

Действительно - всё просто как три копейки. И как мы сразу не догадались?

зы Лучше бы ты её открыл в "ANSI C++", а ты её открыл специально в UNIX. Вот и результат.
Так тебе инфы по STL не хватает?
Как видите, не хватает! Кстати, так никто и не ответил на вопрос-то!! Правильный или неправильный синтаксис вызова функции erase? И если не правильный, то какой верный?
Да и вообще, почему в одной книге функция называется erase, а в другой - remove? Где ответ-то искать. Спасибо RXL хоть он подсказал! А то все рассуждают, почему я в MSDN смотрю - извиняйте, читаю что есть!
Записан
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #9 : 12-10-2006 19:05 » 

andrystepa, только не обольщайся - MSDN к libstdc++ не прилагается - лишь несколько html-файлов.
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
andrystepa
Помогающий

ru
Offline Offline

« Ответ #10 : 12-10-2006 19:28 » 

andrystepa, только не обольщайся - MSDN к libstdc++ не прилагается - лишь несколько html-файлов.
А жаль!! Конечно, хотелось бы побольше А черт его знает..., но, тем не менее, я все-таки выяснил, что не зря смотрел в MSDN - моя функция delchar в части удаления символа была абсолютно правильной, просто до удаления дело не доходило - оператор if был записан неверно, и символ в строке не находился! Улыбаюсь) Приведенный пример я печатал по памяти, а когда пришел домой, и сравнил его с тем, что есть - тут то ошибочка и обнаружилась! Эх, никак не освою Линуксовый дебаггер! Жаль
Записан
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #11 : 12-10-2006 19:56 » 

andrystepa, если ты взялся писать не под win, то стоит сперва смотреть в стандарты, потом в местные доки, что идут с ОС, компилятором или с конкретной библиотекой (если они есть), но в MSDN искать ответ - последнее дело. Это как в повареной книге искать формулы ионообменных реакций: химия одна, а кухня разная.
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Джон
просто
Администратор

de
Offline 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
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #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)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #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
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #15 : 13-10-2006 05:18 » 

Да LogRus, я это и имею ввиду. Проводить проверку условия цикла именно с константным выражением, а не вызывать для этого все время функцию для получения значения (конечно если это возможно).

Но не все так делают. Иногда приходилось видеть такой код:
Код:
/* C */
for( i=0; i < strlen(str); ++i )
И я не скажу, что так пищут только начинающие. Такое поподается и у людей, которые давно занимаються программированием.
Записан
Антон (LogRus)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #16 : 13-10-2006 05:44 » 

Serg79, я не думаю что компилер догадается, о том что эта функция всегда возвращает одно итоже между итерациями цикла.
хотя size отрабатывает очень быстро, так что можно и забить. провёл пару тестов разницы никакой.
Записан

Странно всё это....
Serg79
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #17 : 13-10-2006 06:06 » 

Serg79, я не думаю что компилер догадается, о том что эта функция всегда возвращает одно итоже между итерациями цикла.
хотя size отрабатывает очень быстро, так что можно и забить. провёл пару тестов разницы никакой.
Вот тото и оно, при современных гигагерц может это и не актуально. А для встроенных систем с тактом в 10 МГц и если этот код вызывается очень часто, думаю разницу будет видно хорошо (на осциллографе  Да-да )
Записан
andrystepa
Помогающий

ru
Offline Offline

« Ответ #18 : 13-10-2006 06:14 » 

Сам не проверял, но во всех книгах, что я читал написано, что класс string хранит внутри себя длину строки. То, есть, возможно есть закрытая переменная, хранящая это значение. В таком случае вызов функции length() будет тривиальной подстановкой значения закрытой переменной, и не должен вызывать никаких дополнительных накладных расходов. Ну, если, конечно, компилятор догадается оптимизировать этот вызов (то есть не начнет переключать контекст задачи, для настоящего вызова функции).
Что же касаемо MSDN - я полез туда от безысходности, так как не мог найти нужной документации. Более того, как я уже говорил, в разных книгах были разные названия этой самой функции, да и ведь, в конце концов, информация в MSDN оказалась верной и для Линуксового компилятора! Стандарт есть стандарт, хотя частенько его и коверкают разные там мелкомягкие. Ага
Записан
Антон (LogRus)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #19 : 13-10-2006 06:34 » 

andrystepa, у меня компилятор не доготался в аcсеблерном коде стоит такая строчка call std::string<char ....>::size
Записан

Странно всё это....
andrystepa
Помогающий

ru
Offline 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
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #21 : 13-10-2006 08:36 » 

Как видно, тут достаточно инструкций call, каждая из которых вызывает сохранение контекста задачи.
Ты зря применяешь термин (контекст задачи) при разговоре об инструкции ассемблера call.
Данная команда не переключает контекст задачи в процессорах семейства i80х86, она всего то сохраняет адрес следующей после нее команды в стеке и загружает в регистр EIP новый адрес с которого начнут выполняться команды. А команда ret извлекает из стека значение (для i80x86 32-разрядное) и помешает его в регистр указателя команды EIP. Вот и все и некакого переключения контекста задачи.

Переключение контекста задачи необходимо для реализации аппаратной поддержки со стороны процессора паралельных вычислений, т.н. многозадочности. Данное переключение ведет вообще к смене сегмента состояния задачи (TSS), что в свою очередь приводит и к смене виртуального адресного пространства. Это происходит например когда ОС останавливает выполнение твоей программы, забирает у твоей программы центральный процессор и отдает его скажем Блокноту и разрешает выполнение его кода, и так по кругу для всех потоков сушествующих в системе.

Так что термин (контекст задачи), andrystepa ты не подходящий выбрал.  Не-а...
Записан
andrystepa
Помогающий

ru
Offline Offline

« Ответ #22 : 13-10-2006 12:52 » 

Да, конечно, термин неверный. Просто я спешил на работу, поэтому приплел первое, что пришло на ум. Извиняюсь.
Вообще говоря, сама по себе инструкция call не отнимает много времени у процессора. Ведь он только сохраняет адрес возврата, и делает jmp. В основном время отнимает загрузка переменных в стек ( для Линукса обычно в регистры) и очистка стека (регистров). Да и перезагрузка конвейра процессора при дальнем jmp тоже время отнимает.
P.S. Приведенный мною в предыдущем посте пример не совсем корректен! Все-таки прямой вызов функции length(), и вызов ее в условии цикла for могут компилятором интертрепироваться по-разному. При просмотре в дебаггере от VS, функция length() состояла из одной строчки кода:
return length;
Корректнее было, конечно дизассемблировать пример с циклом. Приду домой - попробую!
Записан
andrystepa
Помогающий

ru
Offline 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
Спокойный
Администратор

il
Offline Offline
Пол: Мужской
Пролетал мимо


« Ответ #24 : 13-10-2006 16:05 » new

andrystepa, Не ожидай от компилятора большего, чем он может сделать. Ты можеш один и тот же кусок текста написать тысячью способами. Компилятор не должен знать все эти тысячи способов, он использует универсальный (как правило более громоздкий способ). Иногда, то что выглядит элегантно в сишном коде, преврашается в машинном коде в сплошное убожество.
Насчет разных оптимизаций в коде самим компилятором, у меня есть некоторая аллергия. Лет восемь назад писал код, и затем долго искал ошибку в 10 строчках. Оказалось, что компилятор с оптимизировал код и всю мою логику послал подальше.
Ситуация была такая: У меня вычислялся массив чисел. Следуюший элемент массива зависил от предыдушего. Я сделал цикл по возрастаюшей. Компилятор сумничал, сделал цикл ниспадаюший.  Так как ниспадаюший цикл работает чуть быстрее.
Записан

Не будите спашяго дракона.
             Джаффар (Коша)
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #25 : 13-10-2006 20:53 » 

Но не все так делают. Иногда приходилось видеть такой код:
Код:
/* C */
for( i=0; i < strlen(str); ++i )
Это офтоп: в теме обсуждается объект string, а не char*! Попрошу не смешивать C  и C++!
Для разясянения: тут проверяется длина строки, кторая _может_ изменяться в цикле. Конечно, если она не изменяется, то это лишнее время процессора, но некоторая экономия времени программиста. На мой взгляд такое подход, в случае непреднамереного изменения строки, с большей вероятностью приведет к ошибке при отладке, а не в процессе экспуатации.
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Serg79
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #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
Помогающий

ru
Offline 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)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #28 : 01-11-2006 05:51 » 

andrystepa, я думаю ты не прав.
Компилятор не знает о побочных эффектах внутри итерации цикла, т.е. возможно функцию внутри цикла надо вызывать именно при монотонно возрастающем i, иначе логика развалится, в большенстве случаев это так
кроме того не известно, что происходит внутри функции length, если она конечно не inline

я лично вполне согласен с компилятором Улыбаюсь боюсь представить даже, что бы могло произойти при с некоторыми алгоритмами при оптимизации описанной тобой Улыбаюсь
Это же ранняя оптимизация. Улыбаюсь
Записан

Странно всё это....
Антон (LogRus)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #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)

никакой рабты с память одни регистры, сдвиг итератора и проверка
правильной код правильно оптимизируется Улыбаюсь
Записан

Странно всё это....
Страниц: [1] 2  Все   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines