Як виникла мова С / С + +

Історія мови C / C + +. Приклад використання

Завдяки чому склався такий статус мови С? Історично ця мова невіддільний від операційної системи Unix, яка в наші дні переживає своє друге народження. 60-і роки були епохою становлення операційних систем і мов програмування високого рівня.

У той період для кожного типу комп’ютерів незалежно розроблялися ОС і компілятори, а нерідко навіть свої мови програмування (пригадаємо, наприклад, PL / I). У той же час, спільність виникають при цьому проблем вже стала очевидною. Відповіддю на усвідомлення цієї спільності стала спроба створити універсальну мобільну операційну систему, а для цього знадобився не менше універсальний і мобільний мова програмування. Такою мовою став З, а Unix стала першою ОС, практично повністю написаною на мові високого рівня.

Тісний зв’язок з Unix дала мови З такий полігон для обкатки, якого не було в той час ні у одного іншої мови. Завдання системного програмування по праву вважалися в той час найскладнішими в галузі. У більшості своїй вони були настільки машинно-залежними, що багато хто взагалі не мислили їх рішення інакше, ніж на асемблері. Мови високого рівня призначалися для прикладного програмування і лише дуже обмежено реалізовували функції, необхідні для системних робіт, причому часто тільки для певного типу машин.

Мова С з самого початку створювався так, щоб на ньому можна було писати системні завдання. Творці З не стали розробляти абстрактну модель виконавця мови, а просто реалізували в ньому ті можливості, в яких найбільше потребували практиці системного програмування. Це в першу чергу були кошти безпосередньої роботи з пам’яттю, структурні конструкції управління і модульна організація програми. І по суті більше нічого в мову включено не було. Все інше було віднесено до бібліотеки часу виконання. Тому недоброзичливці інший раз відгукуються про мову З як про структурний асемблері. Але що б вони не базікали, підхід виявився дуже вдалим. Завдяки йому був досягнутий новий рівень по співвідношенню простоти і можливостей мови.

Є, втім, ще один чинник, що визначив успіх мови. Творці дуже вміло розділили в ньому машинно-залежні і незалежні властивості. Завдяки цьому більшість програм вдається писати універсально – їх працездатність не залежить від архітектури процесора і пам’яті. Нечисленні ж апаратно-залежні частини коду можна локалізувати в окремих модулях. А користуючись препроцесором, можна створювати такі модулі, які при компіляції на різних платформах будуть породжувати відповідний машинно-залежний код.

Багато суперечок викликав синтаксис мови С. Застосовані в ньому прийоми скорочення запису при непомірному використанні можуть зробити програму абсолютно нечитаною. Але, як казав Дейкстра, – кошти не винні в тому, що їх безграмотно використовують. Насправді ж, запропоновані в З скорочення синтаксису відповідають найбільш часто зустрічається на практиці стереотипним ситуацій. Якщо вважати скорочення ідіомами для виразного і компактного представлення таких ситуацій, то користь від них стає безумовною і очевидною.

Отже, З виник як універсальна мова системного програмування. Але він не залишився в цих рамках. До кінця 80-х років мова З, відтіснивши Fortran з позиції лідера, завоював масову популярність серед програмістів у всьому світі і став використовуватися в самих різних прикладних задачах. Чималу роль тут зіграло поширення Unix (а значить і С) в університетському середовищі, де проходило підготовку нове покоління програмістів.

Як і всі мови, З поступово удосконалювався, але більшість удосконалень не носило радикального характеру. Найбільш істотним з них, мабуть, слід вважати введення суворої специфікації типів функцій, яка значно підвищила надійність межмодульного взаємодії на С. Всі такі вдосконалення були в 1989 році закріплені в стандарті ANSI який і понині визначає мову С.

