Jump to content

Си (язык программирования)

Страница защищена ожидающими изменениями
(Перенаправлено с C (программирование) )

С
Текст гласит: «Язык программирования C».
Логотип, использованный на обложке первого издания The C Programming Language. [1]
Парадигма Мультипарадигмальность : императивная ( процедурная ), структурированная.
Разработано Деннис Ричи
Разработчик ANSI X3J11 ( ANSI C ); ISO/IEC JTC 1 (Объединенный технический комитет 1) / SC 22 (Подкомитет 22) / WG 14 (Рабочая группа 14) (ISO C)
Впервые появился 1972 год ; 52 года назад ( 1972 ) [2]
Стабильная версия
С17 / июнь 2018 г .; 6 лет назад ( 2018-06 )
Предварительный выпуск
С23 ( Н3220 ) / 21 февраля 2024 г .; 5 месяцев назад ( 21.02.2024 ) [3]
Дисциплина набора текста Статический , слабый , явный , номинальный
ТЫ Кросс-платформенный
Расширения имен файлов .с, .ч
Веб-сайт www .iso .org /стандартный /74528 .html
www .open-std .org /jtc1 /sc22 /wg14 /
Основные реализации
pcc , GCC , Clang , Intel C , C++Builder , Microsoft Visual C++ , Watcom C
Диалекты
Cyclone , Unified Parallel C , Split-C , Cilk , C*
Под влиянием
Б ( BCPL , CPL ), АЛГОЛ 68 , [4] ПЛ/И , ФОРТРАН
Под влиянием
Многочисленные : AMPL , AWK , csh , C++ , C-- , C# , Objective-C , D , Go , Java , JavaScript , JS++ , Julia , Limbo , LPC , Perl , PHP , Pike , Processing , Python , Rust , Seed7 , В (Вланг) , Вала , Верилог (ЛПВП), [5] Ним , Зиг

C ( произносится / ˈ s / – как буква c ) [6] язык программирования общего назначения . Он был создан в 1970-х годах Деннисом Ритчи и остается очень широко используемым и влиятельным. По замыслу функции C четко отражают возможности целевых процессоров. Он нашел прочное применение в операционных систем коде (особенно в ядрах) . [7] ), драйверы устройств и стеки протоколов , но его использование в прикладном программном обеспечении сокращается. [8] C обычно используется в компьютерных архитектурах, от крупнейших суперкомпьютеров до самых маленьких микроконтроллеров и встроенных систем .

Преемник языка программирования B , C был первоначально разработан в Bell Labs Ритчи между 1972 и 1973 годами для создания утилит, работающих на Unix . Он был применен для повторной реализации ядра операционной системы Unix. [9] В 1980-е годы C постепенно набирал популярность. Он стал одним из наиболее широко используемых языков программирования. [10] [11] с компиляторами C, доступными практически для всех современных компьютерных архитектур и операционных систем. Книга «Язык программирования C» , соавтором которой является первоначальный разработчик языка, в течение многих лет служила фактическим стандартом для языка. [12] [1] C стандартизируется с 1989 года Американским национальным институтом стандартов (ANSI), а затем совместно Международной организацией по стандартизации (ISO) и Международной электротехнической комиссией (IEC).

C — императивный процедурный язык, поддерживающий структурное программирование , область видимости лексических переменных и рекурсию , со статической системой типов . Он был разработан для компиляции с целью обеспечения низкоуровневого доступа к памяти и языковым конструкциям, которые эффективно сопоставляются с машинными инструкциями , и все это с минимальной поддержкой во время выполнения . Несмотря на свои низкоуровневые возможности, язык был разработан для поощрения кроссплатформенного программирования. программа Соответствующая стандартам на языке C, написанная с учетом переносимости , может быть скомпилирована для широкого спектра компьютерных платформ и операционных систем с небольшими изменениями в ее исходном коде.

С 2000 года C неизменно входит в тройку лучших языков в индексе TIOBE , показателе популярности языков программирования. [13]

Деннис Ритчи (справа), изобретатель языка программирования C, с Кеном Томпсоном

C — императивный процедурный язык в традиции АЛГОЛА . Он имеет статическую систему типов . В языке C весь исполняемый код содержится в подпрограммах (также называемых «функциями», но не в смысле функционального программирования ). Параметры функции передаются по значению, хотя массивы передаются как указатели , то есть адрес первого элемента массива. Передача по ссылке моделируется в C путем явной передачи указателей на объект, на который имеется ссылка.

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

Язык C также обладает следующими характеристиками:

  • В языке имеется небольшое фиксированное количество ключевых слов, включая полный набор примитивов потока управления : if/else, for, do/while, while, и switch. Пользовательские имена не отличаются от ключевых слов никакими символами .
  • Он имеет большое количество арифметических, побитовых и логических операторов: +, +=, ++, &, ||, и т. д.
  • одного присваивания. более В одном операторе может выполняться
  • Функции:
    • Возвращаемые значения функции можно игнорировать, если они не нужны.
    • Указатели на функции и данные допускают специальный полиморфизм во время выполнения .
    • Функции не могут быть определены в лексической области действия других функций.
    • Переменные могут быть определены внутри функции с областью действия .
    • Функция может вызывать сама себя, поэтому рекурсия . поддерживается
  • Типизация данных статична , но соблюдается слабо ; все данные имеют тип, но неявные преобразования . возможны
  • определяемые пользователем ( typedef Возможны ) и составные типы.
    • Гетерогенные совокупные типы данных ( struct) позволяют получить доступ к связанным элементам данных и назначить их как единое целое. Содержимое целых структур нельзя сравнивать с помощью одного встроенного оператора (элементы необходимо сравнивать индивидуально).
    • Союз — это структура с перекрывающимися членами; он позволяет нескольким типам данных использовать одну и ту же ячейку памяти.
    • Индексация массива — это вторичная запись, определяемая в терминах арифметики указателей. Целые массивы нельзя присваивать или сравнивать с помощью одного встроенного оператора. Ключевое слово «массив» не используется и не определяется; вместо этого квадратные скобки синтаксически обозначают массивы, например month[11].
    • Перечислимые типы возможны с помощью enum ключевое слово. Они свободно конвертируются в целые числа.
    • Строки не являются отдельным типом данных, а традиционно реализуются как с нулевым завершением . массивы символов
  • Низкоуровневый доступ к памяти компьютера возможен путем преобразования машинных адресов в указатели .
  • Процедуры (подпрограммы, не возвращающие значения) представляют собой особый случай функции с пустым типом возвращаемого значения. void.
  • Память может быть выделена программе с помощью вызовов библиотечных процедур .
  • Препроцессор и выполняет макроса определение , включение файла исходного кода условную компиляцию .
  • Существует базовая форма модульности : файлы можно компилировать отдельно и связывать вместе, при этом можно контролировать, какие функции и объекты данных видны другим файлам через static и extern атрибуты.
  • Сложные функции, такие как ввод-вывод , манипуляции со строками и математические функции, последовательно делегируются библиотечным подпрограммам .
  • Сгенерированный код после компиляции имеет относительно простые требования к базовой платформе, что делает его пригодным для создания операционных систем и использования во встроенных системах.

Хотя C не включает в себя определенные функции, имеющиеся в других языках (такие как объектная ориентация и сборка мусора ), их можно реализовать или эмулировать, часто с помощью внешних библиотек (например, GLib Object System или сборщика мусора Boehm ).

Отношения с другими языками

[ редактировать ]

Многие более поздние языки заимствовали прямо или косвенно из C, включая C++ , C# Unix , оболочку C , D , Go , Java , JavaScript (включая транспиляторы ), Julia , Limbo , LPC , Objective-C , Perl , PHP , Python , Ruby , Rust , Swift , Verilog и SystemVerilog (языки описания оборудования). [5] Эти языки заимствовали многие из своих управляющих структур и других основных функций из C. Большинство из них также имеют синтаксис, очень похожий на C, и они имеют тенденцию сочетать узнаваемый синтаксис выражений и операторов C с базовыми системами типов, моделями данных и семантикой. это может быть кардинально иначе.

Ранние разработки

[ редактировать ]
Хронология языка C
Год Неофициальный
имя
Официальный
стандартный
1972 первый выпуск
1978 К&Р С
1989,
1990
АНСИ С , С89,
ИСО С, С90
АНСИ X3.159-1989
ИСО/МЭК 9899:1990.
1999 С99 , С9Х ИСО/МЭК 9899:1999.
2011 С11 , С1Х ИСО/МЭК 9899:2011
2018 С17 ИСО/МЭК 9899:2018
2024 С23 , С2Х ИСО/МЭК 9899:2024
Будущее C2Y

Происхождение C тесно связано с разработкой операционной системы Unix , первоначально реализованной на языке ассемблера на PDP-7 Деннисом Ритчи и Кеном Томпсоном , включившей в себя несколько идей коллег. В конце концов, они решили портировать операционную систему на PDP-11 . Исходная версия Unix PDP-11 также была разработана на языке ассемблера. [9]

