Історія мови Сі: 100% чистий Сі, без єдиного «плюса»

image

Популярність мови програмування Сі важко переоцінити, особливо згадуючи його колишні заслуги. Напевно, кожен розробник, як мінімум, знає про його існування, і, як максимум, пробував на ньому програмувати. Сі є попередником таких мов, як C++, Objective-C, C#, Java.

Компанія Microsoft для розробки рідної мови до своєї платформі .Net вибрала саме Сі-подібний синтаксис. Більш того, на Сі написано безліч операційних систем.

Звичайно, Сі не ідеальний: творці мови – Кен Томпсон і Деніс Рітчі – довгий час доопрацьовували його. Стандартизація Сі триває досі. Він існує більше 45 років і активно використовується.

З ним часто асоціюють не один, а дві мови програмування — C/C++. Однак нижче мова піде саме про «чистому» Сі.

Мова Сі сходить корінням до мови ALGOL (розшифровується як ALGorithmic Language), який був створений в 1958 році спільно з комітетом Європейських та Американських вчених в галузі комп'ютерних наук на зустрічі у Швейцарської вищої технічної школи Цюріха. Мова була відповіддю на деякі недоліки мови FORTRAN і спробою їх виправити. Крім того, розробка Сі тісно пов'язана з створенням операційної системи UNIX, над якою працювали Кен Томпсон і Деніс Рітчі.

UNIX

Проект МАС (Multiple Access Computer, Machine-Aided Cognition, Man and Computer) почався як суто дослідницький в МАССАЧУСЕТСЬКОМУ технологічному інституті в 1963 році.

В рамках проекту МАС була розроблена операційна система CTSS (Compatible Time-Sharing System). У другій половині 60-х було створено кілька інших систем з поділом часу, наприклад, BBN, DTSS, JOSS, SDC і Multiplexed Information and Computing Service (MULTICS) в тому числі.

Multics – спільна розробка MIT, Bell Telephone Laboratories (BTL) і General Electric (GE) по створенню ОС з поділом часу для комп'ютера GE-645. Останній комп'ютер під управлінням Multics вимкнули 31 жовтня 2000 року.

Однак BTL відійшов від цього проекту ще на початку 1969 року.

Деякі його співробітники (Кен Томпсон, Денніс Рітчі, Стью Фельдман, Дуг Макілрой, Боб Морріс, Джо Оссанна) захотіли продовжити роботу самостійно. Томпсон працював над грою Space Travel на GE-635. Її написали спочатку для Multics, а потім переписали на Фортране під GECOS на GE-635. Гра моделювала тіла Сонячної системи, а гравцеві треба було посадити корабель куди-небудь на планету або супутник.

Ні софт, ні залізо цього комп'ютера не годилися для такої гри. Томпсон шукав альтернативу, і переписав гру під безхазяйний PDP-7. Пам'ять була об'ємом 8К 18-бітних слів, і ще був процесор векторного дисплея для виведення красивою для того часу графіки.

image
Изображение з сайту slideshare.net

Томпсон і Рітчі повністю вели розробку на крос-асемблері на GE і переносили код на перфострічки. Томпсону це активно не подобалося, і він почав писати ОС для PDP-7, починаючи з файлової системи. Так з'явилася UNIX.
Томпсон хотів створити комфортне обчислювальне оточення, сконструйований згідно з його дизайном, використовуючи будь-які доступні засоби. Його задуми, що очевидно озираючись назад, вбирали в себе багато інновації Multics, включаючи поняття процесу як основи управління, деревоподібну файлову систему, інтерпретатор команд в якості користувача програми, спрощене уявлення текстових файлів і узагальнений доступ до пристроїв.
PDP-7 UNIX також поклав початок високорівневому мові B, який створювався під впливом мови BCPL. Денніс Рітчі сказав, що — це Сі без типів. BCPL містився в 8 Кб пам'яті і був ретельно перероблений Томпсоном. Поступово виріс у С.

image
Изображение з сайту it-world.com
До 1973 році мова Сі став досить сильний, і велика частина ядра UNIX, спочатку написана на асемблері PDP-11/20, була переписана на Сі. Це було одне з найперших ядер операційних систем, написане мовою, відмінному від асемблера.
Виходить, що Сі – це «супутній продукт, отриманий під час створення операційної системи UNIX.