Але якщо все так безхмарно, то чому ж ще продовжують використовуватися всі інші мови, що підтримує їх існування? Ахіллесовою п’ятою мови С стало те, що він виявився занадто низькорівневим для тих завдань, які поставили на порядок денний 90-і роки. Причому у цієї проблеми є два аспекти. З одного боку, в мову були вбудовані занадто низькорівневі засоби – перш за все це робота з пам’яттю і адресна арифметика. Недарма зміна розрядності процесорів дуже болісно відбивається на багатьох С-програмах. З іншого боку, в С бракує коштів високорівневих – абстрактних типів даних і об’єктів, поліморфізму, обробки виключень. Як наслідок, в програмах на С техніка реалізації завдання часто домінує над її змістовною стороною.

Перші спроби виправити ці недоліки стали робитися ще на початку 80-х років. Вже тоді Бьерн Страуструп в AT & T Bell Labs став розробляти розширення мови С під умовною назвою. Стиль ведення розробки цілком відповідав духу, в якому створювався і сама мова З, – у нього вводилися ті або інші можливості з метою зробити більш зручною роботу конкретних людей і груп. Перший комерційний транслятор нової мови, що отримав назву C + + з’явився в 1983 році. Він представляв собою препроцесор, що транслював програму в код на С. Однак фактичним народженням мови можна вважати вихід в 1985 році книги Страуструпа. Саме з цього моменту C + + починає набирати всесвітню популярність.

Головне нововведення C + + – механізм класів, що дає можливість визначати і використовувати нові типи даних. Програміст описує внутрішнє представлення об’єкту класу і набір функцій-методів для доступу до цього поданням. Однією із заповітних цілей при створенні C + + було прагнення збільшити відсоток повторного використання вже написаного коду. Концепція класів пропонувала для цього механізм спадкоємства. Спадкування дозволяє створювати нові (похідні) класи з розширеним уявленням і модифікованими методами, не зачіпаючи при цьому скомпільований код вихідних (базових) класів. Разом з тим спадкування забезпечує один з механізмів реалізації поліморфізму – базової концепції об’єктно-орієнтованого програмування, згідно з якою, для виконання однотипної обробки різних типів даних може використовуватися один і той же код. Власне, поліморфізм – теж один з методів забезпечення повторного використання коду.

Введення класів не вичерпує всіх новацій мови C + +. У ньому реалізовані повноцінний механізм структурної обробки виключень, відсутність якого в С значно ускладнювало написання надійних програм, механізм шаблонів – витончений механізм макрогенерації, глибоко вбудований в мову, що відкриває ще один шлях до повторної використовуваного коду, і багато іншого.

Таким чином, генеральна лінія розвитку мови була спрямована на розширення його можливостей шляхом введення нових високорівневих конструкцій при збереженні наскільки можливо повної сумісності з ANSI С. Звичайно, боротьба за підвищення рівня мови йшла і на другому фронті – ті ж класи дозволяють при грамотному підході ховати низькорівневі операції, так що програміст фактично перестає безпосередньо працювати з пам’яттю і системно-залежними сутностями. Однак мова не містить механізмів, що змушують розробника правильно структурувати програму, а автори не випустили ніяких систематичних рекомендацій по використанню його досить витончених конструкцій. Не подбали вони своєчасно і про створення стандартної бібліотеки класів, що реалізує найбільш часто зустрічаються структури даних.

Все це призвело до того, що багато розробники вимушені були самі досліджувати лабіринти мовної семантики і самостійно відшукувати успішно працюючі ідіоми. Так, наприклад, на першому етапі розвитку мови багато творців бібліотек класів прагнули побудувати єдину ієрархію класів із загальним базовим класом Object. Ця ідея була запозичена з Smalltalk – одного з найбільш відомих об’єктно-орієнтованих мов. Однак вона виявилася абсолютно нежиттєздатною в C + + – ретельно продумані ієрархії бібліотек класів виявлялися негнучкими, а робота класів – неочевидній. Для того щоб бібліотеками класів можна було користуватися, їх доводилося поставляти в початкових текстах.