Томпсону нужен был язык программирования для разработки утилит для новой платформы. Сначала он пытался написать компилятор Фортрана , но вскоре отказался от этой идеи. Вместо этого он создал урезанную версию недавно разработанного языка системного программирования под названием BCPL . Официальное описание BCPL в то время не было доступно. [14] и Томпсон изменил синтаксис, сделав его менее многословным и похожим на упрощенный АЛГОЛ, известный как СМАЛГОЛ. [15] назвал результат B. Томпсон [9] Он описал B как «семантику BCPL с большим количеством синтаксиса SMALGOL». [15] Как и BCPL, B имел самозагружающийся компилятор, упрощающий портирование на новые машины. [15] Однако в конечном итоге на языке B было написано лишь несколько утилит, поскольку он был слишком медленным и не мог использовать преимущества функций PDP-11, таких как байтовая адресация.

Новый выпуск B и первый выпуск C

[ редактировать ]

В 1971 году Ричи начал улучшать B, чтобы использовать возможности более мощного PDP-11. Существенным дополнением стал символьный тип данных. Он назвал это New B (NB). [15] Томпсон начал использовать NB для написания ядра Unix , и его требования определили направление развития языка. [15] [16] К 1972 году в язык NB были добавлены более богатые типы: в NB были массивы int и char. Также были добавлены указатели, возможность генерировать указатели на другие типы, массивы всех типов и типы, возвращаемые функциями. Массивы внутри выражений стали указателями. Был написан новый компилятор, и язык был переименован в C. [9]

Компилятор C и некоторые созданные с его помощью утилиты были включены в версию 2 Unix , также известную как Research Unix . [17]

Структуры и перезапись ядра Unix

[ редактировать ]

В версии 4 Unix , выпущенной в ноябре 1973 года, ​​Unix ядро было широко переработано на языке C. [9] К этому времени язык C приобрел некоторые мощные функции, такие как struct типы.

Препроцессор PL был представлен примерно в 1973 году по настоянию Алана Снайдера , а также в знак признания полезности механизмов включения файлов, доступных в BCPL и /I . Его первоначальная версия содержала только включенные файлы и простые замены строк: #include и #define макросов без параметров. Вскоре после этого он был расширен, в основном Майком Леском , а затем Джоном Райзером, для включения макросов с аргументами и условной компиляции . [9]

Unix был одним из первых ядер операционной системы, реализованных на языке, отличном от ассемблера . Более ранние примеры включают систему Multics (написанную на PL/I ) и программу главного управления (MCP) для Burroughs B5000 (написанную на ALGOL ) в 1961 году. Примерно в 1977 году Ричи и Стивен К. Джонсон внесли дальнейшие изменения в язык, облегчающий переносимость операционной системы Unix. Джонсона Портативный компилятор C послужил основой для нескольких реализаций C на новых платформах. [16]

Обложка книги «Язык программирования C» , первое издание. Брайана Кернигана и Денниса Ритчи

В 1978 году Брайан Керниган и Деннис Ритчи опубликовали первое издание «Языка программирования C» . [18] Книга, известная как K&R по инициалам авторов, долгие годы служила неформальным описанием языка. Описанную в нем версию C обычно называют « K&R C ». Поскольку он был выпущен в 1978 году, теперь его также называют C78 . [19] Второе издание книги [20] охватывает более поздний стандарт ANSI C , описанный ниже.

K&R представила несколько языковых функций:

  • Стандартная библиотека ввода-вывода
  • long int тип данных
  • unsigned int тип данных
  • Составные операторы присваивания вида =op (такой как =-) были изменены на форму op= (то есть, -=), чтобы удалить семантическую двусмысленность, создаваемую такими конструкциями, как i=-10, что было интерпретировано как i =- 10 (уменьшение i на 10) вместо возможно предполагаемого i = -10 (позволять i быть −10).

Даже после публикации стандарта ANSI 1989 года K&R C в течение многих лет все еще считался « наименьшим общим знаменателем », которым ограничивались программисты C, когда была желательна максимальная переносимость, поскольку многие старые компиляторы все еще использовались, и поскольку тщательно написанный K&R Код C также может быть законным стандартом C.

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

Например:

long some_function(); /* This is a function declaration, so the compiler can know the name and return type of this function. */
/* int */ other_function(); /* Another function declaration.  Because this is an early version of C, there is an implicit 'int' type here.  A comment shows where the explicit 'int' type specifier would be required in later versions. */

/* int */ calling_function() /* This is a function definition, including the body of the code following in the { curly brackets }. Because no return type is specified, the function implicitly returns an 'int' in this early version of C. */
{
    long test1;
    register /* int */ test2; /* Again, note that 'int' is not required here. The 'int' type specifier */
                              /* in the comment would be required in later versions of C. */
                              /* The 'register' keyword indicates to the compiler that this variable should */
                              /* ideally be stored in a register as opposed to within the stack frame. */
    test1 = some_function();
    if (test1 > 1)
          test2 = 0;
    else
          test2 = other_function();
    return test2;
}

The int закомментированные спецификаторы типа могут быть опущены в K&R C, но они потребуются в более поздних стандартах.

Поскольку объявления функций K&R не включали никакой информации об аргументах функций, проверки типов параметров функции не выполнялись, хотя некоторые компиляторы выдавали предупреждающее сообщение, если локальная функция вызывалась с неправильным количеством аргументов или если разные вызовы внешней функции использовали разные числа или типы аргументов. Были разработаны отдельные инструменты, такие как утилита Unix lint , которая (помимо прочего) могла проверять согласованность использования функций в нескольких исходных файлах.