Прабатьки Сі

Натхненні мовою ALGOL-60, Математична лабораторія Кембриджського Університету спільно з Комп'ютерним відділом Лондонського університету створили в 1963 році мова CPL (Combined Programming Language).

Мова CPL вважають складним, і у відповідь на це Мартіном Річардсоном був створений у 1966 році мова BCPL, основне призначення якого полягало в написанні компіляторів. Зараз він практично не використовується, але в свій час з-за хорошою портируемости він грав важливу роль.
BCPL використовувався на початку 1970-х в кількох цікавих проектах, в числі яких — операційна система OS6 і частково в зароджуються розробках Xerox PARC.
BCPL став предком для мови Бі (B), розробленого в 1969 у вже знайомій всім AT&T Bell Telephone Laboratories, не менш знайомими Кеном Томпсоном і Деннісом Рітчі.

Як і інші операційні системи того часу, UNIX була написана на асемблері. Налагодження програм на асемблері справжня мука. Томпсон вирішив, що для подальшої розробки ОС необхідний мову високого рівня і придумав невеликий мова B. За основу Томпсон взяв мова BCPL. Мова B можна розглядати як C без типів.

У багатьох деталях BCPL, B і C розрізняються синтаксично, але в основному вони схожі. Програми складаються з послідовності глобальних декларацій та оголошень функцій (процедур). В BCPL процедури можуть бути вкладеними, але не можуть посилатися на нестатические об'єкти визначені містять їх процедурах. B і C уникають такого обмеження, вводячи більш суворе: вкладених процедур немає взагалі. Кожна з мов (за винятком самих давніх версій B) підтримує роздільну компіляцію і надає засоби для включення тексту з назв файлів.

В протилежність повсюдного зміни синтаксису, яке відбувалося під час створення B, основна семантика BCPL — його структура типів і правила обчислення виразів — залишилася недоторканою. Обидві мови — безтиповые, вірніше мають єдиний тип даних — «слово» або «осередок», набір бітів фіксованої довжини. Пам'ять у цих мовах — масив таких осередків, а сенс вмісту комірки залежить від операції, який до неї застосовується. Наприклад, оператор "+" просто складає свої операнди за допомогою машинної інструкції add, і інші арифметичні операції також байдужі до змісту своїх операндів.

Ні BCPL, ні B, ні C не виділяють в мові символьні дані; вони вважають рядка векторами цілих чисел і доповнюють загальні правила кількома угодами. І в BCPL, і в B рядковий літерал означає адреса статичної області ініціалізований символами рядка упакованими в осередку.

Як створювався Сі

У 1970 Bell Labs придбала для проекту комп'ютер PDP-11. Так як B був готовий до роботи на PDP-11, Томпсон переписав частину UNIX B.

Але модель B і BCPL передбачала витрати при роботі з покажчиками: правила мови, визначаючи вказівник як індекс у масиві слів, робили покажчики індексами слів. Кожне звернення до покажчика при виконанні генерувало масштабування вказівника на адресу байта, який очікував процесор.

Тому ставало ясно, що для того, щоб впоратися з символами і байтовой адресацією, а також підготуватися до майбутньої апаратної підтримки обчислень з плаваючою точкою, потрібна типізація.

У 1971 році Рітчі почав створювати розширену версію B. Спочатку він назвав її NB (New B), але коли мова стала сильно відрізнятися від B, назву змінили на C. Ось що, писав про це сам Рітчі:

Я хотів, щоб структура не тільки характеризувала абстрактний об'єкт, але й описувала набір біт, який міг бути прочитано з каталогу. Де компілятор зміг би сховати курсор, напаме, якого вимагає семантика? Навіть якби структури були задумані більш абстрактними, і місце для покажчиків могло б бути заховано де-небудь, як би я вирішив технічну проблему коректної ініціалізації цих покажчиків при виділенні пам'яті для складного об'єкта, можливо структури містить масиви, які містять структури, і так до довільної глибини?

