Продолжение сезона C и C++

По своей природе я страшно ленивый человек, вдобавок ко всему еще сейчас и очень занятой. Однако статья, опубликованная в последнем номере "Компьютерной газеты" с впечатляющим названием "Открытие сезона на C и C++! Наполеон отдыхает...", добавила последнюю каплю в чашу моего терпения и таки сумела заставить меня найти время и желание высказать свои мысли по этому поводу.

На страницах КГ уже появлялись воинствующие фанаты (не побоюсь этого слова), которые пытались убедить мир, что MODULA и Паскаль - лучшее, что есть в мире программирования и что дни C/C++ сочтены. У большинства профессиональных программистов она вызвала, скорее всего, либо улыбку, либо ностальгию по давно ушедшим временам.

Однако один мой товарищ, сравнительно недавно начавший заниматься компьютерами, чуть не убил меня своей непоколебимой уверенностью, что C++ заниматься бесполезно, так как это страшно плохой язык программирования. Эту уверенность он приобрел, прочитав про MODULA на страницах КГ. И вот в последнем номере - статья со строго противоположной точкой зрения, в которой Паскаль причислен к "мусорным языкам".

Господа! Давайте смотреть на вещи объективно, не ударясь в крайности и не впадая в фанатизм! Люди верят напечатанному в газете и приобретают опасные заблуждения!

Однако не о сравнении языков я собираюсь написать. "Чем докажешь, что Паскаль хуже C/C++?". Этот вопрос уже давно подходит к тому, чтобы его разместить в рубрике "Тупые вопросы 2000." И тут я полностью согласен с автором. Собственно, я согласен с ним во многих вопросах, которые он осветил. Однако, к сожалению, не во всех. Так что сейчас я хочу кое-что опровергнуть, а кое-что описать более подробно.

C/C++ от Borland

"Видели ли вы хоть одну коммерческую работу, написанную на Borland'е?". Скажу - видел, и не одну. На выбор пройдусь по программам, которые установлены на моем компьютере в пределах каталога "Program Files".

Первая попавшаяся - FAR manager. Знакома? Боюсь, что 90% читателей КГ используют именно ее для управления файлами. Смотрим исполняемый файл, и что мы видим? "Borland C++ - Copyright 1995 Borland Intl.". Идем далее, видим - "DISKo Pumper", в русскоязычных странах известная как "ДИСКо Качалка". Опять смотрим исполняемый файл - та же строка, оставленная "на память" компилятором Borland. Далее встречаем "Net optimizer 2000", о котором как-то писали на страницах КГ. Снова строчка о Borland. Ага! Вот очень приятная программа, знакомая, наверное, каждому пользователю Интернет: The Bat! Правда, он написан не на C++, а на Delphi, но мы тут говорим о Borland. Кстати, фирма RIT Research Labs практически все свои программы компилирует компиляторами от Borland.