Поява темплетний класів і зовсім спростувало цей напрям розвитку. Спадкуванням стали користуватися тільки в тих випадках, коли було потрібно породження спеціалізованої версії наявного класу. Бібліотеки стали складатися з окремих класів і невеликих незв’язаних один з одним ієрархій. Однак на цьому шляху стало знижуватися повторне використання коду, так як в C + + неможливо полиморфное використання класів з незалежних ієрархій. Повсюдне ж застосування темплетів веде до неприпустимого зростання обсягу скомпільованого коду – не будемо забувати, темплети реалізуються методами макрогенерації.

Один з важких недоліків C + +, успадкований ним від синтаксису З, полягає в доступності компілятору опису внутрішньої структури всіх використаних класів. Як наслідок, зміна внутрішньої структури представлення якого-небудь бібліотечного класу приводить до необхідності перекомпіляції всіх програм, де ця бібліотека використовується. Це сильно обмежує розробників бібліотек в частині їх модернізації, адже, випускаючи нову версію, вони повинні зберігати двійкову сумісність з попередньою. Саме ця проблема змушує багатьох фахівців вважати, що C + + непридатний для ведення великих і надвеликих проектів.

І все ж, незважаючи на перераховані недоліки і навіть на неготовність стандарту мови (це після п’ятнадцяти з гаком років використання!), C + + залишається одним з найбільш популярних мов програмування. Його сила насамперед у практично повної сумісності з мовою С. Завдяки цьому програмістам C + + доступні всі напрацювання, виконані на С. При цьому C + + навіть без використання класів привносить в С ряд настільки важливих додаткових можливостей і зручностей, що багато хто користується їм просто як поліпшеним З .

Що стосується об’єктної моделі C + +, то поки ваша програма не стала дуже великий (сотні тисяч рядків), нею цілком можна користуватися. Намітилася останнім часом тенденція переходу до компонентного програмного забезпечення тільки підсилює позиції C + +. При розробці окремо взятих компонентів недоліки C + + ще не виявляються, а зв’язування компонентів в працюючу систему проводиться вже не на рівні мови, а на рівні операційної системи.

У світлі всього сказаного перспективи C + + не виглядають похмурими. Хоча і монополія на ринку мов програмування йому не світить. Мабуть, з упевненістю можна стверджувати тільки те, що ще однією модернізації-розширення ця мова не переживе. Недарма, коли з’явилася Java, на неї звернули таку пильну увагу. Мова, близьке по синтаксису до C + +, а значить, що здається знайомим багатьом програмістам, був позбавлений від найбільш волаючих недоліків C + +, успадкованих їм з 70-х років. Однак не схоже, щоб Java справлялася з покладеної на неї деякими роллю.

Особлива роль мов C / C + + в сучасному програмуванні практично позбавляє сенсу приведення конкретних адрес в Інтернеті, де можна знайти матеріали по них. Таких місць просто занадто багато. Однак, якщо цікаво докладніше познайомитися з еволюцією C + +, то почніть з невеликої статті http://citforum.syzran.ru/programming/prg96/ 76.shtml.

Олександр Сергєєв, [email protected]

Для того, щоб наочно продемонструвати використання описаних мов на практиці нами була обрана завдання, в якій потрібно було ввести зі стандартного вводу або з файлу ряд цілих чисел, а потім вивести тільки непарні з них, причому в зворотному порядку проходження. Це одна з найпростіших завдань, яка істотним чином вимагає для свого рішення роботи з масивами, циклами, розгалуженням і введенням / висновком, а також дозволяє продемонструвати виклики підпрограм. При цьому вона обозрима і легко сприймається.

Лістинг 1. З

1 # include / * Підключаємо функції введення-виведення * /
2
3 void main (void)
4 {
5 int М [10]; / * Масив з 10 цілих, рахунок з 0 * /
6 int N;
7 for (N = 0; N<10; ++N) /* Вводим не более 10 чисел */
8 if (EOF == scanf (% d, M + N))
9 break; / * Якщо кінець файлу, перериваємо цикл * /
10
11 for (-N; N> = 0;-N) / * Проходимо масив у зворотному * /
12 if (M [N]% 2) / * порядку і виводимо непарні * /
13 printf (% d, M [N]);
14}