За годы, прошедшие после публикации K&R C, в язык было добавлено несколько функций, поддерживаемых компиляторами AT&T (в частности PCC). [21] ) и некоторых других поставщиков. В их число вошли:

  • void функции (т. е. функции без возвращаемого значения)
  • функции, возвращающие struct или union типы (ранее мог быть возвращен только один указатель, целое число или число с плавающей запятой)
  • задание для struct типы данных
  • перечислимые типы (ранее использовались определения препроцессора для целочисленных фиксированных значений, например #define GREEN 3)

Большое количество расширений и отсутствие соглашения о стандартной библиотеке , а также популярность языка и тот факт, что даже компиляторы Unix не реализовали точно спецификацию K&R, привели к необходимости стандартизации. [22]

ANSI C и ISO C

[ редактировать ]

В конце 1970-х и 1980-х годах версии C были реализованы для широкого спектра мэйнфреймов , мини-компьютеров и микрокомпьютеров , включая IBM PC , поскольку его популярность начала значительно возрастать.

В 1983 году Американский национальный институт стандартов (ANSI) сформировал комитет X3J11 для установления стандартной спецификации C. X3J11 основал стандарт C на реализации Unix; однако непереносимая часть библиотеки Unix C была передана 1003 IEEE, рабочей группе чтобы стать основой для стандарта POSIX 1988 года . В 1989 году стандарт C был ратифицирован как ANSI X3.159-1989 «Язык программирования C». Эту версию языка часто называют ANSI C , Standard C или иногда C89.

В 1990 году стандарт ANSI C (с изменениями форматирования) был принят Международной организацией по стандартизации (ISO) как ISO/IEC 9899:1990, который иногда называют C90. Следовательно, термины «C89» и «C90» относятся к одному и тому же языку программирования.

ANSI, как и другие национальные органы по стандартизации, больше не разрабатывает стандарт C самостоятельно, а подчиняется международному стандарту C, поддерживаемому рабочей группой ISO/IEC JTC1/SC22 /WG14. Национальное принятие обновления международного стандарта обычно происходит в течение года после публикации ISO.

Одной из целей процесса стандартизации C было создание расширенного набора K&R C, включающего многие из появившихся впоследствии неофициальных функций. Комитет по стандартам также включил несколько дополнительных функций, таких как прототипы функций (заимствованные из C++), void указатели, поддержка международных наборов символов и локалей , а также улучшения препроцессора. Хотя синтаксис объявлений параметров был расширен за счет включения стиля, используемого в C++, интерфейс K&R по-прежнему разрешался для совместимости с существующим исходным кодом.

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

В тех случаях, когда код должен быть скомпилирован либо компиляторами, соответствующими стандарту, либо компиляторами на основе K&R C, __STDC__ Макрос можно использовать для разделения кода на разделы Standard и K&R, чтобы предотвратить использование в компиляторе K&R C функций, доступных только в Standard C.

После процесса стандартизации ANSI/ISO спецификация языка C оставалась относительно неизменной в течение нескольких лет. В 1995 году была опубликована Нормативная поправка 1 к стандарту C 1990 года (ISO/IEC 9899/AMD1:1995, неофициально известная как C95), в которой были исправлены некоторые детали и добавлена ​​более обширная поддержка международных наборов символов. [23]

Стандарт C был дополнительно пересмотрен в конце 1990-х годов, что привело к публикации ISO/IEC 9899:1999 в 1999 году, который обычно называют « C99 ». С тех пор в него трижды вносились технические исправления. [24]

В C99 появилось несколько новых функций, включая встроенные функции , несколько новых типов данных (в том числе long long int и complex тип для представления комплексных чисел ), массивы переменной длины и гибкие члены массива , улучшенная поддержка IEEE 754 плавающей запятой , поддержка макросов с переменным числом аргументов (макросы переменной арности ) и поддержка однострочных комментариев, начинающихся с //, как в BCPL или C++. Многие из них уже были реализованы как расширения в нескольких компиляторах C.

C99 по большей части обратно совместим с C90, но в некоторых отношениях более строг; в частности, объявление, в котором отсутствует спецификатор типа, больше не имеет int неявно предполагается. Стандартный макрос __STDC_VERSION__ определяется значением 199901L чтобы указать, что доступна поддержка C99. GCC , Solaris Studio и другие компиляторы C теперь [ когда? ] поддерживают многие или все новые функции C99. Однако компилятор C в Microsoft Visual C++ реализует стандарт C89 и те части C99, которые необходимы для совместимости с C++11 . [25] [ нужно обновить ]

Кроме того, стандарт C99 требует поддержки идентификаторов, использующих Unicode, в виде экранированных символов (например, \u0040 или \U0001f431) и предлагает поддержку необработанных имен в Юникоде.

В 2007 году началась работа над еще одной версией стандарта C, неофициально названной «C1X», до официальной публикации ISO / IEC 9899: 2011 08 декабря 2011 г. Комитет по стандартам C принял рекомендации по ограничению внедрения новых функций, которые не были протестированы существующими реализациями.

Стандарт C11 добавляет множество новых функций в C и библиотеку, включая общие макросы типов, анонимные структуры, улучшенную поддержку Unicode, атомарные операции, многопоточность и функции с проверкой границ. Он также делает некоторые части существующей библиотеки C99 необязательными и улучшает совместимость с C++. Стандартный макрос __STDC_VERSION__ определяется как 201112L чтобы указать, что доступна поддержка C11.

C17, опубликованный в июне 2018 года под названием ISO/IEC 9899:2018, является текущим стандартом языка программирования C. Он не вводит никаких новых функций языка, а только технические исправления и разъяснения дефектов C11. Стандартный макрос __STDC_VERSION__ определяется как 201710L чтобы указать, что доступна поддержка C17.

C23 — неофициальное название следующей (после C17) основной версии стандарта языка C. На протяжении большей части своего развития он был неофициально известен как «C2X». Ожидается, что C23 будет опубликован в начале 2024 года как ISO/IEC 9899:2024. [26] Стандартный макрос __STDC_VERSION__ определяется как 202311L чтобы указать, что доступна поддержка C23.

C2Y — это временное неофициальное название следующей основной версии стандарта языка C после C23 (C2X), которая, как ожидается, будет выпущена позднее в десятилетии 2020-х годов , отсюда цифра «2» в «C2Y». Ранний рабочий проект C2Y был выпущен в феврале 2024 года под номером N3220 рабочей группой ISO/IEC JTC1/SC22 /WG14. [27]

Встроенный C

[ редактировать ]

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

В 2008 году Комитет по стандартам C опубликовал технический отчет, расширяющий язык C. [28] Чтобы решить эти проблемы, предоставив общий стандарт для всех реализаций. Он включает в себя ряд функций, недоступных в обычном C, таких как арифметика с фиксированной запятой , именованные адресные пространства и базовая аппаратная адресация ввода-вывода.

Синтаксис

[ редактировать ]

C имеет формальную грамматику, определенную стандартом C. [29] Окончания строк обычно не имеют значения в C; однако границы строк имеют значение на этапе предварительной обработки. Комментарии могут появляться либо между разделителями /* и */, или (начиная с C99) после // до конца строки. Комментарии разделены /* и */ не вложены, и эти последовательности символов не интерпретируются как разделители комментариев, если они появляются внутри строковых или символьных литералов. [30]

Исходные файлы C содержат объявления и определения функций. Определения функций, в свою очередь, содержат объявления и операторы . Объявления либо определяют новые типы, используя такие ключевые слова, как struct, union, и enumили назначьте типы и, возможно, зарезервируйте место для новых переменных, обычно записывая тип, за которым следует имя переменной. Ключевые слова, такие как char и int укажите встроенные типы. Разделы кода заключаются в фигурные скобки ( { и }, иногда называемые «фигурными скобками»), чтобы ограничить область объявлений и действовать как единый оператор для управляющих структур.

В качестве императивного языка C используются операторы для определения действий . Наиболее распространенным оператором является оператор выражения , состоящий из вычисляемого выражения, за которым следует точка с запятой; В качестве побочного эффекта оценки могут быть вызваны переменным функции и присвоены новые значения. Чтобы изменить обычное последовательное выполнение операторов, C предоставляет несколько операторов потока управления, идентифицируемых зарезервированными ключевыми словами. Структурированное программирование поддерживается if ... [ else] условное исполнение и do ... while, while, и for итеративное выполнение (циклическое выполнение). for Оператор имеет отдельные выражения инициализации, тестирования и повторной инициализации, любое или все из которых можно опустить. break и continue можно использовать внутри цикла. Break используется для выхода из самого внутреннего оператора цикла, а continue используется для перехода к его повторной инициализации. Существует также неструктурированный goto оператор, который переходит непосредственно к назначенной метке внутри функции. switch выбирает case который будет выполнен на основе значения целочисленного выражения. В отличие от многих других языков, поток управления переходит к следующему языку. case если не прекращено break.

Выражения могут использовать различные встроенные операторы и содержать вызовы функций. Порядок, в котором оцениваются аргументы функций и операнды большинства операторов, не указан. Оценки могут даже чередоваться. Однако все побочные эффекты (включая сохранение в переменных) возникнут до следующей « точки последовательности »; Точки последовательности включают конец каждого оператора выражения, а также вход и возврат из каждого вызова функции. Точки последовательности также возникают при вычислении выражений, содержащих определенные операторы ( &&, ||, ?: и оператор запятая ). Это обеспечивает высокую степень оптимизации объектного кода компилятором, но требует от программистов C большей осторожности для получения надежных результатов, чем это необходимо для других языков программирования.

Керниган и Ритчи говорят во введении к языку программирования C : «C, как и любой другой язык, имеет свои недостатки. Некоторые операторы имеют неправильный приоритет; некоторые части синтаксиса могли бы быть лучше». [31] Стандарт C не пытался исправить многие из этих недостатков из-за влияния таких изменений на уже существующее программное обеспечение.

Набор символов

[ редактировать ]

Базовый исходный набор символов C включает следующие символы:

Новая строка указывает на конец текстовой строки; он не обязательно должен соответствовать отдельному символу, хотя для удобства C рассматривает его как один.

В строковых литералах можно использовать дополнительные многобайтовые закодированные символы, но они не полностью переносимы . Последний стандарт C ( C11 ) позволяет переносимо встраивать символы Юникода из разных стран в исходный текст C с помощью \uXXXX или \UXXXXXXXX кодировка (где X обозначает шестнадцатеричный символ), хотя эта функция пока широко не реализована.

Базовый набор символов выполнения C содержит те же символы, а также представления для alert , backspace и возврата каретки . Поддержка расширенных наборов символов во время выполнения увеличивалась с каждой версией стандарта C.

Зарезервированные слова

[ редактировать ]

Следующие зарезервированные слова чувствительны к регистру .

C89 имеет 32 зарезервированных слова, также известных как ключевые слова, которые нельзя использовать ни для каких целей, кроме тех, для которых они предопределены:

C99 зарезервировал еще пять слов: (‡ — альтернативный вариант написания ключевого слова C23)

C11 зарезервировал еще семь слов: [32] (‡ — альтернативный вариант написания ключевого слова C23)

  • _Alignas
  • _Alignof
  • _Atomic
  • _Generic
  • _Noreturn
  • _Static_assert
  • _Thread_local

C23 зарезервировал еще пятнадцать слов:

  • alignas
  • alignof
  • bool
  • constexpr
  • false
  • nullptr
  • static_assert
  • thread_local
  • true
  • typeof
  • typeof_unqual
  • _BitInt
  • _Decimal32
  • _Decimal64
  • _Decimal128

Большинство недавно зарезервированных слов начинаются с подчеркивания, за которым следует заглавная буква, поскольку идентификаторы этой формы ранее были зарезервированы стандартом C для использования только в реализациях. Поскольку существующий исходный код программы не должен был использовать эти идентификаторы, это не повлияет на то, когда реализации C начнут поддерживать эти расширения языка программирования. Некоторые стандартные заголовки определяют более удобные синонимы для подчеркнутых идентификаторов. Некоторые из этих слов были добавлены как ключевые слова с их обычным написанием в C23, а соответствующие макросы были удалены.

До C89, entry было зарезервировано как ключевое слово. Во втором издании своей книги «Язык программирования C» , в которой описывается то, что стало известно как C89, Керниган и Ричи написали: «… [ключевое слово] entry, ранее зарезервированный, но никогда не использовавшийся, больше не зарезервирован» и «Мёртворожденный entry ключевое слово отозвано». [33]

Операторы

[ редактировать ]

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

C использует оператор = (используется в математике для выражения равенства) для обозначения присваивания, следуя прецедентам Фортрана и PL/I , но в отличие от АЛГОЛА и его производных. C использует оператор == проверить на равенство. Сходство между этими двумя операторами (присваивание и равенство) может привести к случайному использованию одного вместо другого, и во многих случаях ошибка не приводит к появлению сообщения об ошибке (хотя некоторые компиляторы выдают предупреждения). Например, условное выражение if (a == b + 1) может быть ошибочно записано как if (a = b + 1), который будет оцениваться как true если только значение a является 0 после задания. [34]

C Приоритет операторов не всегда интуитивно понятен. Например, оператор == связывается более жестко, чем (выполняется до) операторов & (побитовое И) и | (побитовое ИЛИ) в таких выражениях, как x & 1 == 0, который необходимо записать как (x & 1) == 0 если это намерение кодера. [35]

Пример «Привет, мир»

[ редактировать ]
"Привет, мир!" программа Брайана Кернигана (1978)

Пример « hello, world », появившийся в первом издании K&R , стал образцом вводной программы в большинстве учебников по программированию. Программа выводит «привет, мир» на стандартный вывод , который обычно представляет собой терминал или экран.

Оригинальная версия была: [36]

main()
{
    printf("hello, world\n");
}

Соответствующая стандарту программа «Привет, мир»: [а]

# include <stdio.h>

int main(void)
{
    printf("hello, world\n");
}

Первая строка программы содержит директиву предварительной обработки , обозначенную #include. Это приводит к тому, что компилятор заменяет эту строку всем текстом stdio.h стандартный заголовок, который содержит объявления для стандартных функций ввода и вывода, таких как printf и scanf. Угловые скобки, окружающие stdio.h указать, что stdio.h можно найти с помощью стратегии поиска, которая предпочитает заголовки, предоставленные компилятором, другим заголовкам с тем же именем, в отличие от двойных кавычек, которые обычно включают локальные или специфичные для проекта файлы заголовков.

Следующая строка указывает, что функция с именем main определяется. main функция служит специальной цели в программах на языке C; среда выполнения вызывает main функция для начала выполнения программы. Спецификатор типа int указывает, что значение, возвращаемое вызывающей стороне (в данном случае среде выполнения) в результате оценки main функция, является целым числом. Ключевое слово void поскольку список параметров указывает, что эта функция не принимает аргументов. [б]

Открывающая фигурная скобка указывает на начало определения main функция.

Следующая строка вызывает (перенаправляет выполнение) функцию с именем printf, который в данном случае предоставляется из системной библиотеки . В этом звонке printf функции передается (предоставляется) один аргумент — адрес первого символа строкового литерала. "hello, world\n". Строковый литерал представляет собой безымянный массив с элементами типа char, автоматически устанавливается компилятором с последним символом NULL (значение ASCII 0) для обозначения конца массива (для printf чтобы узнать длину строки). Символ NULL также можно записать как escape-последовательность , записанную как \0. \n — это escape-последовательность , которую C преобразует в символ новой строки , который на выходе означает конец текущей строки. Возвращаемое значение printf функция имеет тип int, но он молча отбрасывается, поскольку не используется. (Более осторожная программа могла бы проверить возвращаемое значение, чтобы определить, действительно ли printf функция завершилась успешно.) Точка с запятой ; завершает заявление.

Закрывающая фигурная скобка указывает на конец кода для main функция. Согласно спецификации C99 и новее, main функция, в отличие от любой другой функции, неявно возвращает значение 0 по достижении } это завершает функцию. (Раньше явное return 0; требовался оператор.) Это интерпретируется системой времени выполнения как код выхода, указывающий на успешное выполнение. [37]

