Чи вважалася мова програмування C мовою низького рівня, коли вона вийшла?


151

В даний час C вважається мовою низького рівня , але ще в 70-х його вважали низьким рівнем? Чи використовувався цей термін навіть тоді?

Багато популярних мов вищого рівня існували до середини 80-х років і пізніше, тому мені цікаво, чи змінився характер низького рівня з роками.


13
Як один із даних, приблизно в 1978 р. Деякі наставники програмування описали C як прославлену мову монтажу.
Бен Кроуелл

7
@BenCrowell Я не впевнений, що означає «прославлений» у контексті вашої заяви, але я зіткнувся з тим, що називаю C універсальною (= незалежною від платформи) мовою асемблера .
Мелебій

13
Цікаво, що може бути зроблено випадок, що C - це не низький рівень мови , і він зараз менший, ніж це було у 70-х, адже абстрактна машина C далекіше видалена із сучасного обладнання, ніж з PDP-11.
Том

5
Думаючи про це, ви також можете подумати про походження - Unix пишеться на C, c працює на Unix і крос-компілює unix на інші платформи. Все, що не потрібно для компіляції Unix, було непотрібним накладними витратами, а головна мета C полягала в тому, щоб якомога простіше писати / переносити компілятор. Для цього це був правильний рівень EXACT, тому я не вважаю C високим чи низьким рівнем, я вважаю це інструментом для порту Unix, який, як і майже всі інструменти Unix, надзвичайно пристосований до багатьох проблем.
Білл К

4
Варто зазначити, що ліс був винайдений у 1958 році
прем'єр

Відповіді:


144

Щоб відповісти на історичні аспекти питання:

Філософія дизайну пояснюється мовою програмування на C, яку написали Брайан Керніган та дизайнер C Денніс Річі, "K&R", про який ви, можливо, чули. Передмова до першого видання говорить

C - це не "дуже високий рівень" мови, ані "великий" ...

і вступ говорить

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

Список триває деякий час, перш ніж текст продовжується:

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

(У мене є лише друге видання з 1988 року, але коментар нижче вказує на те, що текст, що цитується, є однаковим у першому виданні 1978 року.)

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


42
Це відмінна відповідь! Достовірні історичні докази + свідчення актора, що найкраще інформується про значення низького та високого рівня в період, про який йдеться в ОП. До речі, я підтверджую, що цитати були вже у виданні 1978 року.
Крістоф

157

Це залежить від вашого визначення мови високого та низького рівня. Коли C був розроблений, все, що було вищого рівня, ніж збірка, вважалося мовою високого рівня. Це низька планка для очищення. Пізніше ця термінологія змістилася до того, що деякі сьогодні вважають навіть Java мовою низького рівня.

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

C свідомо залишає дорогі або важкі для реалізації функції, які вже були добре налагоджені на той час, наприклад

  • автоматичне управління пам’яттю
  • вкладені функції або закриття
  • основні ООП або супроводи
  • системи більш виразного типу (наприклад, типи з обмеженням діапазону, визначені користувачем типи, такі як типи записів, сильне введення тексту, ...)

C має деякі цікаві особливості:

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

У той час, коли C розроблявся, інші інноваційні мови, такі як COBOL, Lisp, ALGOL (на різних діалектах), PL / I, SNOBOL, Simula та Pascal, вже були опубліковані та / або широко використовувались для конкретних проблемних областей. Але більшість цих існуючих мов були призначені для програмування мейнфреймів або були академічними дослідницькими проектами. Наприклад, коли ALGOL-60 вперше був розроблений як універсальна мова програмування, необхідні технології та інформатика для його впровадження ще не існували. Деякі з них (деякі діалекти ALGOL, PL / I, Pascal) також були призначені для програмування низького рівня, але вони, як правило, мали складніші компілятори або були занадто безпечними (наприклад, відсутність необмежених покажчиків). Паскалю особливо не вистачає хорошої підтримки для масивів змінної довжини.