Рядок 3. У C / C + + виконання програми завжди починається з функції main.
Рядки 7 і 11. У заголовку циклу через крапку з комою вказуються початкова установка, умова продовження і правило перерахунку параметра циклу. Операції + + і – / – найвідоміші з скорочень мови З, що означають інкремент і декремент змінної, тобто збільшення і зменшення її значення на одиницю.
Рядок 8. Функція scanf вводить по формату, заданому першим параметром, значення змінних, адреси яких задані рештою параметрів. Тут адресу, куди вводиться значення, обчислюється за допомогою адресної арифметики, до адреси розташування масиву М додається зсув на N елементів. Той же ефект можна отримати, записавши & M [N].
Рядок 12. Операція% обчислює залишок від ділення. Умова оператора if вважається виконаним, якщо чисельне значення виразу відмінно від нуля.
Рядок 13. Функція printf – друк по формату діє аналогічно scanf, але замість адрес їй передаються значення, що підлягають висновку.

1 # include
2
3 template class Array
4 {
5 public: Array (T Size = 1): M (new T [Size]), N (Size), n (0) {}
6 Array (void) {delete [] М;}
7 T Count (void) const {return n;}
8 T operator [] (int i) const {return M [i];}
9 void Add (Т Data);
10 private:
11 T * М; / / Адреса розподіленої пам’яті
12 int N, n; / / N – розподілено; n – використано
13};
14
15 template void Array :: Add (T Data)
16 {if (Nn) / / Якщо використано всі розподілене
17 {int * P = new T [N + = 10]; / / місце, розподілимо побільше
18 for (int i = 0; i 19 P [i] = M [i];
20 delete [] М; / / звільнимо старе місце
21 М = P; / / запам’ятаємо нову адресу
22}
23 М [n + +] = Data; / / занесемо число в масив, збільшивши лічильник
24}
25
26 void main (void)
27 {Array A; / / Масив цілих змінного розміру
28 while (1) / / Нескінченний цикл
29 {int N;
30 cin >> N; / / cin – стандартний потік введення
31 if (cin.eof ()) break; / / Вихід з циклу по кінцю файлу
32 A.Add (N); / / Додаємо введене число в масив
33}
34 for (int N = A.Count () -1; N> = 0;-N) / / Проходимо по масиву
35 if (A [N]% 2)
36 cout < 37 } // Здесь вызовется деструктор Array, и освободит память

Рядки 3-13. Оголошується темплетний клас Аrray з параметром Т. Він являє собою масив змінного розміру об’єктів типу Т. Звичайно, в нашому завданні немає ніякої необхідності використовувати темплетний клас. Однак нам хотілося продемонструвати, як на C + + створюється поліморфна структура даних, здатна працювати з будь-яким типом елементів.
Рядок 5. Конструктор класу. У ньому инициализируется уявлення об’єкта. Наприклад, у полі М заноситься адреса блоку пам’яті, замовленого операцією new T [Size].
Рядок 8. Приклад перевантаження операції []. Функція operator [] буде викликатися, коли квадратні дужки з’являтимуться праворуч від об’єкту класу Array.
Рядок 9. Ця функція основна в реалізації. Вона додає елементи в масив, розширюючи його при необхідності. Оскільки вона складніша інших, її визначення винесене з опису класу. Функції, описані в тілі класу, реалізуються в C + + не викликом, а inline-підстановкою. Це прискорює роботу програми, хоча збільшує її розмір.
Рядки 15-24. Визначення функції Аrrау :: Add (T) (між іншим, це її повне ім’я).
Рядок 27. Створюємо об’єкт типу Array. Темплет Агга параметрізіруется типом int.

Коментування вимкнено .

  1. Семецкий коментує:

    Прикольно …
    Треба було добавітьпрограммістскую байку типу:
    Спершу був язичок А, потім В. Ну іпоследствіі …
    Було б цікавіше …

  2. autolord коментує:

    Здорово.
    великий СПС автору.