Рішення полягало в рішучий стрибок в еволюційному ланцюжку між безтиповым BCPL і типізованих C. Він виключав матеріалізацію покажчика в сховище, а замість цього породжував його створення, коли ім'я масиву згадувалося у виразі. Правило, яке збереглося і в сьогоднішньому C, полягає в тому, що значення–масиви, коли вони згадуються у виразі, конвертуються в покажчики на перший з об'єктів, що становлять цей масив.

Друге нововведення, яке найбільш ясно відрізняє C від його попередників, — ось ця більш повна структура типів і особливо її виразність у синтаксисі декларацій. NB пропонував основні типи int і char спільно з масивами з них і покажчиками на них, але ніяких інших способів скомпонувати їх.

Вимагалося узагальнення: для об'єкта будь-якого типу має бути можливим описати новий об'єкт, який об'єднує кілька таких об'єктів в масив, отримує його з функції або є покажчиком на нього.
image
Изображение з книги «Мова Сі»: M. Уейт, С. Прата, Д. Мартін

Для будь-якого об'єкта такого складеного типу, вже був спосіб вказати на об'єкт, який є його частиною: індексувати масив, викликати функцію, використовувати з покажчиком оператор непрямого звернення. Аналогічне міркування призводило до синтаксису оголошення імен, який відображає синтаксис вираження, де ці імена використовуються. Так
int i, *pi, **ppi;
оголошує ціле, покажчик на ціле і покажчик на покажчик на int. Синтаксис цих оголошень відображає той факт, що i, *pi, і **ppi все в результаті дають тип int, коли використовуються у виразі. Схожим чином
int f(), *f(), (*f)();
оголошують функцію, яка повертає ціле, функція повертає покажчик на ціле, вказівник на функцію повертає ціле;
int *api[10], (*pai)[10];
оголошують масив покажчиків на ціле, вказівник на масив цілих.

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

70-ті роки: «смутний час» і лже-діалекти

Мова до 1973 став досить стабільний для того, щоб на ньому можна було переписати UNIX. Перехід на C забезпечив важлива перевага: переносимість. Написавши компілятор C для кожної з машин в Bell Labs, команда розробників могла перенести на них UNIX.

З приводу виникнення мови Сі Пітер Мойлан у своїй книзі «The case against C» пише: «Потрібен був мову, здатний обійти деякі жорсткі правила, вбудовані в більшість мов високого рівня і забезпечують їх надійність. Потрібен був такий язик, який дозволив би робити те, що до нього можна було реалізувати тільки на асемблері або на рівні машинного коду».

C продовжив розвиватися в 70-х. У 1973-1980-х роках мова трохи підріс: структура типів отримала беззнакові, довгі типи, об'єднання і перерахування, структури стали близькими до об'єктів–класів (не вистачало тільки нотації для літералів).

Перша книга по Сі. Книга «Мова програмування Сі», написана Брайаном Керниганом і Деннісом Рітчі і опублікована в 1978 році, стала біблією програмістів на Сі. При відсутності офіційного стандарту ця книга – відома також як K&R, або «Біла Книга», як люблять називати шанувальники сі – фактично стала стандартом.

image
Изображение з сайту learnc.info

У 70-х програмістів на Сі було небагато і більшість з них були користувачами UNIX. Тим не менш, у 80-х Сі вийшов за вузькі рамки світу UNIX. Компілятори Сі стали доступні на різних машинах, що працюють під управлінням різних операційних систем. Зокрема, Сі став поширюватися на швидко розвивається платформі IBM PC.

K&R ввів наступні особливості мови:

• структури (тип даних struct);
• довге ціле (тип даних long int);
• ціле без знака (тип даних unsigned int);
• оператор += і подібні йому (старі оператори =+ вводили аналізатор лексики компілятора Сі в оману, наприклад, при порівнянні виразів i =+ 10 і i = +10).

K&R C часто вважають найголовнішою частиною мови, яку повинен підтримувати компілятор Сі. Багато років навіть після виходу ANSI Сі він вважався мінімальним рівнем, якого слід дотримуватися програмістам, які бажають домогтися від своїх програм максимальної переносимості, тому що не всі компілятори тоді підтримували ANSI C, а хороший код на K&R C був вірний і для ANSI C.