Но все это - "Shareware" программы. Наверное, стоит привести чисто коммерческую программу, которая стоит столько, что говорить об этом неприлично? Пожалуйста - SQL Navigator. Фирма чисто западная - Quest Software (http://www.quest.com). Ее программы практически все написаны на компиляторах от Borland и пользуются заслуженной популярностью у разработчиков баз данных. В следующем каталоге Perl Builder - очень знакома всем, пишущим на языке Perl под Windows. А вот RAR for Windows - кто не знает этот архиватор?

Продолжать можно долго, я бы мог заглянуть на винчестер коллег по работе и наверняка найти там еще с десяток программ, собранных компиляторами Borland. Конечно, программ, написанных на Visual C++ под Windows, гораздо больше, но утверждать, что на инструментарии от Borland не пишутся коммерческие (читай - серьезные) программы, нельзя, как и ставить вообще такой вопрос.

Теперь о файле BCWL32.DLL, которая якобы обязательно должна быть в наличии для работоспособности программ, собранных компилятором Borland. Предлагаю поискать ее у меня. Попробуй, найди! Ни одна из перечисленных программ не нуждается в этом файле! Дело в том, что никто не обязывает использовать в вашей программе библиотеку OWL, реализация которой и находится в файле BCWL32.DLL и еще нескольких рядом. И большинство программистов и не используют OWL в своих программах! В последних версиях своих средств разработки Borland оставила OWL только для обратной совместимости, а доминирует у нее ныне библиотека VCL, необходимые части которой очень просто включать в исполняемый файл программы.

Кстати, подобной проблемой страдает и библиотека MFC, которая является одним из основных коньков Visual C++ от Microsoft. Приглядитесь внимательно, сколько программ, написанных с применением Visual C++ и MFC, таскают за собой в дистрибутиве букет из динамических библиотек mfc*.dll? И сколько их потом лежит в системном каталоге Windows? Вот у меня сейчас лежит их аж 4 версии: 3-я, 3.2, 4-я и 4.2. Если бы все было в порядке с совместимостью - зачем же такой букет библиотек? Так что тут Visual Studio и MFC не имеет преимуществ перед Borland C++ и OWL. Напоследок скажу, что никто не запрещает использовать в программах, разрабатывающихся на компиляторах от Borland, ту же MFC. Или для особо одаренных сразу 3 библиотеки - VCL, MFC и OWL. Правда, кому это нужно?

Достаточно для опровержения?

Теперь об удобстве написания программ. Если вы когда-нибудь писали программу "под заказ" и сроки сдачи готового продукта были весьма сжатыми, то вы оцените среду разработки программ от Borland на 20 баллов по 5-балльной шкале. Применяя Visual C++, нужно часто задумываться о вещах, которые далеко выходят за общий алгоритм программы. MFC крайне бедна в сравнении с VCL. Нет многих нужных в хозяйстве вещей, которые приходится заменять сторонними компонентами ActiveX или опять же сторонними библиотеками. А далеко не все из них бесплатные и общедоступные. За примером ходить далеко не надо. Нужна мне таблица ячеек, как на листе книги Excel (компонент TStringGrid в Borland C++ Builder). Где ее аналог в MFC? А сравните CString (объект - текстовая строка) от Microsoft и AnsiString от Borland? Я не утерпел в свое время и отказался от использования CString в своих программах ввиду его полной ущербности.

Однако у Borland есть свои недостатки. Например, оптимизатор кода компиляторов Borland гораздо хуже оптимизатора от Microsoft. Совместимость со стандартом ANSI C++, значительно поправленная в последних версиях, все же остается далекой от совершенства. Ну и, конечно, нет полной уверенности в будущем компиляторов Borland. Фирма переживает трудные времена, и что будет с поддержкой ее компилятора другими производителями ПО - неизвестно.

С/С++ от Watcom

Ну, теперь, когда восстановили честное имя Borland, займемся компилятором от приказавшей долго и счастливо жить фирмы Watcom. Последняя известная миру версия - 11.0. Есть веские основания полагать, что новых версий этой Watcom C/C++ никто не увидит. 11-я помечена 1996 годом - целая эпоха прошла с момента его выхода. Вывод - компилятор от Watcom мертв. И будет чистым самоубийством начинать мало-мальски большой проект делать на нем. Среда разработки программ Watcom даже на 1996 год отдает мамонтами и дубинами. То есть она почти отсутствует. Набор утилит, который идет с ним, вряд ли можно назвать серьезной IDE. Так что если собираетесь писать программы в Watcom C++, готовьтесь к спартанской жизни.

Да, чуть не забыл. Среди программистов до сих пор жива легенда о непревзойденном качестве оптимизатора Watcom C++. Не минула чаша сия и автора данной статьи: "Самая сильная сторона WATCOM - это его оптимизатор. Этот оптимизатор может сделать из кривого Си-текста отличный EXE". А кто-нибудь пытался сравнить? Я пытался. Приведу результаты тестов.

Для теста я взял программу, изначально писавшуюся в Watcom C++ 10.0. Это процессорный тест журнала BYTE - BYTEmark версии 2.0. Эта программа написана на чистом C, благодаря чему она без проблем собирается практически любым компилятором. Что - делает? Известно что - тестирует производительность процессора. Проводит целую серию тестов. Причем все тесты имеют такой объем данных, чтобы попадание в процессорный кэш было максимальным. Понятное дело - это тест ядра процессора, а не скорости работы с системной памятью.

Какие тесты выполняет программа:
- Numeric sort: Сортируется массив 32-битных целых чисел;
- String sort: Сортируется массив строк произвольной длины;
- Bitfield: Выполняет целый ряд функций поразрядных операций с отдельными битами;
- Emulated floating-point: Эмуляция математического сопроцессора для расчетов с плавающей точкой;
- Fourier coefficients: Цифровая программа анализа для расчета последовательных аппроксимаций сигналов;
- Assignment algorithm: Известный алгоритм распределения задач;
- Huffman compression: Известный алгоритм сжатия текста и графики;
- IDEA encryption: Блочный алгоритм шифрования;
- Neural Net: Небольшой, но функционально полный имитатор нейронной сети;
- LU Decomposition: Алгоритм для решения линейных уравнений.

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

В тесте приняли участие:
1) Borland C++ Builder 1.0
2) Borland C++ Builder 3.0 UP1
3) Borland C++ Builder 4.0
4) Borland C++ Builder 5.0
5) Borland C++ 5.02
6) Microsoft Visual C++ 5.0 SP3
7) Microsoft Visual C++ 6.0 SP3
8) Watcom C++ 11.0
9) Intel C/C++ compiler 4.5