Порівняно з цими мовами, C відкидає "елегантні" та дорогі функції, щоб бути більш практичними для розвитку на низькому рівні. C ніколи не був насамперед дослідницьким проектом з мовної розробки. Натомість це було відхиленням ядра Unix на мінікомп'ютері PDP-11, який був порівняно обмежений ресурсами. Своя ніша (мінімалістична мова низького рівня для написання Unix з односхилим компілятором, який легко підключити), C абсолютно вигравала - і через 45 років вона все ще є мовою франкінгу програмування систем.


17
Лише невелике доповнення для людей, які не знають, що потрібно для існуючої сімейства ALGOL та мов Pascal: Ці мови мали лексично вкладені функції, де можна було отримати доступ до (локальної) змінної, оголошеної у зовнішніх функціях. Це означало або, що вам потрібно підтримувати "дисплей" - масив покажчиків до зовнішніх лексичних областей - при кожному виклику та поверненні функції (що змінив лексичний рівень) - або вам довелося ланцюг лексичних областей до стека та кожного такого змінного доступу Для його знаходження потрібні кілька непрямих стрибків стека. Дорогий! C все це викреслив. Я все ще сумую за цим.
davidbak

12
@davidbak: Система x86-64 System V ABI (яка називається умовою виклику на Linux / OS X) визначає %r10як "статичний покажчик ланцюга", саме про це ви говорите. Для C це лише черговий регістр подряпин, закріплений за викликом, але я думаю, що Паскаль використає його. (Вкладені функції GNU C використовують його для передачі вказівника до зовнішньої області, коли така функція не вбудована (наприклад, якщо ви зробите на неї вказівник функції, щоб компілятор створював батут машинного коду на стеку): Прийнятність регулярних використання r10 та r11 )
Пітер Кордес

2
@PeterCordes Захоплююче! Паскаль все ще широко використовувався, коли вийшла System V (хоча я не знаю, коли було визначено формальний ABS SysV). Ваша відповідна відповідь дуже інформативна.
davidbak

3
C має визначені користувачем типи (структура / об'єднання). Я підозрюю, що решта цих "функцій" залишилися поза нульовим або негативним використанням (якщо ви не вступаєте у змагання з прихованим кодом :-)), оскільки вони заважають досягти мети - збереження мови як простої, так і виразної .
jamesqf

6
@jamesqf: але дуже рано C не мав призначення структури; Я здогадуюсь, що вам доведеться запам’ятовувати або копіювати членів окремо, а не писати, a = b;щоб скопіювати цілу структуру так, як можна в ISO C89. Так, на початку C визначені користувачем типи, безумовно, були другого класу і їх можна було передавати лише посиланням як аргументи функцій. Відраза C до масивів, а також Чому C ++ підтримує присвоєння масивів масивів у структурах, але не взагалі?
Пітер Кордес

37

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

Автори C, насамперед Денніс Річі, були більш обережними, і в статті Bell System Technical Journal сказано, що "C - не дуже висока мова". З кривою посмішкою та наміром бути провокаційним, Деніс Річі сказав, що це мова низького рівня. Головною з його цілей дизайну для C було зберегти мову, близьку до машини, але забезпечити мобільність, тобто машинну незалежність.

Для отримання додаткової інформації зверніться до оригінальної статті BSTJ:

Дякую Деннісе. Нехай ти спочиваєш у спокої.


3
Якщо ви запитаєте мене, це в основному було набрано і приємніші синтаксичні обгортки для складання PDP.
einpoklum


2
@einpoklum Я схильний з цим погодитися. Я навчився C ще в 1980 році в університеті, на PDP-11 / 34a, як це відбувається, і його один з професорів описав як "переносну мову асемблера". Можливо, тому, що крім PDP-11, у нас в лабораторії було кілька машин Superbrain CP / M, які мали на собі компілятори C. en.wikipedia.org/wiki/Intertec_Superbrain
dgnuff

2
Також відмінна відповідь, заснована на перевірених історичних свідченнях (уау! Там була попередня версія K&R!).
Крістоф

7
@einpoklum Хіба це не дещо надумано? У мене була можливість записати код системи та програми в середині 80-х у асемблері (Z80 та M68K), "портативному асемблері" під назвою M (синтаксис PDP11 з абстрактним 16-бітовим реєстром та набором інструкцій) та C. У порівнянні з асемблером , C, безумовно, мова високого рівня: продуктивність програмування на C була на порядок вище, ніж у асемблера! Звичайно, жодних рядків (SNOBOL), ніякої підтримки нативного файлу даних (COBOL), не самогенеруючого коду (LISP), ніякої розширеної математики (APL), жодних об'єктів (SIMULA), тому ми можемо погодитись, що це було не дуже високо
Крістоф

21

Як я писав десь на цьому сайті, коли хтось називав схему управління пам'яттю malloc / free як "програмування низького рівня"

Смішно, як з часом змінюється визначення "низького рівня". Коли я вперше вчився програмувати, будь-яка мова, що забезпечувала стандартизовану модель купи, яка робить можливим простий шаблон розподілу / вільного, вважалася справді високим рівнем. У програмуванні низького рівня вам доведеться самостійно відслідковувати пам'ять (не розподіли, а самі місця пам'яті!), Або записувати власний розподільник купи, якщо ви відчуваєте справді фантазію.