Разом із зростанням популярності з'явилися проблеми. Програмісти, писали нові компілятори брали за основу мову, описаний в K&R. На жаль, в K&R деякі особливості мови були описані розпливчасто, тому компілятори часто трактували їх на свій розсуд. Крім того, в книзі не було чіткого поділу між тим, що є особливістю мови, а що особливістю операційної системи UNIX.

Після публікації K&R C мову було додано декілька можливостей, підтримуваних компіляторами AT&T, і деяких інших виробників:

• функції, що не повертають значення типу void), та покажчики, які не мають типу (типу void *);
• функції, які повертають об'єднання і структури;
• імена полів даних структур у різних просторах імен для кожної структури;
• присвоювання структур;
• спецификатор констант const);
• стандартна бібліотека, що реалізує більшу частину функцій, введених різними виробниками;
• перечислимый тип (enum);
• дробове число одинарної точності (float).

Погіршувало ситуацію й те, що після публікації K&R Сі продовжував розвиватися: у нього додавалися нові можливості і з нього вирізалися старі. Незабаром з'явилася очевидна необхідність у вичерпному, точному і відповідає сучасним вимогам описі мови. Без такого стандарту стали з'являтися діалекти мови, які заважали переносимості – найсильнішої стороні мови.

Стандарти

В кінці 1970-х років, мова Сі почав витісняти BASIC, який в той час був провідним в області програмування мікрокомп'ютерів. У 1980-х роках він був адаптований під архітектуру IBM-PC, що призвело до значного стрибка його популярності.

image

Розробкою стандарту мови Сі зайнявся Американський національний інститут стандартів (ANSI). При ньому в 1983 році був сформований комітет X3J11, який зайнявся розробкою стандарту. Перша версія стандарту була випущена в 1989 році і отримала назву С89. У 1990, внісши невеликі зміни в стандарт, його прийняла Міжнародна Організація Стандартизації ISO. Тоді він став відомий під кодом ISO/IEC 9899:1990, але в середовищі програмістів закріпилася назва, пов'язане з роком прийняття стандарту: С90. Останньої на даний момент версією стандарту є стандарт ISO/IEC 9899:1999, також відомий як С99, який був прийнятий у 2000 році.

image

Серед нововведень стандарту С99 варто звернути увагу на зміну правила, що стосується місця оголошення змінних. Тепер нові змінні можна було оголошувати посеред коду, а не тільки на початку складового блоку або в глобальній області видимості.

Деякі особливості C99:

• підставляються функції (inline);
• оголошення локальних змінних в будь-якому операторі програмного тексту (як в C++);
• нові типи даних, такі, як long long int (для полегшення переходу від 32 до 64-бітним числом), явний булевий) тип даних _Bool і тип complex для представлення комплексних чисел;
• масиви змінної довжини;
• підтримка обмежених покажчиків (restrict);
• іменована ініціалізація структур: struct { int x, y, z; } point = { .y=10, .z=20, .x=30 };
• підтримка однорядкових коментарів, що починаються на //, запозичених з C++ (багато компілятори Сі підтримували їх і раніше в якості доповнення);
• кілька нових бібліотечних функцій, таких, як snprintf;
• кілька нових заголовних файлів, таких, як stdint.h.

Стандарт С99 зараз в більшій чи меншій мірі підтримується всіма сучасними компіляторами мови Сі. В ідеалі, код написаний на Сі з дотриманням стандартів і без використання апаратно — і системно-залежних викликів, ставав як апаратно — так і платформно-незалежних кодом.

У 2007 році почалися роботи над наступним стандарту мови Сі. 8 грудня 2011 опублікований новий стандарт для мови Сі (ISO/IEC 9899:2011). Деякі можливості нового стандарту вже підтримуються компіляторами GCC і Clang.

Основні особливості С11:

• підтримка багатопоточності;
• покращена підтримка Юнікоду;
• узагальнені макроси (type-generic expressions, дозволяють статичні перевантаження);
• анонімні структури та об'єднання (спрощують звернення до вкладених конструкцій);
• управління вирівнюванням об'єктів;
• статичні затвердження (static assertions);
• вилучення небезпечної функції gets (на користь безпечної gets_s);
• функція quick_exit;
• спецификатор функції _Noreturn;
• новий режим ексклюзивного відкриття файлу.