В каждом компиляторе компилировались, в зависимости от его возможностей оптимизированные по скорости выполнения версии для i386, Pentium и Pentium Pro. Для Microsoft Visual C++ компилировалась еще и версия с оптимизацией процессора типа "Blend*".

Тесты проводились на компьютере следующей конфигурации:
Процессор: Pentium-II 400 MHz.
Материнская плата: FIC VB-601 (чипсет BX, 100 MHz шина)
Память: 128 Мb, PC-100
Видеокарта: AUSUS V3600 - Riva TNT, 16 Mb
Операционная система: Windows 2000 Professional, build 2195
Формат приведенных значений в ячейках:
1 строка: INTEGER INDEX (90 MHz Dell Pentium = 1.00)
2 строка: FLOATING-POINT INDEX (90 MHz Dell Pentium = 1.00)


Компилятор

Результаты с различной оптимизацией

i386

Pentium

Pentium Pro

Blend

Borland C++ Builder 1.0

4.978

-

-

-

3.272

-

-

-

Borland C++ Builder 3.0

5.179

5.129

5.187

-

2.703

3.001

3.079

-

Borland C++ Builder 4.0

5.149

5.113

5.149

-

3.059

2.667

2.688

-

Borland C++ Builder 5.0

5.196

5.191

5.180

-

3.378

2.920

2.976

-

Borland C++ 5.02

4.886

4.826

-

-

2.812

2.919

-

-

Microsoft Visual C++ 5.0 SP3

6.269

6.268

6.669

6.256

6.382

6.380

6.499

6.378

Microsoft Visual C++ 6.0 SP3

6.210

6.218

6.644

6.217

6.662

6.659

6.672

6.655

Watcom C++ 11.0

5.183

5.186

-

-

6.393

6.310

-

-

Intel C/C++ compiler 4.5

7.926

7.797

8.047

8.044

7.705

7.666

7.902

7.904
Какие могут быть выводы, глядя на эту таблицу? Да, Watcom C/C++ имеет даже на сегодня отличный оптимизатор кода. И в 1996 году он бесспорно был лучшим. Но сейчас Visual C++ имеет оптимизатор не хуже, чем у Watcom! А компилятор Intel вообще вне конкуренции. Поэтому использовать Watcom C++ просто не имеет смысла.

Конечно, данный тест компиляторов является чисто искусственным. Это тест только скорости исполнения кода ядром процессора.

В реальных задачах здесь добавляются такие вещи, как скорость обращения к API операционной системы, качество реализации стандартных библиотек и функций. Например, одна из моих программ работает быстрее будучи откомпилированной в Visual C++, а код компилятора Intel выполняется медленнее. Все дело, думаю, в том, что в моей программе идет очень интенсивная работа с объектами ядра Windows, что лучше реализовано в компиляторе Microsoft.