З контексту це було на початку 90-х, добре після того, як вийшов С.


Чи не була стандартна бібліотека лише предметом, починаючи з стандартизації в кінці 80-х (ну, на основі існуючих API Unix)? Крім того, програмування в ядрі (що було початковою проблемою домену C) вимагає, звичайно, матеріалів низького рівня, таких як ручне управління пам'яттю. На той час C, мабуть, була мовою програмування найвищого рівня, на якій було написано серйозне ядро ​​(я думаю, що в даний час ядро ​​NT використовує досить велику кількість C ++).
амон

6
@hyde, я використовував Algol 60, два різних діалекти FORTRAN та кілька різних діалектів BASIC ще в 1970-х, і жодна з цих мов не мала вказівників чи розподілу купи.
Соломон повільно

7
Власне кажучи, ви все одно можете програмувати без malloc(), безпосередньо зателефонувавши brk(2)або mmap(2)керуючи отриманою пам'яттю самостійно. Це масовий ПДФА без жодної можливої ​​вигоди (якщо ви не будете реалізовувати подібну річ), але ви можете це зробити.
Кевін

1
@amon - за винятком чудових машин на базі стека Burroughs, які були запрограмовані в ALGOL знизу вгору ... і набагато раніше, ніж Unix. О, і, до речі, Multics, яка була натхненником для Unix: Написано в PL / I. Схожий на ALGOL, вищий рівень, ніж C.
davidbak

6
Насправді причина більшості з нас не використовує мультимедіа сьогодні, мабуть, пов’язана з тим, що вона працювала лише на пекельно дорогих мейнфреймах, тобто типовим шахрайським витратом на мейнфрейм дня та додатковими витратами на півтора кабінету спеціалізоване обладнання для реалізації віртуальної пам'яті з захистом. Коли 32-розрядні міні-комп’ютери, такі як VAX-11, вийшли всі, окрім банків та уряду, відклалися від IBM та Seven Dwarfs та перевели їх масштабну обробку на "міні" комп'ютери.
davidbak

15

Багато відповідей уже згадували старі статті, де сказано, як "C - це не мова високого рівня".

Однак я не можу протистояти накопиченню: багато, якщо не більшість або всі HLL на той час - Algol, Algol-60, PL / 1, Pascal - забезпечують перевірку меж масиву та числове виявлення переповнення.

Востаннє я перевірив переповнення буфера та цілих чисел, що були першопричиною багатьох уразливостей безпеки. ... Так, все-таки справа ...

Ситуація з динамічним управлінням пам'яттю була складнішою, але все-таки стиль malloc / free в стилі C був чудовим кроком назад в плані безпеки.