Типы данных

[ редактировать ]

Система типов в C статична и слабо типизирована , что делает ее похожей на систему типов потомков АЛГОЛА, таких как Паскаль . [38] Существуют встроенные типы для целых чисел различных размеров, как знаковых, так и беззнаковых, чисел с плавающей запятой и перечислимых типов ( enum). Целочисленный тип char часто используется для однобайтовых символов. В C99 добавлен логический тип данных . Существуют также производные типы, включая массивы , указатели , записи ( struct) и союзы ( union).

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

Некоторые считают синтаксис объявления C неинтуитивным, особенно для указателей на функции . (Идея Ричи заключалась в том, чтобы объявлять идентификаторы в контексте, напоминающем их использование: « объявление отражает использование ».) [39]

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

Указатели

[ редактировать ]

C поддерживает использование указателей — типа ссылки , которая записывает адрес или расположение объекта или функции в памяти. Указатели можно разыменовать для доступа к данным, хранящимся по указанному адресу, или для вызова указанной функции. Указателями можно манипулировать с помощью присваивания или арифметики указателей . Представление значения указателя во время выполнения обычно представляет собой необработанный адрес памяти (возможно, дополненный полем смещения внутри слова), но поскольку тип указателя включает тип объекта, на который он указывает, выражения, включающие указатели, могут быть проверены по типу. во время компиляции. Арифметика указателей автоматически масштабируется в соответствии с размером типа данных, на который указывает.

Указатели используются в C для многих целей. Текстовые строки обычно обрабатываются с помощью указателей на массивы символов. Динамическое выделение памяти осуществляется с помощью указателей; результат malloc обычно приводится к типу сохраняемых данных. Многие типы данных, такие как деревья , обычно реализуются как динамически выделяемые. struct объекты, связанные между собой с помощью указателей. Указатели на другие указатели часто используются в многомерных массивах и массивах struct объекты. Указатели на функции ( указатели функций ) полезны для передачи функций в качестве аргументов функциям более высокого порядка (таким как qsort или bsearch ), в таблицах диспетчеризации или в качестве обратных вызовов обработчикам событий . [37]

нулевого указателя Значение явно указывает на отсутствие допустимого местоположения. Разыменование значения нулевого указателя не определено, что часто приводит к ошибке сегментации . Значения нулевого указателя полезны для указания особых случаев, таких как отсутствие указателя «следующий» в конечном узле связанного списка или в качестве индикации ошибки в функциях, возвращающих указатели. В соответствующих контекстах исходного кода, например, при присвоении переменной указателя, константа нулевого указателя может быть записана как 0, с явным приведением к типу указателя или без него, поскольку NULL макрос, определенный несколькими стандартными заголовками или, начиная с C23, с константой nullptr. В условных контекстах значения нулевого указателя оцениваются как false, в то время как все остальные значения указателя оцениваются как true.

Пустые указатели ( void *) указывают на объекты неопределенного типа и поэтому могут использоваться как «универсальные» указатели данных. Поскольку размер и тип объекта, на который указывает указатель, неизвестны, указатели void не могут быть разыменованы, а арифметика указателей на них не допускается, хотя их можно легко (и во многих контекстах неявно так и делают) преобразовать в указатель любого другого объекта и из него. тип. [37]

Неосторожное использование указателей потенциально опасно. Поскольку они обычно не отмечены флажком, переменную-указатель можно указать на любое произвольное место, что может вызвать нежелательные эффекты. Хотя правильно используемые указатели указывают на безопасные места, их можно заставить указывать на небезопасные места, используя недопустимую арифметику указателей ; объекты, на которые они указывают, могут продолжать использоваться после освобождения ( висячие указатели ); их можно использовать без инициализации ( дикие указатели ); или им может быть напрямую присвоено небезопасное значение с помощью приведения, объединения или другого поврежденного указателя. В общем, C позволяет манипулировать типами указателей и преобразовывать их между собой, хотя компиляторы обычно предоставляют опции для различных уровней проверки. Некоторые другие языки программирования решают эти проблемы, используя более ограничительные ссылочные типы.

Типы массивов в C традиционно имеют фиксированный статический размер, указанный во время компиляции. Более поздний стандарт C99 также допускает использование массивов переменной длины. Однако также возможно выделить блок памяти (произвольного размера) во время выполнения, используя стандартную библиотеку. malloc функцию и рассматривать ее как массив.

Поскольку доступ к массивам (фактически) всегда осуществляется через указатели, доступ к массиву обычно не проверяется по размеру базового массива, хотя некоторые компиляторы могут предоставлять проверку границ в качестве опции. [40] [41] Поэтому возможны нарушения границ массива, которые могут привести к различным последствиям, включая незаконный доступ к памяти, повреждение данных, переполнение буфера и исключения во время выполнения.