С/С++ от Microsoft

"Очень много дешевых выпадов, что в MSVC плохой оптимизатор. Я настаиваю на сравнении с возможностями WATCOM. Если разобраться, ведь очень мало отличий."

Эти слова мне удалось подтвердить экспериментально, не правда ли?

Все хорошее, что есть в Visual C++, уже отмечено автором, и сказать более вроде бы нечего. Поэтому поговорим о явных недостатках фундамента построения приложений в Visual C++ - библиотеки MFC (Microsoft Foundation Classes) в сравнении с аналогичными библиотеками от Borland (VCL) или Troll tech (Qt). Собственно, единственным преимуществом MFC перед VCL и Qt является ее широчайшее распространение в мире. А остальное - сплошные недостатки.

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

Все остальное должно делаться "автоматически", не приводя к явно лишним затратам времени вроде поиска "почему по нажатии кнопки A не вызывается связанная с ней функция B". Однако для программиста не должно быть преград заглянуть во внутренний механизм библиотеки и что-то там изменить или работать с системой на самом низком уровне.

Интерфейс взаимодействия с библиотекой должен быть наиболее простым и логичным. Все построение приложения с ее использованием должно сводиться к комбинированию объектов и построению на основе их новых объектов пользователя. В идеале каждый элемент интерфейса обязан быть отдельным объектом.

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

Что мы видим в MFC? В целом она удовлетворяет описанным условиям, однако есть одно большое "но". Возьмем, к примеру, задачу создания диалогового окна средствами MFC. Пускай у нас будет в нем поле ввода, пара кнопок, статическая надпись и, скажем, ниспадающий список (Combo box) (Рис. 1).

Class Wizard услужливо сгенерирует нам класс, описывающий данное окно.

// CTestDialog dialog
class CTestDialog: public CDialog
{
// Construction
public:
CTestDialog(CWnd* pParent = NULL); // standard constructor
// Dialog Data
//{{AFX_DATA(CTestDialog)
enum { IDD = IDD_DIALOG1 };
// NOTE: the ClassWizard will add data members here
//}}AFX_DATA

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CTestDialog)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL

// Implementation
protected:

//Generated message map functions
//{{AFX_MSG(CTestDialog)
// NOTE: the ClassWizard will add member functions here
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};



Рис. 1. Пример диалога.

Как видно, все элементы данного окна не являются отдельными объектами, а являются просто ресурсами Windows. Это значительно усложняет манипулирование ими, если вы хотите работать с ними как-то по-особенному, и вынуждает программистов заниматься самодеятельностью, пытаясь самим реализовать то, что по идее должна позволять делать сама библиотека. Тексты программ с использованием MFC испещрены маловразумительными макросами, детали работы которых не посмотришь средствами отладчика. Class Wizard ориентируется в тексте программы через комментарии страшного вида типа "//{{AFX_MSG(CTestDialog)", и не дай бог несведущему человеку что-то в них случайно изменить! Более на помощь Class Wizard он может не рассчитывать. Ну и, как я уже писал раньше, в MFC не реализован целый перечень очень нужных при визуальном программировании классов. Писать на MFC, не понимая, как реально работает сама Windows, практически невозможно.

Все эти проблемы Microsoft пытается решить, предлагая средство быстрой разработки приложений по имени Visual Basic. Да, писать программы на нем ну очень просто! Особенно людям, не очень хорошо разбирающимся в программировании. Однако в этой среде наблюдается перегиб в другую сторону - полное сокрытие механизмов работы ОС. Многие вещи просто невозможно реализовать, используя Visual Basic.

