|
Программирование >> Перегруженные имена функций и идентификаторы
переполнение внутреннего буфера. Надеемся, все знают как это происходит - несколько часов непрерывных бдений с отладчиком, а потом через день, на свежую голову , выясняется что где-то б1 пропущен один символ... Опять же, для программиста самым удобным будет моментальное аварийное прекращение работы программы в этом месте - тогда он сможет заменить gets() на что-нибудь более порядочное . У кого-то может возникнуть предложение просто взять и увеличить размер буфера. Но не надо забывать, что всегда можно ввести строку длиной, превышающий выделенный размер; если кто-то хочет возразить, что случаи имен длиной более чем, например, 1024 байта все еще редки, то перейдем к другому, несколько более интересному примеру возникающей проблемы при использовании gets(). Для это просто подчеркнем контекст, в котором происходит чтение строки. void foo() char name[10]; ... puts( Enter you name: ); gets(name); ... Имеется в виду, что теперь name расположен в стеке. Надеемся, что читающие эти строки люди имеют представление о том, как обычно выполняется вызов функции. Грубо говоря, сначала в стек помещается адрес возврата, а потом в нем же размещается память под массив name. Так что теперь, когда функция gets() будет писать за пределами массива, она будет портить адрес возврата из функции foo(). На самом деле, это значит, что кто-то может задать вашей программе любой адрес, по которому она начнет выполнять инструкции. Немного отвлечемся, потому что это достаточно интересно. В операционной системе Unix есть возможность запускать программы, которые будут иметь привилегии пользователя, отличного от того, кто этот запуск произвел. Самый распространенный пример, это, конечно же, суперпользователь. Например, команды ps или passwd при запуске любым пользователем получают полномочия rootа. Сделано это потому, что копаться в чужой памяти (для ps) может только суперпользователь, так же как и вносить изменения в /etc/passwd. Понятно, что такие программы тщательнейшим образом проверяются на отсутствие ошибок - через них могут утечь полномочия к нехорошим хакерам (существуют и хорошие!). Размещение буфера в стеке некоторой функции, чей код выполняется с привилегиями другого пользователя, позволяет при переполнении этого буфера изменить на что-то осмысленное адрес возврата из функции. Как поместить по переданному адресу то, что требуется выполнить (например, запуск командного интерпретатора), это уже другой разговор и он не имеет прямого отношения к программированию на Си или C++. Принципиально иное: отсутствие проверки на переполнение внутренних буферов очень серьезная проблема. Зачастую программисты ее игнорируют, считая что некоторого заданного размера хватит на все, но это не так. Лучше с самого начала позаботиться о необходимых проверках, чтобы потом не мучиться с решением внезапно возникающих проблем. Даже если вы не будете писать программ, к которым выдвигаются повышенные требования по устойчивости к взлому, все равно будет приятно осознавать, что некоторых неприятностей возможно удалось избежать. А от gets() избавиться совсем просто: fgets(name, 10, stdin); Использование функции gets() дает лишнюю возможность сбоя вашей программы, поэтому ее использование крайне не рекомендовано. Обычно вызов gets() с успехом заменяется fgets(). Свойства Когда только появилась Delphi (первой версии) и ее мало кто видел, а еще меньше людей пробовали использовать, то в основном сведения об этом продукте фирмы Borland б1ли похожи на сплетни. Помнится такое высказывание: Delphi расширяет концепции объектно-ориентированного программирования за счет использования свойств . Те, кто работал с C++ Builder, представляет себе, что такое его свойства. Кроме того, кто хоть как-то знаком с объектно-ориентированным анализом и проектированием, понимает - наличие в языке той или иной конструкции никак не влияет на используемые подходы. Мало того, префикс ОО обозначает не использование ключевых слов class или property в программах, а именно подход к решению задачи в целом (в смысле ее архитектурного решения). С этой точки зрения, будут использоваться свойства или методы set и get, совершенно без разницы - главное разграничить доступ. Тем не менее, свойства позволяют использовать следующую конструкцию: описать функции для чтения и записи значения и связать их одним именем. Если обратиться к этому имени, то в разных контекстах будет использоваться соответственно либо функция для чтения значения, либо функция для его установки. Перед тем, как мы перейдем к описанию реализации, хотелось бы высказаться по поводу удобства использования. Прямо скажем, программисты - люди. Очень разные. Одним нравится одно, другое это ненавидят... в частности, возможность переопределения операций в C++ часто подвергается нападкам, при этом одним из основных аргументов (в принципе, не лишенный смысла) является то, что при виде такого выражения: a = b; нельзя сразу же сказать, что произойдет. Потому что на оператор присваивания можно повесить все что угодно. Самый распространенный пример неправильного (в смысле, изменение исходных целей) использования операций являются потоки ввода-вывода, в которых операторы побитового сдвига выполняют роль printf. Примерно то же нарекание можно отнести и к использованию свойств. Тем не менее, перейдем к описанию примера. Итак, нужно оформить такой объект, при помощи которого можно было бы делать примерно следующее: class Test protected: int p a;
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |