|
Alf
Гость
|
|
« Ответ #1 : 05-12-2005 12:48 » |
|
Ну почему же ужасы? Ну немного погорячились, не без того. Старичка С++ перехвалили немного, попугали ужасной сложностью Java.
А в целом сумбурненькая статейка, конечно. Блочную структуру за уши притянули зачем-то...
|
|
|
Записан
|
|
|
|
Olegator
|
|
« Ответ #2 : 05-12-2005 13:07 » |
|
Блочную структуру за уши притянули зачем-то...
Это как это?
|
|
|
Записан
|
|
|
|
npak
|
|
« Ответ #3 : 05-12-2005 13:17 » |
|
А я думал, что в Си++ запретили переход внутрь блока в switch ... Век живи - век учись Автор слукавил, сведя "ужасные" особенности Си++ к особенностям инструкции switch. Основные трудности с Си++, имхо, кроются в том, что для любого большого проекта есть проблема отладки утечек памяти и ошибок доступа к памяти. Я понимаю, что на встроенном девайсе сборщик мусора кажется избыточным, но какой, скажите, смысл, самому управлять объектами на трёх-гигагерцевом сервере с четырьмя гигабайтами памяти? Ещё одна засада -- сделать софт, который компилируется и работает на нескольких платформах (скажем, Windows/Linux/BSD) без особых усилий со стороны разработчика. На Си++ это очень нетривиальная задача. Вот две главные причины, по которым я предпочитаю использовать Java, а не Си++.
|
|
|
Записан
|
|
|
|
Alf
Гость
|
|
« Ответ #4 : 05-12-2005 13:26 » |
|
А Вот так (цитирую статью): Для начала маленькое отступление. В языках программирования высокого уровня обычно используют блочную структуру программы. Блоки позволяют, например, локализовать переменные, перекладывая заботы об управлении памятью на компилятор. Так, память, выделенная под переменные, объявленные в начале блока, обычно автоматически освобождается перед завершением блока. Естественно, в основе такого принципа лежит простой механизм стека... Ну и так далее - все переписывать не имеет смысла. Затем идет пример кода, от которого мы должны по замыслу автора упасть в обморок: switch (len % 8) { case 0: { do { HAL_IO_PORT = *pSource++; case 7: { HAL_IO_PORT = *pSource++; } ... case 1: { HAL_IO_PORT = *pSource++; } } while (--n > 0); } } Ну и далее по тексту: Для понимания, что это – натуральный ужас, вам вовсе не надо знать C++ или С. Просто следите за соблюдением правила вложенности блоков. Видите? Блоки, которые, по логике вещей, должны быть на одном уровне иерархии (они начинаются со слова case), почему-то на одном уровне не находятся. Зачем-то нам сначала рассказывают прописные истины насчет размещения локальных в блоке переменных, а потом пугают составным оператором, в блоке которого ни одной локальной переменной не объявлено. Так что непонятно, к чему эти разглагольствования насчет механизма стека, ибо я не увидел никакого использования стека. Может, конечно, я что проглядел. Поправьте меня, пожалуйста, если кто понял, в чем тут фишка. Код, конечно, на самом деле ужасен, но в другом отношении - в нем имеет место переход внутрь цикла, что является грубейшим нарушением канонов структурного программирования, намного грубее даже, чем использование пресловутого goto. Впрочем, стек здесь ни при чем, это всего лишь пример плохого стиля программирования в стиле С, при этом компилятор не выдает ошибку, хотя следовало бы.
|
|
|
Записан
|
|
|
|
Alf
Гость
|
|
« Ответ #5 : 05-12-2005 13:30 » |
|
А я думал, что в Си++ запретили переход внутрь блока в switch ... Век живи - век учись Переход внутрь блока - это еще полбеды. Там фактически переход внутрь цикла, а эта штука куда похуже будет.
|
|
|
Записан
|
|
|
|
RXL
|
|
« Ответ #6 : 05-12-2005 22:16 » |
|
Alf, я думаю, данный пример, возможно, совсем не то, что автор хотел сказать: частая ошибка copy-paste в публикациях. Такие вещи особенно в справочниках убивают: как правило, самое интересующее заменено билибердой. Устал человек и редактор напился...
|
|
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
Alf
Гость
|
|
« Ответ #7 : 05-12-2005 23:00 » |
|
Alf, я думаю, данный пример, возможно, совсем не то, что автор хотел сказать: частая ошибка copy-paste в публикациях. Такие вещи особенно в справочниках убивают: как правило, самое интересующее заменено билибердой. Устал человек и редактор напился... Возможно. Хорошо, если действительно ошибка наборщика, это легко поправимо. Однако есть подозрение (и небеспочвенное), что причина кроется глубже - в непонимании предмета. В частности, нас наталкивают на мысль, что переменные, описанные внутри блока, создаются при входе в блок, а при выходе уничтожаются. Однако мне помнится, что как-то копался я с отладчиком в функции, содержащей несколько блоков с локальными переменными. Так вот, если мне не изменяет память, под эти самые переменные память (прошу прощения за каламбур) выделялась единовременно в самом начале функции, а вовсе не в момент входа в блок. Так что локальность этих переменных обеспечивалась за счет видимости имен (за пределами блока нельзя было обратиться к описанной внутри него переменной), а вовсе не за счет ограниченного времени жизни (локальные в блоке переменные преспокойненько размещались в стеке вместе с остальными переменными стекового размещения, а потом при выходе из функции стек очищался опять же разом. Если сумбурно выразился, попробую на примере: void f(void) { int i; // переменная, локальная в f() ... { int j; // переменная, локальная в блоке ... } }
Так вот, есть подозрение, что i и j будут размещены в стеке при входе в f() одновременно, а не так, как рассказывается в статье, - при входе в блок разместили переменную j на стеке, при выходе из блока уничтожили. Впрочем, было это давно, могу и спутать. Постараюсь проверить, как свободное время выдастся. Если ошибся - заранее извиняюсь перед автором статьи. Если нет - опечаткой тут не отвертеться, это просто непонимание предмета, о котором пишешь, да еще столь детально. В общем, отложу вердикт на денек, нужно поставить эксперимент.
|
|
|
Записан
|
|
|
|
Alf
Гость
|
|
« Ответ #8 : 06-12-2005 08:18 » |
|
Собственно, чуда не произошло. Вот фрагмент программы с переменными, локальными в блоке: 7: void f(void) 8: { 00401020 55 push ebp 00401021 8B EC mov ebp,esp 00401023 83 EC 4C sub esp,4Ch 00401026 53 push ebx 00401027 56 push esi 00401028 57 push edi 00401029 8D 7D B4 lea edi,[ebp-4Ch] 0040102C B9 13 00 00 00 mov ecx,13h 00401031 B8 CC CC CC CC mov eax,0CCCCCCCCh 00401036 F3 AB rep stos dword ptr [edi] 9: int i; 10: i = 1; 00401038 C7 45 FC 01 00 00 00 mov dword ptr [ebp-4],1 11: { 12: int j; 13: j = 2*i; 0040103F 8B 45 FC mov eax,dword ptr [ebp-4] 00401042 D1 E0 shl eax,1 00401044 89 45 F8 mov dword ptr [j],eax 14: { 15: int k; 16: k = j + 3; 00401047 8B 4D F8 mov ecx,dword ptr [j] 0040104A 83 C1 03 add ecx,3 0040104D 89 4D F4 mov dword ptr [k],ecx 17: } 18: } 19: } 00401050 5F pop edi 00401051 5E pop esi 00401052 5B pop ebx 00401053 8B E5 mov esp,ebp 00401055 5D pop ebp 00401056 C3 ret
А вот что нам обещают в статье: Для начала маленькое отступление. В языках программирования высокого уровня обычно используют блочную структуру программы. Блоки позволяют, например, локализовать переменные, перекладывая заботы об управлении памятью на компилятор. Так, память, выделенная под переменные, объявленные в начале блока, обычно автоматически освобождается перед завершением блока. Естественно, в основе такого принципа лежит простой механизм стека – в начале блока ссылка на каждую объявленную в нем переменную записывается на вершину специального стека (обычно невидимого для прикладного программиста), а в конце блока ссылки автоматически снимаются со стека, и адресуемые ими участки памяти помечаются как свободные. Нигде не усмотрел обещанных манипуляций с памятью. Как и следовало ожидать, стек единожды выделяется при входе в функцию и единожды очищается при выходе из нее независимо от уровня вложенности блоков внутри функции. Я уже не говорю об абсурдности фразы "в конце блока ссылки автоматически снимаются со стека, и адресуемые ими участки памяти помечаются как свободные", а также о таинственном засекреченном стеке, который оказался невидим не только для прикладного программиста, но даже в окошке дизассемблера не показывается. Видимо, у меня нет надлежащего допуска к столь секретным штукам. Резюме. Конечно, С++ весьма далек от совершенства. Да и трудно найти в наше время что-либо, остающееся неизменным в течение двух десятилетий, тем более в компьютерной области Тем не менее писать статью со столь претенциозным названием, не имея понятия о предмете, просто неуместно. Как говорил профессор Преображенский, разруха начинается в первую очередь в головах. Причем в данном случае - явно не в голове Страуструпа. P.S. Проглядел еще одну статейку того же автора в том же источнике на аналогичную тему. Там все еще больше запущено. Так что это не опечатка и не ошибка наборщика, а диагноз.
|
|
|
Записан
|
|
|
|
Гром
Птычк. Тьфу, птычник... Вот!
Готовлюсь к пенсии
Offline
Пол:
Бодрый птах
|
|
« Ответ #9 : 06-12-2005 09:04 » |
|
Alf - честно говоря - вообще сыр-бор на пустом месте. Если автор так уж обеспокоен проблемами С++ то пускай лучше бы покапался в проблема Обжект-ориентед, то бишь в тех гиганстких. часто неоправданно огромадных запросах, которые тот требует на память и ресурсы, перенасыщенность кода, который получается. Вот это действительно огромный + и - в одном флаконе в разработанной кнцепции С++. А все что написано в статье - бред воспаленного журналистского эго. Видать жена не дает или что-то в этом роде.
|
|
|
Записан
|
А птичку нашу прошу не обижать!!!
|
|
|
Alf
Гость
|
|
« Ответ #10 : 06-12-2005 09:16 » |
|
Гром, такое скажешь... Какие там объектные заморочки, если парень в другой статье на полном серьезе рекламирует lint и его производные, а также предостерегает от опаснейшей путаницы между равенством и присваиванием? Опомнился спустя четверть века. Так скоро будем на полном серьезе обсуждать поблемы COBOL'а с FORTFAN'ом, а лет через 5 дорастем до обсуждения вреда оператора GOTO.
Ясное дело, что место пустое. Даже хуже того, не ноль, а еще и со знаком минус. Однако раз уж у Olegator'а возникли вопросы, нужно же на них ответить. Сам видишь на недавнем примере, невежество - штука агрессивная и всепроникающая, если не перекрывать кислород, норовит пролезть в любую щель и навязать свою единственно правильную точку зрения. Поэтому стараюсь выметать из нашего форума при малейшем появлении, это как сорняк - вовремя не вырвешь, укоренится намертво.
|
|
« Последнее редактирование: 04-12-2007 20:10 от Алексей1153++ »
|
Записан
|
|
|
|
Гром
Птычк. Тьфу, птычник... Вот!
Готовлюсь к пенсии
Offline
Пол:
Бодрый птах
|
|
« Ответ #11 : 06-12-2005 10:19 » |
|
Это уже точно... !
|
|
|
Записан
|
А птичку нашу прошу не обижать!!!
|
|
|
nikedeforest
|
|
« Ответ #12 : 06-12-2005 17:38 » |
|
Alf, вот ты привел код: void f(void) { int i; // переменная, локальная в f() ... { int j; // переменная, локальная в блоке ... } }
А я хочу обратить внимание на такой код void f(void) { int i; // переменная, локальная в f() ... { int i; // переменная, локальная в блоке ... } }
Как же быть в этом случае? Просто неувязочка какая-то , а может .
|
|
« Последнее редактирование: 04-12-2007 20:12 от Алексей1153++ »
|
Записан
|
ещё один вопрос ...
|
|
|
RXL
|
|
« Ответ #13 : 06-12-2005 18:05 » |
|
nikedeforest, эти примеры отличаются только видимостью имен, в остальном - то же самое. Такой пример: void f(void) { char *p; { char c[] = "abc"; p = &c[1]; } *p = 'x'; }
Если бы стек освобождался бы в конце блока, то были бы проблемы. Конечно, разрешить сей вопрос может только стандарт. Кто-нибудь процитирует сей момент?
|
|
« Последнее редактирование: 04-12-2007 20:13 от Алексей1153++ »
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
nikedeforest
|
|
« Ответ #14 : 06-12-2005 19:58 » |
|
to RXL, Хм, я не проверял, но если это работает (хотя это и вправду должно работать, никогда на это внимание не обращал), то ты по сути прав или есть какое-то на этот счет исключение. Короче, сейчас это похоже на гадание на кофейной гуще. К своему стыду, я не знаю ассемблера, а также я плохо знаю процессы происходящие на низком уровне, к примеру работа того же стека. И возникает такой ламерский вопрос, если сразу и заранее выделяется память для переменных с разной областью видимости, но с одинаковым именем, значит существует какое-то средство для избежания конфликта в стеке или для стека имя переменной вообще не имеет никакого значения?
|
|
|
Записан
|
ещё один вопрос ...
|
|
|
Гром
Птычк. Тьфу, птычник... Вот!
Готовлюсь к пенсии
Offline
Пол:
Бодрый птах
|
|
« Ответ #15 : 06-12-2005 20:34 » |
|
nikedeforest - в общем твой пример оперирует двумя i переменными причем разными...
|
|
|
Записан
|
А птичку нашу прошу не обижать!!!
|
|
|
LP
Помогающий
Offline
|
|
« Ответ #16 : 06-12-2005 20:40 » |
|
ИМХО, автор все-таки прав. Он описал общую концепцию блоков в С++, так сказать на "высоком уровне". Ассемблер ничего не доказывает. Компиляторы в соответствии с принципом "as if", могут в целях оптимизации и не выделять дважды память в стеке, а сделать это за раз, главное чтобы результат работы программы остался таким же. Пример RXL'а конечно же приводит к проблемам, так как происходит обращение к освобожденной памяти. Пусть даже на каком-то конкретном компиляторе (или на всех) это не вызывает проблем, согласно стандарту так делать нельзя. Скажем, может появиться такой компилятор который будет освобождать память после блока {} (имеет право) и тогда такая программа упадет "на законных основаниях". По поводу стандарта: вот здесь говорится что "просто" блок определяет локальную область видимости. 6.3/1 So that several statements can be used where one is expected, the compound statement (also, and equivalently, called “block”) is provided. compound-statement: { statement-seqopt } statement-seq: statement statement-seq statement A compound statement defines a local scope (3.3).
А здесь говорится, что память для локальных переменных сохраняется до конца блока в котором они созданы. 3.7.2 Local objects explicitly declared auto or register or not explicitly declared static or extern have automatic storage duration. The storage for these objects lasts until the block in which they are created exits. If a named automatic object has initialization or a destructor with side effects, it shall not be destroyed before the end of its block, nor shall it be eliminated as an optimization even if it appears to be unused, except that a class object or its copy may be eliminated as specified in 12.8.
PS Это было ИМХО, ногами не бить!
|
|
« Последнее редактирование: 20-12-2007 17:14 от Алексей1153++ »
|
Записан
|
Если эта надпись уменьшается, значит ваш монитор уносят
|
|
|
Alexiski
Гость
|
|
« Ответ #17 : 06-12-2005 21:46 » |
|
ИМХО выделение стека за один прием = это чистейшей воды оптимизация. И это еще цветочки. Я вот недавно смотрел в дизассемблере программу, скомпилированную под UNIX. Долго не мог врубиться, пока не понял: компилятор не двигает стек под вызов процедур ! Вместо привычного push Z push Y push X call Func add sp, 12h
написано примерно следующее: mov [sp], X mov [sp+4], Y mov [sp+8], Z call Func
При этом память под такое безобразие просто добавлена к области локальных переменных
|
|
|
Записан
|
|
|
|
RXL
|
|
« Ответ #18 : 06-12-2005 22:11 » |
|
Alexiski, видимо, это оптимизация. Ко ли стек будет израсходован, то почему бы и не выделить место сразу и не дергать указатель понапрасну.
nikedeforest, имена переменных - это абстракции, которые при компиляции преобразуются в численные значения: адреса и смещения в куче, в коде, на стеке и т.п.
|
|
« Последнее редактирование: 06-12-2005 22:13 от RXL »
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
nikedeforest
|
|
« Ответ #19 : 07-12-2005 12:03 » |
|
RXL, спасибо.
|
|
|
Записан
|
ещё один вопрос ...
|
|
|
Alf
Гость
|
|
« Ответ #20 : 07-12-2005 12:32 » |
|
ИМХО, автор все-таки прав. Он описал общую концепцию блоков в С++, так сказать на "высоком уровне". Ассемблер ничего не доказывает. Компиляторы в соответствии с принципом "as if", могут в целях оптимизации и не выделять дважды память в стеке, а сделать это за раз, главное чтобы результат работы программы остался таким же. А в чем тогда смысл статьи, если общая концепция описывается в деталях, которые в конкретном случае не имеют места? Ассемблер весьма неплохо все доказывает, поскольку я привел фрагмент программы, скомпилированной в режиме отладки при выключенной оптимизации. Кстати, я неспроста заподозрил неладное, поскольку первый раз столкнулся с этим, программируя в DOS на Borland C++ 3.1 много лет назад. Поскольку дело было связано с взаимодействием с аппаратурой в реальном времени, приходилось периодически просматривать стек, регистры и т.д., именно тогда я впервые и заметил, что автоматические переменные резервируются в стеке сразу, а не по мере объявления. Поскольку Borland C++ 3.1 в свое время был весьма популярен, да и компиляторы от Microsoft редкими не назовешь, то как минимум изрядная часть софта (если не б ольшая) ведет себя не так, как описано в статье. Может, конечно, и найдется компилятор, который столь щепетильно обращается со стеком, но тогда это следовало явно оговорить. Потому как статья преподносится как мудрость едва ли не уровня стандарта. IMHO автор сильно не прав, ибо писать нужно о том, что понимаешь, и тщательно проверять написанное в отладчике (не столь уж трудоемко набрать десяток строк и пройтись отладчиком, следя за стеком). Но прочитав другую статью того же автора (не буду делать ей рекламу здесь), я понял, что поверхностный подход - это стиль автора, поэтому развивать тему вряд ли целесообразно.
|
|
|
Записан
|
|
|
|
Anchorite
Гость
|
|
« Ответ #21 : 14-12-2005 20:34 » |
|
именно тогда я впервые и заметил, что автоматические переменные резервируются в стеке сразу, а не по мере объявления.
Я думаю что конструкция типа работает быстрее и занимает меньше места, чем sub esp, 4 ... some actions here ... sub esp, 4 ... some actions here ... sub esp, 4 ... some actions here ... sub esp, 4
|
|
|
Записан
|
|
|
|
REM
Гость
|
|
« Ответ #22 : 14-12-2005 20:35 » |
|
To ALF И всё же в данном случае автор статьи был прав. Как справедливо заметил LP, ассемблер ничего не доказывает. Ссылка на стандарт кажется мне весьма убедительной (в конце концов раздел форума называется ANSI С/С++). Да и Страуструп в своей книжке, помнится, говорил о том же. А по поводу "выключенной оптимизации" -- как вы безусловно знаете, компиллятор сам решает что и где ему оптимизировать, мы можем дать ему лишь "хинт". To RXL nikedeforest, эти примеры отличаются только видимостью имен, в остальном - то же самое. Такой пример: void f(void) { char *p; { char c[] = "abc"; p = &c[1]; } *p = 'x'; }
Если бы стек освобождался бы в конце блока, то были бы проблемы. Нууу, это-то как раз ничего не доказывает. Я могу привести пример аналогичного кода, который совершенно точно приводит к серьёзным проблемам (пример взят из книжки Скотта Мэйерса Effective C++): int * f(void) { int i; return &i; }
Этот код, безусловно, есть зло, но он к сожалению компиллируется и приводит к очень трудноотлаживаемым ошибкам. Приведу другой фрагмент кода: class A{ public: A(){;} ~A(){;} };
void main() { { A a; }//(*) }
Вызов деструктора произойдёт на строчке, отмеченной (*). А значит память освобождается всё таки в конце блока, ч.т.д.
|
|
« Последнее редактирование: 04-12-2007 20:16 от Алексей1153++ »
|
Записан
|
|
|
|
Alf
Гость
|
|
« Ответ #23 : 14-12-2005 21:29 » |
|
Я думаю что конструкция типа ... работает быстрее и занимает меньше места, чем ...
Совершенно верно. Я бы и сам, если бы взялся реализовывать компилятор, поступил именно так. Зачем в статье понадобилось изобретать усложненную версию, которой не следуют реальные компиляторы, мне непонятно. To ALF
И всё же в данном случае автор статьи был прав. Как справедливо заметил LP, ассемблер ничего не доказывает. Ссылка на стандарт кажется мне весьма убедительной (в конце концов раздел форума называется ANSI С/С++). Да и Страуструп в своей книжке, помнится, говорил о том же. В приведенной ссылке на стандарт указывается лишь, что ...память для локальных переменных сохраняется до конца блока в котором они созданы. Из этого никоим образом не следует, что она немеддленно освобождается по выходу из блока, как утверждается в статье. И если "ассемблер ничего не доказывает" (точнее, доказывает обратное), то в чем правота автора? Только в том, что так могло бы быть в принципе? С таким же успехом я могу уверять, что умножение реализуется через сложение в цикле. Ну и что, что в реальном коде этого нет? Зато в принципе правильно, да и в учебнике арифметики операция умножения именно так определяется. Что касается Страуструпа - насколько я помню, в своей "книжке" он говорит об области видимости локальных в блоке переменных; никаких деталей относительно выделения памти под них и забирания ее обратно не припоминаю. Впрочем, "книжка" слишком велика, вполне возможно, что где-то об этом говорится; если приведете параграф, буду благодарен. А по поводу "выключенной оптимизации" -- как вы безусловно знаете, компиллятор сам решает что и где ему оптимизировать, мы можем дать ему лишь "хинт". На самом деле не так все запущено. Если в режиме отладки компилятор чрезмерно увлечется оптимизацией, сама отладка станет невозможной, - например, я устанавливаю точку останова на вычислении выражения в цикле, чтобы пройти цикл по шагам, а компилятор соизволил вынести выражение до цикла, поскольку оно инвариантно и может быть единожды вычислено раньше. Поэтому в режиме отладки он не столь склонен к самоуправству, а я пример именно под отладчиком выполнял.
|
|
|
Записан
|
|
|
|
REM
Гость
|
|
« Ответ #24 : 15-12-2005 06:32 » |
|
Что касается Страуструпа - насколько я помню, в своей "книжке" он говорит об области видимости локальных в блоке переменных; никаких деталей относительно выделения памти под них и забирания ее обратно не припоминаю. Впрочем, "книжка" слишком велика, вполне возможно, что где-то об этом говорится; если приведете параграф, буду благодарен. Приведу: 10.4.3 "Конструирование и уничтожение". Пример, который я привёл в предыдущем посте прекрастно иллюстрирует сказанное в этом разделе. Но, к сожалению, в нём Страуструп рассматривает только конструировани и уничтожение объектов, не затрагивая примитивные типы. Я был абсолютно уверен, что это касается всех типов, однако должен признаться, что эту уверенность вы во мне подорвали. Тем не менее, следует учесть, что С++ всё же ООП язык, и как следствие в классической С++ программе львиная доля управления памяти будет связана именно с обЪектами. А здесь говорится, что память для локальных переменных сохраняется до конца блока в котором они созданы. Из этого никоим образом не следует, что она немеддленно освобождается по выходу из блока, как утверждается в статье. Возможно я чего то не понимаю, но на мой взгляд между этими утверждениями не существует причинно-следственной связи только по тому, что они тождественны. Пожалуйста, поясните разницу.
|
|
|
Записан
|
|
|
|
Hooter
|
|
« Ответ #25 : 15-12-2005 09:08 » |
|
Приведу: 10.4.3 "Конструирование и уничтожение". Пример, который я привёл в предыдущем посте прекрастно иллюстрирует сказанное в этом разделе. Но, к сожалению, в нём Страуструп рассматривает только конструировани и уничтожение объектов, не затрагивая примитивные типы.
В разделе 10.4 под уничтожением понимается вызов деструктора объекта, а не освобождение памяти. А здесь говорится, что память для локальных переменных сохраняется до конца блока в котором они созданы. Из этого никоим образом не следует, что она немеддленно освобождается по выходу из блока, как утверждается в статье. Возможно я чего то не понимаю, но на мой взгляд между этими утверждениями не существует причинно-следственной связи только по тому, что они тождественны. Пожалуйста, поясните разницу. Разница именно в том, что память _должна_ сохраняться до конца блока, но нет правил, говорящих о том, что память _должна_ освобождаться при выходе из блока. В стандарте нигде не оговорено _когда_именно_ должна освобождаться память.
|
|
|
Записан
|
|
|
|
REM
Гость
|
|
« Ответ #26 : 15-12-2005 10:34 » |
|
Разница именно в том, что память _должна_ сохраняться до конца блока, но нет правил, говорящих о том, что память _должна_ освобождаться при выходе из блока. В стандарте нигде не оговорено _когда_именно_ должна освобождаться память. Абсолютно справедливое замечание. Был не прав. Исправлюсь В разделе 10.4 под уничтожением понимается вызов деструктора объекта, а не освобождение памяти. И опять в точку. Для меня вызов деструктора всегда ассоциировался с освобождением памяти. Но как я не пытался найти подтверждение этому, у меня ничего не вышло. Итак, ситуация патовая. Я не могу подтвердить свою точку зрения, но и убедительных доказательств обратной не нашёл. Однако чем дольше я думаю об этом, тем больше я склоняюсь к тому, чтобы перейти на сторону Альфа. Одна из причин -- я не вижу принципиальной разницы межну локальным примитивным типом и локальным объектом. В любом случае, спасибо Alf'у и Hooter'у за интересную дискуссию.
|
|
« Последнее редактирование: 20-12-2007 17:19 от Алексей1153++ »
|
Записан
|
|
|
|
NetRaider
Гость
|
|
« Ответ #27 : 16-12-2005 05:07 » |
|
Alf, я думаю, данный пример, возможно, совсем не то, что автор хотел сказать: частая ошибка copy-paste в публикациях. Такие вещи особенно в справочниках убивают: как правило, самое интересующее заменено билибердой. Устал человек и редактор напился...
Техника Duffs-device. Применяется в целях оптимизации.
|
|
|
Записан
|
|
|
|
NetRaider
Гость
|
|
« Ответ #28 : 16-12-2005 05:12 » |
|
Alf, вот ты привел код: void f(void) { int i; // переменная, локальная в f() ... { int j; // переменная, локальная в блоке ... } }
А я хочу обратить внимание на такой код void f(void) { int i; // переменная, локальная в f() ... { int i; // переменная, локальная в блоке ... } }
Как же быть в этом случае? Просто неувязочка какая-то , а может . Вполне законно. Имя переменной во вложенном блоке скрывает внешнюю. Здесь важно то, с какого момента происходит сокрытие имен: int main() { const int i = 10; { int i[i]; //эквивалентно int i[12] } }
Внешнее имя становится недоступно в конце полного декларатора, который в данном случае заканчивается после ']'.
|
|
« Последнее редактирование: 04-12-2007 20:18 от Алексей1153++ »
|
Записан
|
|
|
|
REM
Гость
|
|
« Ответ #29 : 16-12-2005 07:07 » |
|
Техника Duffs-device. Применяется в целях оптимизации. Техника сама по себе, конечно очень интересная, в частности, как ещё один пример использования case без break. Мне удалось найти более вразумительный прмер кода. send(to, from, count) register short *to, *from; register count; { do *to = *from++; while(--count>0); }
А теперь оптимизированный код send(to, from, count) register short *to, *from; register count; { register n=(count+7)/8; switch(count%8){ case 0:do{*to = *from++; case 7:*to = *from++; case 6:*to = *from++; case 5:*to = *from++; case 4:*to = *from++; case 3:*to = *from++; case 2:*to = *from++; case 1:*to = *from++; }while(--n>0); } }
Это бесспорно ugly hack. Не должен высокоуровневый язык позволять осуществлять такие приёмы. Компиллятор сам должен пытаться "оценивать" код и проводить необходимую оптимизацию (в данном случае цикл должен транслироваться в movs). Конечно, компиллятор не всесилен. Однако данный код заставляет программиста думать не о алгоритме, а о процессорных инструкциях, что, конечно, противоречит принципам структурного программирования. Однако данный код относится всё таки не столько к C++, а к C. А лично я люблю C++ за великолепную поддержку объектов, за шаблоны, за STL, а вовсе не за C-подобный синтаксис.
|
|
|
Записан
|
|
|
|
|