В C нет специального средства для объявления многомерных массивов , а скорее полагается на рекурсию внутри системы типов для объявления массивов массивов, что фактически выполняет то же самое. Значения индекса результирующего «многомерного массива» можно рассматривать как увеличивающиеся в порядке возрастания строк . Многомерные массивы обычно используются в числовых алгоритмах (в основном из прикладной линейной алгебры ) для хранения матриц. Структура массива C хорошо подходит для этой конкретной задачи. Однако в ранних версиях C границы массива должны быть известны фиксированными значениями или явно передаваться в любую подпрограмму, которая их требует, а к массивам массивов с динамическим размером нельзя получить доступ с помощью двойной индексации. (Обходным решением этой проблемы было выделение массива с дополнительным «вектором-строкой» указателей на столбцы.) C99 представил «массивы переменной длины», которые решают эту проблему.

В следующем примере с использованием современного C (C99 или более поздней версии) показано размещение двумерного массива в куче и использование индексации многомерного массива для доступа (которая может использовать проверку границ во многих компиляторах C):

int func(int N, int M)
{
  float (*p)[N] [M] = malloc(sizeof *p);
  if (p == 0)
    return -1;
  for (int i = 0; i < N; i++)
    for (int j = 0; j < M; j++)
      (*p)[i] [j] = i + j;
  print_array(N, M, p);
  free(p);
  return 1;
}

А вот аналогичная реализация с использованием функции Auto VLA C99 : [с]

int func(int N, int M)
{
  // Caution: checks should be made to ensure N*M*sizeof(float) does NOT exceed limitations for auto VLAs and is within available size of stack.
  float p[N] [M]; // auto VLA is held on the stack, and sized when the function is invoked
  for (int i = 0; i < N; i++)
    for (int j = 0; j < M; j++)
      p[i] [j] = i + j;
  print_array(N, M, p);
  // no need to free(p) since it will disappear when the function exits, along with the rest of the stack frame
  return 1;
}

Взаимозаменяемость массива и указателя

[ редактировать ]

Индексное обозначение x[i] (где x обозначает указатель) является синтаксическим сахаром для *(x+i). [42] Воспользовавшись знанием компилятора типа указателя, адреса, который x + i указывает на не базовый адрес (на который указывает x) увеличивается на i байт, а скорее определяется как базовый адрес, увеличенный на i умножается на размер элемента, который x указывает на. Таким образом, x[i] обозначает i+1-й элемент массива.

Более того, в большинстве контекстов выражений (заметным исключением является операнд sizeof), выражение типа массива автоматически преобразуется в указатель на первый элемент массива. Это означает, что массив никогда не копируется целиком, если он назван в качестве аргумента функции, а передается только адрес его первого элемента. Таким образом, хотя вызовы функций в C используют семантику передачи по значению , массивы фактически передаются по ссылке .

Общий размер массива x можно определить, применив sizeof к выражению типа массива. Размер элемента можно определить, применив оператор sizeof к любому разыменованному элементу массива A, как в n = sizeof A[0]. Таким образом, количество элементов в объявленном массиве A может быть определен как sizeof A / sizeof A[0]. Обратите внимание: если доступен только указатель на первый элемент, как это часто бывает в коде C из-за описанного выше автоматического преобразования, информация о полном типе массива и его длине теряется.

Управление памятью

[ редактировать ]

Одной из наиболее важных функций языка программирования является предоставление средств управления памятью и объектами, хранящимися в памяти. C предоставляет три основных способа выделения памяти для объектов: [37]

  • Статическое выделение памяти : пространство для объекта предоставляется в двоичном виде во время компиляции; эти объекты имеют размер (или время жизни) до тех пор, пока содержащий их двоичный файл загружается в память.
  • Автоматическое выделение памяти : временные объекты могут храниться в стеке , и это пространство автоматически освобождается и может быть повторно использовано после выхода из блока, в котором они объявлены.
  • Динамическое распределение памяти : блоки памяти произвольного размера могут быть запрошены во время выполнения с использованием библиотечных функций, таких как malloc из области памяти, называемой кучей ; эти блоки сохраняются до тех пор, пока впоследствии не будут освобождены для повторного использования путем вызова библиотечной функции. realloc или free

Эти три подхода подходят в разных ситуациях и имеют различные компромиссы. Например, статическое выделение памяти имеет небольшие накладные расходы, автоматическое выделение может потребовать немного больше накладных расходов, а динамическое выделение памяти потенциально может иметь большие накладные расходы как на выделение, так и на освобождение. Постоянный характер статических объектов полезен для сохранения информации о состоянии во время вызовов функций, автоматическое выделение легко использовать, но пространство стека обычно гораздо более ограничено и преходяще, чем статическая память или пространство кучи, а динамическое распределение памяти позволяет удобно выделять объекты, размер известен только во время выполнения. Большинство программ на языке C широко используют все три.

Там, где это возможно, автоматическое или статическое выделение обычно является самым простым, поскольку хранилищем управляет компилятор, что освобождает программиста от потенциально подверженной ошибкам рутинной работы по выделению и освобождению памяти вручную. Однако размер многих структур данных может изменяться во время выполнения, а поскольку статические выделения (и автоматические выделения до C99) должны иметь фиксированный размер во время компиляции, существует множество ситуаций, в которых динамическое выделение необходимо. [37] До стандарта C99 частым примером были массивы переменного размера. (См. статью о malloc в качестве примера динамически выделяемых массивов.) В отличие от автоматического выделения, которое может привести к сбою во время выполнения с неконтролируемыми последствиями, функции динамического выделения возвращают индикацию (в виде значения нулевого указателя), когда необходимое хранилище не может быть выделено. (Слишком большое статическое выделение обычно обнаруживается компоновщиком или загрузчиком еще до того, как программа сможет начать выполнение.)

Если не указано иное, статические объекты содержат нулевые или нулевые значения указателя при запуске программы. Автоматически и динамически выделяемые объекты инициализируются только в том случае, если начальное значение указано явно; в противном случае они изначально имеют неопределенные значения (обычно это любой битовый шаблон , присутствующий в хранилище , который может даже не представлять допустимое значение для этого типа). Если программа попытается получить доступ к неинициализированному значению, результаты будут неопределенными. Многие современные компиляторы пытаются обнаружить и предупредить об этой проблеме, но могут возникать как ложноположительные, так и ложноотрицательные результаты .

Выделение кучи памяти должно быть синхронизировано с ее фактическим использованием в любой программе, чтобы ее можно было использовать повторно. Например, если единственный указатель на выделение памяти в куче выходит за пределы области действия или его значение перезаписывается до того, как оно будет явно освобождено, то эта память не может быть восстановлена ​​для последующего повторного использования и по существу теряется для программы — явление, известное как память. утечка . И наоборот, память может быть освобождена, но к ней будут обращаться впоследствии, что приведет к непредсказуемым результатам. Обычно симптомы сбоя появляются в части программы, не связанной с кодом, вызывающим ошибку, что затрудняет диагностику сбоя. Такие проблемы решаются в языках с автоматической сборкой мусора .

Библиотеки

[ редактировать ]

Язык программирования C использует библиотеки в качестве основного метода расширения. В языке C библиотека — это набор функций, содержащихся в одном «архивном» файле. Каждая библиотека обычно имеет заголовочный файл , который содержит прототипы функций, содержащихся в библиотеке, которые могут использоваться программой, а также объявления специальных типов данных и макросимволов, используемых с этими функциями. Чтобы программа могла использовать библиотеку, она должна включать заголовочный файл библиотеки, а библиотека должна быть связана с программой, что во многих случаях требует флагов компилятора (например, -lm, сокращение от «связать математическую библиотеку»). [37]

Наиболее распространенной библиотекой C является стандартная библиотека C , которая определяется стандартами ISO и ANSI C и поставляется с каждой реализацией C (реализации, предназначенные для ограниченных сред, таких как встроенные системы, могут предоставлять только подмножество стандартной библиотеки). Эта библиотека поддерживает потоковый ввод и вывод, распределение памяти, математические операции, строки символов и значения времени. Несколько отдельных стандартных заголовков (например, stdio.h) определяют интерфейсы для этих и других стандартных библиотечных средств.

Другой распространенный набор функций библиотеки C — это те, которые используются приложениями, специально предназначенными для Unix и Unix-подобных систем, особенно функции, которые обеспечивают интерфейс с ядром . Эти функции подробно описаны в различных стандартах, таких как POSIX и Единая спецификация UNIX .

Поскольку многие программы написаны на языке C, существует множество других доступных библиотек. Библиотеки часто пишутся на C, поскольку компиляторы C генерируют эффективный объектный код ; затем программисты создают интерфейсы для библиотеки, чтобы подпрограммы можно было использовать из языков более высокого уровня, таких как Java , Perl и Python . [37]

Обработка файлов и потоки

[ редактировать ]

