stringtranslate.com

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

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

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

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

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

Обзор

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

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

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

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

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

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

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

История

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

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

Б

Томпсону нужен был язык программирования для разработки утилит для новой платформы. Сначала он пытался написать компилятор Фортрана , но вскоре отказался от этой идеи. Вместо этого он создал урезанную версию недавно разработанного языка системного программирования под названием BCPL . Официальное описание BCPL в то время не было доступно [14] , и Томпсон изменил синтаксис, сделав его менее многословным и похожим на упрощенный АЛГОЛ , известный как SMALGOL. [15] Томпсон назвал результат B. [8] Он описал 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. [8]

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

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

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

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

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 представила несколько языковых функций:

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

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

Например:

длинная some_function (); /* Это объявление функции, поэтому компилятор может знать имя и тип возвращаемого значения этой функции. */ /* int */ other_function (); /* Еще одно объявление функции. Поскольку это ранняя версия C, здесь неявно используется тип int. Комментарий показывает, где в более поздних версиях потребуется явный спецификатор типа int. */    /* int */ call_function () /* Это определение функции, включая тело кода, заключенное в { фигурные скобки }. Поскольку тип возвращаемого значения не указан, в этой ранней версии C функция неявно возвращает значение 'int'. */ { long test1 ; зарегистрируйте /* int */ test2 ; /* Еще раз обратите внимание, что 'int' здесь не требуется. Спецификатор типа 'int' */ /* в комментарии потребуется в более поздних версиях C. */ /* Ключевое слово 'register' указывает компилятору, что эта переменная должна */ /* в идеале храниться в регистре как в отличие от внутри кадра стека. */ test1 = некоторая_функция (); если ( тест1 > 1 ) тест2 = 0 ; еще test2 = другая_функция (); вернуть тест2 ; }                           

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

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

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

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

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. С.

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

С99

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

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

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

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

С11

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

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

С17

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

С23

C23 — неофициальное название следующей (после C17) основной версии стандарта языка C. Ожидается, что он будет опубликован в 2024 году. [25]

Встроенный C

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

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

Синтаксис

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

Исходные файлы 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, как и любой другой язык, имеет свои недостатки. Некоторые операторы имеют неправильный приоритет; некоторые части синтаксиса могли бы быть лучше». [29] Стандарт C не пытался исправить многие из этих недостатков из-за влияния таких изменений на уже существующее программное обеспечение.

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

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

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

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

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

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

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

C99 зарезервировал еще пять слов:

C11 зарезервировал еще семь слов: [30]

C23 зарезервирует еще 14 слов:

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

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

Операторы

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

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

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

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

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

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

Первоначальная версия была: [34]

main () { printf ( «привет, мир \n » ); } 

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

#include <stdio.h> int main ( void ) { printf ( «привет, мир \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. Это escape \n- последовательность , которую C преобразует в символ новой строки , который на выходе означает конец текущей строки. Возвращаемое значение функции printfимеет тип int, но оно автоматически отбрасывается, поскольку не используется. (Более осторожная программа могла бы проверить возвращаемое значение, чтобы определить, завершилась ли функция printfуспешно.) Точка с запятой ;завершает оператор.

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

Типы данных

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

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

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

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

Указатели

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

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

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

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

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

Массивы

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

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

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

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

int func ( int N , int M ) { float ( * p ) [ N ] [ M ] = malloc ( sizeof * p ); если ( p == 0 ) вернуть -1 ; for ( int i знак равно 0 ; я < N ; я ++ ) for ( int j знак равно 0 ; j < M ; j ++ ) ( * p )[ i ][ j ] = i + j ; print_array ( N , M , p ); бесплатно ( п ); вернуть 1 ; }                                            

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

int func ( int N , int M ) { // Внимание: необходимо выполнить проверку, чтобы убедиться, что N*M*sizeof(float) НЕ превышает ограничения для автоматических VLA и находится в пределах доступного размера стека. плавать p [ N ][ M ]; // auto VLA хранится в стеке и его размер изменяется при вызове функции for ( int i = 0 ; i < N ; i ++ ) for ( int j = 0 ; j < M ; j ++ ) p [ i ][ j ] знак равно я + j ; print_array ( N , M , p ); // нет необходимости в free(p), так как он исчезнет при выходе из функции вместе с остальной частью кадра стека return 1 ; }                                     

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

Обозначение нижнего индекса x[i](где xобозначает указатель) является синтаксическим сахаром для *(x+i). [40] Используя знание компилятором типа указателя, адрес, на который 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 предоставляет три основных способа распределения памяти для объектов: [35]

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

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

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

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

Библиотеки

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

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

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

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

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

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

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

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

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

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

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

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

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

Некоторое программное обеспечение, написанное на C

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

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

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

Некоторые другие языки сами написаны на C.

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

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

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

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

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

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

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

Ограничения

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

-  Деннис Ричи [54]

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

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

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

Некоторые из этих недостатков побудили создание других языков. [ нужна цитата ]

Родственные языки

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

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

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

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

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

Источники

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

Внешние ссылки