Отже, якщо ваше визначення рівня HLL включає "автоматично запобігає багато помилок низького рівня", ну, шкода стану кібербезпеки була б дуже різною, ймовірно, кращою, якби C та UNIX не сталися.


7
Оскільки я, мабуть, брав участь у впровадженні Intel MPX і компіляторі, що перевіряє вказівник / межі, які вивернулися, якщо це можливо, я можу посилатись на документи щодо їх продуктивності: по суті, 5-15%. Багато з цього стосується аналізів компіляторів, які були ледве можливими у 1970-х та 1980-х - порівняно з наївними чеками, які можуть бути на 50% повільнішими. Однак я вважаю, що справедливо сказати, що C і UNIX почали працювати над подібними аналізами до 20 років - коли C стала найпопулярнішою мовою програмування, попит на безпеку був значно меншим.
Krazy Glew

9
@jamesqf Крім того, багато машин до C мали спеціальну апаратну підтримку для перевірки меж і цілих переповнень. Оскільки C не використовував цей HW, він врешті-решт був устарений та видалений.
Krazy Glew

5
@jamesqf Наприклад: МІСА RISC ISA спочатку базувалася на орієнтирах Стенфорда, які спочатку були написані на Паскалі, лише пізніше перейшли до C. Оскільки Паскаль перевірив наявність підписаного цілого переповнення, отже, так це зробив MIPS у таких інструкціях, як ADD. Приблизно в 2010 році я працював у MIPS, мій бос хотів видалити невикористані інструкції в MIPSr6, і дослідження показали, що перевірки на переповнення майже ніколи не використовувалися. Але виявилося, що Javascript робив такі перевірки - але не міг користуватися дешевими інструкціями через відсутність підтримки ОС.
Krazy Glew

3
@KrazyGlew - Дуже цікаво, що ти це підводиш, оскільки в C / C ++ боротьба між користувачами та авторами-компіляторами за "невизначене поведінку" через підписане ціле число переливається, оскільки сьогодні письменники-компілятори взяли стару мантру "Зробити неправильну програма гірше - не гріх ", і це було до 11. Багато повідомлень про stackoverflow та інших місцях відображають це ...
davidbak

2
Якби у Расті були SIMD-елементи, подібні до C, це може бути майже ідеальною сучасною мовою переносної збірки. C стає все гірше і гірше як переносна мова збірки через агресивну оптимізацію на основі UB, і не в змозі перенести на портативі нові примітивні операції, які підтримують сучасні процесори (як popcnt, підрахунок ведучих / кінцевих нулів, біт-реверс, реверс байтів, насичення математика). Отримати компілятори C для ефективного використання ASM для цих процесорів із підтримкою HW часто потрібні непереносні внутрішні елементи. Мати компілятор емуляції popcnt на цілях без нього краще, ніж розпізнавання ідіоми popcnt.
Пітер Кордес

8

Розглянемо старіші та набагато вищі мови, що передували C (1972):

Фортран - 1957 (не набагато вищий рівень, ніж C)

Лісп - 1958

Кобол - 1959 рік

Фортран IV - 1961 р. (Не набагато вищий рівень, ніж С)

PL / 1 - 1964 рік

APL - 1966

Плюс мова середнього рівня, наприклад RPG (1959), в основному мова програмування для заміни систем запису на основі плат.

З цієї точки зору, C здавався мовою дуже низького рівня, лише трохи вище макроскладачів, які використовувались на мейнфреймах у той час. У випадку мейнфреймів IBM, макроси асемблера використовувались для доступу до баз даних, таких як BDAM (основний метод доступу до диска), оскільки інтерфейси бази даних не були перенесені на Cobol (на той час), що призвело до спадщини суміші складання та Програми Cobol, які досі використовуються сьогодні в основній системі IBM.


2
Якщо ви хочете перелічити більш старі, вищі мови, не забувайте LISP .
Дедуплікатор