Ввод и вывод файлов (I/O) не являются частью самого языка C, а обрабатываются библиотеками (такими как стандартная библиотека C) и связанными с ними заголовочными файлами (например, stdio.h). Обработка файлов обычно реализуется посредством ввода-вывода высокого уровня, который работает через потоки . С этой точки зрения поток — это поток данных, независимый от устройств, тогда как файл — это конкретное устройство. Ввод-вывод высокого уровня осуществляется посредством ассоциации потока с файлом. В стандартной библиотеке C буфер (область памяти или очередь) временно используется для хранения данных перед их отправкой в ​​конечный пункт назначения. Это сокращает время ожидания более медленных устройств, например жесткого или твердотельного накопителя . Функции ввода-вывода низкого уровня не являются частью стандартной библиотеки C. [ нужны разъяснения ] но, как правило, являются частью программирования на «голом железе» (программирование, независимое от какой-либо операционной системы, например, большинство встроенных программ ). За некоторыми исключениями, реализации включают низкоуровневый ввод-вывод.

Языковые инструменты

[ редактировать ]

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

Автоматизированная проверка и аудит исходного кода полезны на любом языке, и для C существует множество таких инструментов, например Lint . Обычной практикой является использование Lint для обнаружения сомнительного кода при первом написании программы. Как только программа проходит Lint, она компилируется с помощью компилятора C. Кроме того, многие компиляторы могут дополнительно предупреждать о синтаксически допустимых конструкциях, которые на самом деле могут оказаться ошибками. MISRA C — это собственный набор правил, позволяющий избегать использования такого сомнительного кода, разработанный для встроенных систем. [43]

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

Такие инструменты, как Purify или Valgrind , а также связывание с библиотеками, содержащими специальные версии функций распределения памяти, могут помочь обнаружить ошибки во время выполнения, связанные с использованием памяти. [44] [45]

Использование

[ редактировать ]

Обоснование использования в системном программировании

[ редактировать ]
Некоторое программное обеспечение, написанное на C

C широко используется для системного программирования при реализации операционных систем и встроенных системных приложений. [46] Это происходит по нескольким причинам:

  • Язык C обеспечивает доступ к аппаратному обеспечению и памяти платформы с помощью указателей и каламбура типов , поэтому специфичные для системы функции (например, регистры управления/состояния , регистры ввода-вывода ) могут быть настроены и использованы с кодом, написанным на C – это обеспечивает наиболее полный контроль над платформа, на которой он работает.
  • Код, сгенерированный после компиляции, не требует многих системных функций и может быть напрямую вызван из некоторого загрузочного кода — его просто выполнить.
  • Операторы и выражения языка C обычно хорошо сопоставляются с последовательностями инструкций для целевого процессора, и, следовательно, во время выполнения требуется мало системных ресурсов — они выполняются быстро.
  • Благодаря богатому набору операторов язык C может использовать многие функции целевых процессоров. Если конкретный ЦП имеет более экзотические инструкции, можно создать языковой вариант с, возможно, встроенными функциями для использования этих инструкций - он может использовать практически все функции целевого ЦП.
  • Язык позволяет легко накладывать структуры на блоки двоичных данных, позволяя понимать данные, перемещаться по ним и изменять их — он может записывать структуры данных, даже файловые системы.
  • Язык поддерживает богатый набор операторов, включая битовые манипуляции, для целочисленной арифметики и логики, а также, возможно, различные размеры чисел с плавающей запятой — он может эффективно обрабатывать соответствующим образом структурированные данные.
  • C — довольно небольшой язык, содержащий всего несколько операторов и не имеющий слишком большого количества функций, которые генерируют обширный целевой код — он понятен.
  • C имеет прямой контроль над выделением и освобождением памяти, что обеспечивает разумную эффективность и предсказуемое время операций обработки памяти, без каких-либо проблем со спорадическими событиями остановки мира - он имеет предсказуемую производительность.
  • C позволяет использовать и реализовывать различные схемы распределения памяти , включая типичную. malloc и free; более сложный механизм с аренами ; или версию для ядра ОС , которая может подходить для DMA , использоваться в обработчиках прерываний или интегрироваться с системой виртуальной памяти .
  • В зависимости от компоновщика и среды код C также может вызывать библиотеки, написанные на языке ассемблера , и может вызываться из языка ассемблера — он хорошо взаимодействует с другим кодом более низкого уровня.
  • C, его соглашения о вызовах и структуры компоновщика обычно используются в сочетании с другими языками высокого уровня, при этом поддерживаются вызовы как на C, так и из C — он хорошо взаимодействует с другим кодом высокого уровня.
  • C имеет очень зрелую и обширную экосистему, включающую библиотеки, фреймворки, компиляторы с открытым исходным кодом, отладчики и утилиты, и является фактическим стандартом. Вполне вероятно, что драйверы на C уже существуют или существует архитектура ЦП, аналогичная внутренней части компилятора C, поэтому стимулы для выбора другого языка уменьшаются.

Используется для библиотек с интенсивными вычислениями.

[ редактировать ]

C позволяет программистам создавать эффективные реализации алгоритмов и структур данных, поскольку уровень абстракции от аппаратного обеспечения тонкий, а его накладные расходы невелики, что является важным критерием для программ с интенсивными вычислениями. Например, библиотека арифметики множественной точности GNU , научная библиотека GNU , Mathematica и MATLAB полностью или частично написаны на C. Многие языки поддерживают вызов библиотечных функций на C, например, Python на основе фреймворк NumPy использует C для высокой точности. -Аспекты производительности и взаимодействия с оборудованием.

C как промежуточный язык

[ редактировать ]

C иногда используется в качестве промежуточного языка в реализациях других языков. Этот подход можно использовать для портативности или удобства; используя C в качестве промежуточного языка, дополнительные генераторы кода, специфичные для машины, не нужны. В C есть некоторые функции, такие как директивы препроцессора с номерами строк и необязательные лишние запятые в конце списков инициализаторов, которые поддерживают компиляцию сгенерированного кода. Однако некоторые недостатки C побудили разработку других языков на основе C, специально предназначенных для использования в качестве промежуточных языков, таких как C-- . Кроме того, современные основные компиляторы GCC и LLVM имеют промежуточное представление , отличное от C, и эти компиляторы поддерживают интерфейсы для многих языков, включая C.

Другие языки, написанные на C

[ редактировать ]

Следствием широкой доступности и эффективности C является то, что компиляторы , библиотеки и интерпретаторы других языков программирования часто реализуются на C. [47] Например, реализации Python эталонные , [48] Перл , [49] Руби , [50] и PHP [51] написаны на языке С.

Когда-то использовался для веб-разработки

[ редактировать ]

Исторически C иногда использовался для веб-разработки с использованием Common Gateway Interface (CGI) в качестве «шлюза» для передачи информации между веб-приложением, сервером и браузером. [52] Си, возможно, был предпочтительнее интерпретируемых языков из-за его скорости, стабильности и почти универсальной доступности. [53] Веб-разработка на языке C больше не является обычной практикой. [54] и существует множество других инструментов веб-разработки .

Веб-серверы

[ редактировать ]

Два самых популярных веб-сервера , Apache HTTP Server и Nginx , написаны на C. Эти веб-серверы взаимодействуют с операционной системой, прослушивают TCP-порты на предмет HTTP-запросов, а затем обслуживают статический веб-контент или вызывают выполнение других языки, обрабатывающие «рендеринг» контента, такие как PHP , который сам по себе в основном написан на C. «близкий к металлу» подход C позволяет создавать такие высокопроизводительные программные системы.

Приложения для конечных пользователей

[ редактировать ]

Язык C также широко используется для реализации приложений для конечных пользователей . [55] Однако такие приложения также можно писать на более новых языках более высокого уровня.

Ограничения

[ редактировать ]

мощь языка ассемблера и удобство... языка ассемблера

Деннис Ричи [56]

Хотя C был популярен, влиятельн и чрезвычайно успешен, у него есть недостатки, в том числе:

  • Стандартная обработка динамической памяти с помощью malloc и free подвержен ошибкам. К ошибкам относятся: Утечки памяти, когда память выделяется, но не освобождается; и доступ к ранее освобожденной памяти.
  • Использование указателей и прямое манипулирование памятью означает, что возможно повреждение памяти, возможно, из-за ошибки программиста или недостаточной проверки неверных данных.
  • Существует некоторая проверка типов , но она не применима к таким областям, как вариативные функции , и проверку типов можно тривиально или непреднамеренно обойти. Он слабо типизирован .
  • Поскольку код, сгенерированный компилятором, сам по себе содержит мало проверок, на программиста ложится обязанность учитывать все возможные результаты, защищать от переполнения буфера, проверки границ массива, переполнения стека , исчерпания памяти, а также учитывать условия гонки , изоляцию потоков и т. д. .
  • Использование указателей и манипулирование ими во время выполнения означает, что может быть два способа доступа к одним и тем же данным (псевдонимы), которые невозможно определить во время компиляции. Это означает, что некоторые оптимизации, доступные для других языков, невозможны в C. FORTRAN считается более быстрым.
  • Некоторые функции стандартной библиотеки, например scanf или strncat, может привести к переполнению буфера .
  • Существует ограниченная стандартизация поддержки низкоуровневых вариантов сгенерированного кода, например: различные соглашения о вызове функций и ABI ; различные по упаковке структур соглашения ; различный порядок байтов в больших целых числах (включая порядок байтов). Во многих реализациях языка некоторые из этих параметров могут обрабатываться с помощью директивы препроцессора. #pragma, [57] [58] а некоторые с дополнительными ключевыми словами, например, используйте __cdecl соглашение о вызовах. Но директива и параметры не поддерживаются последовательно. [59]
  • Обработка строк с использованием стандартной библиотеки требует большого количества кода и требует явного управления памятью.
  • Язык напрямую не поддерживает объектную ориентацию, интроспекцию , оценку выражений во время выполнения, дженерики и т. д.
  • Существует мало средств защиты от ненадлежащего использования возможностей языка, которое может привести к неподдерживаемому коду. В частности, препроцессор C может скрывать такие тревожные эффекты, как двойное вычисление и многое другое. [60] Эта возможность создания сложного кода была отмечена такими соревнованиями, как International Obfuscated C Code Contest и Underhanded C Contest .
  • В C отсутствует стандартная поддержка обработки исключений , и он предлагает только коды возврата для проверки ошибок. setjmp и longjmp использовались стандартные библиотечные функции [61] реализовать механизм try-catch с помощью макросов.