Незважаючи на наявність стандарту 11 року, багато компілятори досі не підтримують повністю навіть версії C99.

За що критикують Сі

У нього досить високий поріг входження, що ускладнює його використання в навчанні в якості першої мови програмування. Програмуючи на Сі, потрібно враховувати безліч деталей. «Будучи народжений в середовищі хакерів, він стимулює відповідний стиль програмування, часто небезпечний, і заохочує написання заплутаного коду», пишет Вікіпедія.

Більш глибоку і аргументовану критику висловив Пітер Мойлан. Він присвятив критиці Сі цілих 12 сторінок. Наведемо кілька фрагментів:

Проблеми з модульність
Модульне програмування на мові Сі можливо, але лише в тому випадку, коли програміст дотримується ряду досить жорстких правил:

• На кожен модуль повинен припадати рівно один header-файл. Він повинен містити лише експортовані прототипи функцій, опису і нічого іншого (крім коментарів).

• Зовнішньої викликає процедурі про це модулі повинні бути відомі тільки коментарі в header-файлі.

• Для перевірки цілісності кожен модуль повинен імпортувати свій власний header-файл.

• Для імпорту будь-якої інформації з іншого модуля кожен модуль повинен містити рядки #include, а також коментарі, що показують, що, власне, імпортується.

• Прототипи функцій можна використовувати тільки в header-файли. (Це правило необхідно, оскільки мова Сі не має механізму перевірки того, що функція реалізується в тому ж модулі, що і її прототип; так що використання прототипу може маскувати помилку «відсутності функції» — «missing function»).

• Будь-яка глобальна змінна в модулі, і будь-яка функція, крім тієї, що імпортується через header-файл, повинні бути оголошені статичними.

• Слід передбачити попередження компілятора «виклик функції без прототипу» (function call without prototype); таке попередження завжди слід розглядати як помилку.

• Програміст повинен упевнитися в тому, що кожному прототипу, заданому в header — файлі, відповідає реалізована під таким же ім'ям в тому ж модулі неприватная (тобто нестатична у звичайній термінології Сі) функція. На жаль, природа мови Сі автоматичну перевірку цього робить неможливою.

• Слід з підозрою ставитися до будь-якого використання утиліти grep. Якщо прототип розташований не на своєму місці, то це, швидше за все, помилка.

• В ідеалі програмісти, що працюють в одній команді, не повинні мати доступу до вихідних файлів один одного. Вони повинні спільно використовувати лише об'єктні модулі і header-файли.

Очевидна складність в тому, що мало хто буде слідувати цим правилам, бо компілятор не вимагає їх неухильно дотримуватись. Модульний мова програмування щонайменше частково захищає хороших програмістів від того хаосу, який створюють погані програмісти. А мова Сі цього зробити не в силах.
image
Изображение з сайту smartagilee.com

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

Можна розрізняти важливі і неважливі покажчики. Важливим у нашому розумінні вважається покажчик, необхідний для створення і підтримання структури даних.

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

Перша полягає в тому, що в середовищі програмістів, які використовують мову Сі, стало традицією створювати покажчики навіть там, де вже існують інші нічим не поступаються їм методи доступу, наприклад, при перегляді елементів масиву.

Друга причина — правило мови Сі, згідно з яким всі параметри функцій повинні передаватися за значенням. Коли вам потрібен еквівалент VAR-параметра мови Паскаль або inout — параметра мови Ada, єдине рішення полягає в тому, щоб передати вказівник. Цим багато в чому пояснюється погана читаність програм на мові Сі.

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

Сі – живий

Згідно даними на червень 2016 року, індекс TIOBE, який вимірює зростання популярності мов програмування, показав, що C займає 2 місце:



Нехай хтось скаже, що Сі застарів, що його широке поширення — наслідок удачі і активного PR. Нехай хтось скаже, що без UNIX мова Сі ніколи б не створили.

Тим не менш, Сі став свого роду стандартом. Він, так чи інакше, пройшов випробування часом відміну від багатьох інших мов. Сі-розробники досі затребувані, а творців мови IT-спільнота згадує добрим словом.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.