@Deduplicator - я додав його до списку. Я зосереджувався на тому, що використовувалося в мейнфреймах IBM, і я не пам'ятаю, щоб LISP був таким популярним, але APL також була нішевою мовою для мейнфреймів IBM (через консолі обміну часом) та IBM 1130. Подібно до LISP, APL є однією з більш унікальні мови високого рівня, наприклад, подивіться, наскільки мало коду потрібно для створення гри життя Конвея з поточною версією APL: youtube.com/watch?v=a9xAKttWgP4 .
rcgldr

2
Я б не вважав RPG або COBOL як особливо високого рівня, принаймні, не раніше COBOL-85. Коли ви подряпаєте поверхню COBOL, ви бачите, що це по суті сукупність дуже просунутих макросів асемблера. Для початку не вистачає як функцій, процедур, так і рекурсії, а також будь-якого виду. Усі сховища повинні бути оголошені у верхній частині програми, що призводить до або надзвичайно довгих увертюр, або до болючого змінного повторного використання.
idrougge

У мене є погані спогади про використання Fortran IV, я не пам'ятаю, щоб він був помітно "вищим", ніж C.
DaveG

@idrougge - COBOL та RPG, а також набори інструкцій для мейнфреймів включали повну підтримку упакованого або розпакованого BCD, вимога до фінансового програмного забезпечення в таких країнах, як США. Я вважаю, що пов'язані оператори, такі як "переміщення відповідно", є високим рівнем. RPG був незвичним тим, що ви вказали зв'язок між необробленими полями введення та відформатованими полями виводу та / або акумуляторами, але не порядок операцій, аналогічно програмованому на ньому платі.
rcgldr

6

Відповідь на ваше запитання залежить від того, на якій мові мови вона задається.

Мова, описана в довідковому посібнику Денніса Річі 1974 р. С, була мовою низького рівня, яка пропонувала деякі зручності програмування мов вищого рівня. Діалекти, похідні від цієї мови, також мали тенденцію бути мовами програмування низького рівня.

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

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

Щоб зрозуміти це розрізнення, подумайте, як мова Річі і C89 переглядатиме фрагмент коду:

struct foo { int x,y; float z; } *p;
...
p[3].y+=1;

на платформі, де "char" - 8 біт, "int" - 16 біт "big-endian", "float" - 32 біт, а структури не мають спеціальних вимог до вирівнювання або вирівнювання, тому розмір "struct foo" становить 8 байт.

У мові Річі поведінка останнього твердження прийме адресу, збережену в "p", додасть до неї 3 * 8 + 2 [тобто 26] байт і отримає 16-бітове значення з байтів за цією адресою та наступним , додайте до цього значення одне, а потім запишіть це 16-бітове значення на ті самі два байти. Поведінка буде визначена як дія 26-го та 27-го байтів, що слідує за адресою p, не зважаючи на те, який об’єкт там зберігався.

У мові, визначеній стандартом C, у випадку, якщо * p ідентифікує елемент "struct foo []", за яким слідують щонайменше ще три повноцінні елементи цього типу, останнє твердження додало б його до члена y третій елемент після * р. Поведінка не визначається Стандартом ні за яких інших обставин.

Мова Річі була мовою програмування низького рівня, оскільки, хоча вона дозволяла програмісту використовувати абстракції, як масиви та структури, коли це зручно, вона визначала поведінку з точки зору базового компонування об’єктів у пам'яті. Навпаки, мова, описана C89 та пізнішими стандартами, визначає речі з точки зору абстракції вищого рівня і лише визначає поведінку коду, яка відповідає цьому. Впровадження якості, придатне для програмування низького рівня, буде вести себе корисно у більшості випадків, ніж це передбачено Стандартом, але немає "офіційного" документа, який би вказував, що потрібно зробити, щоб придатність була такою метою.

Таким чином, мова С, винайдена Деннісом Річі, є мовою низького рівня і була визнана такою. Однак мова, винайдена Комітетом зі стандартів С, ніколи не була мовою низького рівня за відсутності гарантій, що забезпечуються впровадженням, що виходять за рамки мандатів Стандарту.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.