Для некоторых целей были приняты ограниченные стили C, например MISRA C или CERT C , в попытке уменьшить вероятность ошибок. Базы данных, такие как CWE, пытаются подсчитать количество уязвимостей C и т. д., а также дать рекомендации по их устранению.

Существуют инструменты , которые могут смягчить некоторые недостатки. Современные компиляторы C включают проверки, которые могут генерировать предупреждения, помогающие выявить множество потенциальных ошибок.

[ редактировать ]
График индекса TIOBE , показывающий сравнение популярности различных языков программирования. [62]

C прямо и косвенно повлиял на многие более поздние языки, такие как C++ и Java . [63] Наиболее распространенное влияние было синтаксическим; все упомянутые языки сочетают в себе синтаксис операторов и (более или менее узнаваемых) выражений C с системами типов, моделями данных или крупномасштабными программными структурами, которые отличаются от C, иногда радикально.

Существует несколько интерпретаторов C или близких к C, включая Ch и CINT , которые также можно использовать для написания сценариев.

Когда объектно-ориентированные языки программирования стали популярными, C++ и Objective-C были двумя разными расширениями C, предоставлявшими объектно-ориентированные возможности. Оба языка изначально были реализованы как компиляторы исходного кода ; исходный код был переведен на C, а затем скомпилирован компилятором C. [64]

Язык программирования C++ (первоначально называвшийся «C с классами ») был разработан Бьярном Страуструпом как подход к обеспечению объектно-ориентированной функциональности с синтаксисом, подобным C. [65] C++ добавляет большую силу типизации, область видимости и другие инструменты, полезные в объектно-ориентированном программировании, а также допускает универсальное программирование с помощью шаблонов. Теперь это почти расширенный набор C, C++. [ когда? ] поддерживает большую часть C, за некоторыми исключениями .

Objective-C изначально был очень «тонким» слоем поверх C и остается строгим надмножеством C, которое позволяет объектно-ориентированное программирование с использованием гибридной парадигмы динамической/статической типизации. Синтаксис Objective-C заимствован как из C, так и из Smalltalk : синтаксис, включающий предварительную обработку, выражения, объявления функций и вызовы функций, унаследован от C, тогда как синтаксис объектно-ориентированных функций изначально был взят из Smalltalk.

Помимо C++ и Objective-C , Ch , Cilk и Unified Parallel C являются почти надмножествами C.

См. также

[ редактировать ]

Примечания