Фирма Borland сумела создать продукт, в котором мощь C++ сочетается с простотой разработки программ на Visual Basic. Полностью визуальное программирование интерфейса приложения, каждый элемент которого является отдельным объектом. Библиотека полностью скрывает механизмы реализации интерфейса Windows, однако при желании программист может работать с ней на сколь угодно низком уровне, вмешиваясь в ее механизмы и модифицируя их. Например, если мы спроектируем окно, подобное вышеприведенному, но в C++ Builder, - каждый его элемент будет отдельным объектом, которым мы можем управлять как захотим: изменить его размеры, положение, надпись и даже удалить его вообще или создать копию. Причем средствами стандартного объектно-ориентированного программирования C++, вроде

delete Form1-> Btn1;

или

Form1-> Btn1-> Left = 50;

С/С++ от Intel

"Только мы знаем, как выжать из наших процессоров максимум производительности" - говорится в разделе сайта фирмы Intel, посвященному компилятору Intel.

И как показал вышеприведенный тест, эти слава являются безусловной правдой.

Компилятор Intel не является самостоятельной программой. Он встраивается как дополнительный компилятор в Visual C++ 5 или 6.

Помимо всего этого, он устанавливает дополнительный внешний отладчик. Компилятор полностью совместим по форматам файлов с родным компилятором Visual C++ так, что можно компилировать отдельные файлы проекта родным компилятором Microsoft, а другие - компилятором Intel. Потом все это без проблем собирается вместе в исполняемый файл.

Помимо качества оптимизатора, к положительным моментам относится гораздо большая требовательность компилятора Intel к правильности (ANSI-совместимости) C/C++ кода. Я даже иногда специально компилирую программу компилятором Intel, чтобы вычистить из нее все неоднозначности и щекотливые моменты, на которые компилятор Microsoft не обращает внимания.

Конечно, в компиляторе Intel реализована поддержка новейшего набора инструкций для Pentium III - SIMD. В документации имеется отдельная глава, посвященная этой особенности компилятора.

А недостатки у компилятора Intel тоже имеются. Это чудовищные требования к ресурсам компьютера при сборке C++ кода с оптимизацией, особенно с использованием шаблонов и стандартной библиотеки STL. При сборке одного моего модуля размером в 150 килобайт компилятор Intel сумел употребить в дело 1 Гб (я не преувеличиваю!!!) оперативной памяти.

Пришлось обдирать коллег по работе, наращивать память до 512 Мб (больше не было места на материнской плате), делать огромный своп-файл для Windows. В этом случае на Pentium-II-400 компиляция прошла минут за 20.

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

Вот, качаю... Посмотрим, - исправили ли они тут что-нибудь?

Скачать компилятор Intel можно с сервера этой фирмы. Раздается trial copy на 30 дней. Стоимость, если интересно, всего $275. Советую попробовать, хотя бы в целях выявления ошибок в ваших программах.

Итоги

Итак, как видно, в природе не существует во всех отношениях идеального компилятора C/C++. Одни выигрывают в качестве кода, а другие - в удобстве среды разработки. Идеала среди них нет. Но "хочу отметить, что каждый из компиляторов имеет право на существование" Поэтому опытный программист всегда подходит к выбору средств разработки для конкретной задачи с большим вниманием. От этого решения часто может зависеть судьба проекта в целом. Часто удобно писать разные части программы, используя разные компиляторы. Например, интерфейс разрабатывать в C++ Builder, а критичные ко времени выполнения функции реализовывать в Visual C++ или Intel C/C++, размещая их в динамически подгружаемых библиотеках.

Крайне вредно привязываться к какой-либо одной реализации C/C++ и не видеть положительных моментов у конкурирующего продукта, которые могут принести очевидную пользу: повысить качество вашей программы и уменьшить трудозатраты на ее разработку.

"Я знаю, что Паскалем (уже на подносе, как Delphi) захламляют мозги студентам вузов" - явный образец как не нужно мыслить при разработке программ. Паскаль имеет множество положительных свойств как язык программирования и очень нагляден в изучении, что и используется в вузах. Но защита честного имени Паскаля - тема для отдельной статьи, и с ней лучше справится кто-нибудь другой, хотя бы автор статьи о MODULA.

Евгений Балахонов


Компьютерная газета. Статья была опубликована в номере 20 за 2000 год в рубрике разное :: мелочи жизни

©1997-2024 Компьютерная газета