[ редактировать ]
  1. ^ Исходный код примера будет компилироваться на большинстве современных компиляторов, которые не находятся в режиме строгого соответствия стандартам, но он не полностью соответствует требованиям ни C89, ни C99. Фактически, C99 требует создания диагностического сообщения.
  2. ^ main функция на самом деле имеет два аргумента, int argc и char *argv[]соответственно, которые можно использовать для обработки аргументов командной строки . Стандарт ISO C (раздел 5.1.2.2.1) требует обеих форм main поддерживаться, что представляет собой особый режим, не предоставляемый какой-либо другой функции.
  3. ^ Кодекс print_array (не показан) незначительно отличается, [ почему? ] слишком.
  1. ^ Перейти обратно: а б Принц, Питер; Кроуфорд, Тони (16 декабря 2005 г.). Коротко о С. О'Рейли Медиа, Инк. с. 3. ISBN  9780596550714 .
  2. ^ Ричи (1993) : «Томпсон предпринял короткую попытку создать систему, закодированную на ранней версии C - до структур - в 1972 году, но отказался от этой попытки».
  3. ^ «N3221 - Отчет редактора, после встречи в Страсбурге, Франция, январь 2024 г.» . ISO/IEC JTC1/SC22/WG14 . Открытые стандарты. 21 февраля 2024 г. . Проверено 24 мая 2024 г.
  4. ^ Ричи (1993) : «Схема композиции типов, принятая C, во многом обязана Алголу 68, хотя, возможно, она не появилась в форме, которую одобрили бы приверженцы Алгола».
  5. ^ Перейти обратно: а б «Verilog HDL (и C)» (PDF) . Исследовательская школа компьютерных наук Австралийского национального университета. 3 июня 2010 г. Архивировано из оригинала (PDF) 6 ноября 2013 г. . Проверено 19 августа 2013 г. 1980-е: впервые представлен Verilog; Verilog вдохновлен языком программирования C.
  6. ^ «Имя основано на букве C английского алфавита и произносится как буква C» . звук языка программирования c . Англо-китайский словарь. Архивировано из оригинала 17 ноября 2022 года . Проверено 17 ноября 2022 г.
  7. ^ Муньос, Дэниел. «Спустя все эти годы, мир по-прежнему опирается на программирование на языке C | Toptal®» . Блог Toptal Engineering . Проверено 15 июня 2024 г.
  8. ^ «Рейтинг популярности языка C упал до самого низкого уровня» . Разработчик.com . 9 августа 2016 года. Архивировано из оригинала 22 августа 2022 года . Проверено 1 августа 2022 г.
  9. ^ Перейти обратно: а б с д и ж Ричи (1993)
  10. ^ «Популярность языка программирования» . 2009. Архивировано из оригинала 16 января 2009 года . Проверено 16 января 2009 г.
  11. ^ «Индекс сообщества программистов TIOBE» . 2009. Архивировано из оригинала 4 мая 2009 года . Проверено 6 мая 2009 г.
  12. ^ Уорд, Терри А. (август 1983 г.). «Аннотированная C/A библиография языка C» . Байт . п. 268 . Проверено 31 января 2015 г.
  13. ^ «Индекс TIOBE за июнь 2024 года» . Архивировано из оригинала 21 июня 2024 года . Проверено 22 июня 2024 г.
  14. ^ Ричи, Деннис. «BCPL от B до C» . Архивировано из оригинала 12 декабря 2019 года . Проверено 10 сентября 2019 г.
  15. ^ Перейти обратно: а б с д и Дженсен, Ричард (9 декабря 2020 г.). « Чертовски глупый поступок» — происхождение C» . Арс Техника . Архивировано из оригинала 28 марта 2022 года . Проверено 28 марта 2022 г.
  16. ^ Перейти обратно: а б Джонсон, Южная Каролина ; Ричи, DM (1978). «Переносимость программ на языке C и системы UNIX». Белл Систем Тех. Дж . 57 (6): 2021–2048. CiteSeerX   10.1.1.138.35 . дои : 10.1002/j.1538-7305.1978.tb02141.x . ISSN   0005-8580 . S2CID   17510065 . (Примечание. PDF-файл представляет собой сканирование оригинала с помощью оптического распознавания символов и содержит изображение «IBM 370» как «IBM 310».)
  17. ^ Макилрой, доктор медицины (1987). Читатель Research Unix: аннотированные выдержки из Руководства программиста, 1971–1986 (PDF) (Технический отчет). CSTR. Лаборатории Белла. п. 10. 139. Архивировано (PDF) из оригинала 11 ноября 2017 г. . Проверено 1 февраля 2015 г.
  18. ^ Керниган, Брайан В .; Ричи, Деннис М. (февраль 1978 г.). Язык программирования C (1-е изд.). Энглвуд Клиффс, Нью-Джерси : Прентис Холл . ISBN  978-0-13-110163-0 .
  19. ^ «Страницы руководства по C». Руководство по разной информации FreeBSD (изд. FreeBSD 13.0). 30 мая 2011 г. Архивировано из оригинала 21 января 2021 г. Проверено 15 января 2021 г. [1] Архивировано 21 января 2021 г. в Wayback Machine.
  20. ^ Керниган, Брайан В .; Ричи, Деннис М. (март 1988 г.). Язык программирования C (2-е изд.). Энглвуд Клиффс, Нью-Джерси : Прентис Холл . ISBN  978-0-13-110362-7 .
  21. ^ Страуструп, Бьярн (2002). Соперничество между братьями и сестрами: C и C++ (PDF) (Отчет). Лаборатории AT&T. Архивировано (PDF) из оригинала 24 августа 2014 г. Проверено 14 апреля 2014 г.
  22. ^ «Обоснование американского национального стандарта для информационных систем — язык программирования — C» . Архивировано из оригинала 17 июля 2024 года . Проверено 17 июля 2024 г.
  23. ^ С Честность . Международная организация по стандартизации. 30 марта 1995 года. Архивировано из оригинала 25 июля 2018 года . Проверено 24 июля 2018 г.
  24. ^ «JTC1/SC22/WG14 – C» . Домашняя страница . ИСО/МЭК. Архивировано из оригинала 12 февраля 2018 года . Проверено 2 июня 2011 г.
  25. ^ Эндрю Бинсток (12 октября 2011 г.). «Интервью с Хербом Саттером» . Доктор Доббс . Архивировано из оригинала 2 августа 2013 года . Проверено 7 сентября 2013 г.
  26. ^ «WG14-N3132: Пересмотренное расписание C23» (PDF) . open-std.org . 4 июня 2023 г. Архивировано (PDF) из оригинала 9 июня 2023 г.
  27. ^ «WG14-N3220: Рабочий проект, C2y» (PDF) . open-std.org . 21 февраля 2024 г. Архивировано (PDF) из оригинала 26 февраля 2024 г.
  28. ^ «TR 18037: Встроенный C» (PDF) . ИСО/МЭК. Архивировано (PDF) из оригинала 25 февраля 2021 г. Проверено 26 июля 2011 г.
  29. ^ Харбисон, Сэмюэл П.; Стил, Гай Л. (2002). C: Справочное руководство (5-е изд.). Энглвуд Клиффс, Нью-Джерси : Прентис Холл . ISBN  978-0-13-089592-9 . Содержит грамматику BNF для C.
  30. ^ Керниган и Ричи (1988) , с. 192.
  31. ^ Керниган и Ричи (1978) , с. 3.
  32. ^ «Проект комитета ISO/IEC 9899:201x (ISO C11)» (PDF) . Архивировано (PDF) из оригинала 22 декабря 2017 г. Проверено 16 сентября 2011 г.
  33. ^ Керниган и Ричи (1988) , стр. 192, 259.
  34. ^ «10 распространенных ошибок программирования на C++» . Cs.ucr.edu. Архивировано из оригинала 21 октября 2008 года . Проверено 26 июня 2009 г.
  35. ^ Шульц, Томас (2004). C и 8051 (3-е изд.). Отсего, Мичиган: PageFree Publishing Inc., с. 20. ISBN  978-1-58961-237-2 . Проверено 10 февраля 2012 г.
  36. ^ Керниган и Ричи (1978) , с. 6.
  37. ^ Перейти обратно: а б с д и ж г Клеменс, Бен (2013). 21 век С. О'Рейли Медиа . ISBN  978-1-4493-2714-9 .
  38. ^ Фойер, Алан Р.; Гехани, Нараин Х. (март 1982 г.). «Сравнение языков программирования C и Pascal». Обзоры вычислительной техники ACM . 14 (1): 73–92. дои : 10.1145/356869.356872 . S2CID   3136859 .
  39. ^ Керниган и Ричи (1988) , с. 122.
  40. ^ Например, gcc предоставляет _FORTIFY_SOURCE. «Функции безопасности: проверки буфера времени компиляции (FORTIFY_SOURCE)» . Fedoraproject.org. Архивировано из оригинала 7 января 2007 года . Проверено 5 августа 2012 г.
  41. ^ Ямсиривонг, Опас (2016). Программирование с C. EDUCATION PUBLIC COMPANY LIMITED, стр. 225–230. Бангкок, Таиланд: SE -  978-616-08-2740-4 .
  42. ^ Раймонд, Эрик С. (11 октября 1996 г.). Словарь нового хакера (3-е изд.). МТИ Пресс. п. 432. ИСБН  978-0-262-68092-9 . Проверено 5 августа 2012 г.
  43. ^ «Страница руководства для lint (раздел 1 freebsd)» . unix.com . 24 мая 2001 года . Проверено 15 июля 2014 г.
  44. ^ «CS107 Valgrind Memcheck» . веб-сайт Stanford.edu . Проверено 23 июня 2023 г.
  45. ^ Гастингс, Рид; Джойс, Боб. «Очистка: быстрое обнаружение утечек памяти и ошибок доступа» (PDF) . Pure Software Inc .: 9.
  46. ^ Дейл, Нелл Б.; Уимс, Чип (2014). Программирование и решение проблем на C++ (6-е изд.). Берлингтон, Массачусетс: Jones & Bartlett Learning. ISBN  978-1449694289 . OCLC   894992484 .
  47. ^ «С — мать всех языков» . Академия ИКТ при ИИТК . 13 ноября 2018 года. Архивировано из оригинала 31 мая 2021 года . Проверено 11 октября 2022 г.
  48. ^ «1. Расширение Python с помощью C или C++» . Документация Python 3.10.7 . Архивировано из оригинала 5 ноября 2012 года . Проверено 11 октября 2022 г.
  49. ^ Конрад, Майкл (22 января 2018 г.). «Обзор движка Perl 5» . Opensource.com . Архивировано из оригинала 26 мая 2022 года . Проверено 11 октября 2022 г.
  50. ^ «На Ruby из C и C++» . Язык программирования Ruby . Архивировано из оригинала 12 августа 2013 года . Проверено 11 октября 2022 г.
  51. ^ Пара, Майкл (3 августа 2022 г.). «Что такое PHP? Как написать свою первую программу PHP» . freeCodeCamp . Архивировано из оригинала 4 августа 2022 года . Проверено 11 октября 2022 г.
  52. ^ Справочник доктора Добба . США: Miller Freeman, Inc., ноябрь – декабрь 1995 г.
  53. ^ «Использование C для программирования CGI» . linuxjournal.com. 1 марта 2005 года. Архивировано из оригинала 13 февраля 2010 года . Проверено 4 января 2010 г.
  54. ^ Перкинс, Люк (17 сентября 2013 г.). «Веб-разработка на C: безумие? Или безумие, как лиса?» . Середина . Архивировано из оригинала 4 октября 2014 года . Проверено 8 апреля 2022 г.
  55. ^ Муньос, Дэниел. «Спустя все эти годы, мир по-прежнему опирается на программирование на языке C» . Блог Toptal Engineering . Проверено 17 ноября 2023 г.
  56. ^ Мец, Кейд. «Деннис Ричи: Плечи, на которых стоял Стив Джобс» . Проводной . Архивировано из оригинала 12 апреля 2022 года . Проверено 19 апреля 2022 г.
  57. ^ corob-msft (31 марта 2022 г.). «Директивы Pragma и ключевые слова __pragma и _Pragma» . Microsoft Learn . Архивировано из оригинала 24 сентября 2022 года . Проверено 24 сентября 2022 г.
  58. ^ «Прагмы (препроцессор C)» . GCC, Коллекция компиляторов GNU . Архивировано из оригинала 17 июня 2002 года . Проверено 24 сентября 2022 г.
  59. ^ «Прагмы» . Руководство и справочник разработчика классического компилятора Intel C++ . Интел. Архивировано из оригинала 10 апреля 2022 года . Проверено 10 апреля 2022 г.
  60. ^ «Во славу препроцессора C» . апенварр . 13 августа 2007 года . Проверено 9 июля 2023 г.
  61. ^ Робертс, Эрик С. (21 марта 1989 г.). «Реализация исключений в C» (PDF) . Центр системных исследований DEC . СРЦ-РР-40. Архивировано (PDF) из оригинала 15 января 2017 г. Проверено 4 января 2022 г.
  62. ^ Макмиллан, Роберт (1 августа 2013 г.). «Ява теряет свое моджо?» . Проводной . Архивировано из оригинала 15 февраля 2017 года . Проверено 5 марта 2017 г.
  63. ^ О'Риган, Джерард (24 сентября 2015 г.). Основы вычислений: сборник избранных ключевых технологических компаний . Спрингер. ISBN  978-3319214641 . OCLC   922324121 .
  64. ^ Раухвергер, Лоуренс (2004). Языки и компиляторы для параллельных вычислений: 16-й международный семинар, LCPC 2003, Колледж-Стейшн, Техас, США, 2–4 октября 2003 г.: пересмотренные статьи . Спрингер. ISBN  978-3540246442 . OCLC   57965544 .
  65. ^ Страуструп, Бьярн (1993). «История C++: 1979–1991» (PDF) . Архивировано (PDF) из оригинала 2 февраля 2019 г. Проверено 9 июня 2011 г.

Источники

[ редактировать ]

Дальнейшее чтение

[ редактировать ]
[ редактировать ]
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: bfef7eb2078ead797a7ec9b2663684f5__1722549360
URL1:https://arc.ask3.ru/arc/aa/bf/f5/bfef7eb2078ead797a7ec9b2663684f5.html
Заголовок, (Title) документа по адресу, URL1:
C (programming language) - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)