Язык программирования C был разработан Деннисом Ритчи (Dennis Ritchie) в 1972 году для операционной системы UNIX. Спустя годы язык стал доступен на многих других системах, удовлетворяя потребности программистов, желающих разрабатывать программы, которые могут работать на различных платформах.
Так как тогда язык C не был формально определён, то каждая реализация языка имела свои особенности и расширения. В результате цели переносимости не были достигнуты.
В 1982 году Американский Национальный Комитет Стандартов (ANSI) образовал Технический Комитет X3J11 по языку программирования C, чьей целью было формальное определение языка C, его библиотечных функций и взаимодействия с окружением времени выполнения. Стандарт языка C был введён в 1989 году.
Компиляторы Watcom C/16 и C/32 образовались в результате развития технологии генерации кода для процессоров 8086, которую разрабатывали компания Watcom и университет Ватерлоо начиная с 1980 года. Первый компилятор Watcom C/16 вышел в свет в 1988 году. Первый компилятор Watcom C/32 — в 1989 году.
C иногда называют языком «низкого уровня», имея в виду, что C–программисты часто думают в терминах битов, байтов, адресов и других, относящихся к программированию на языке ассемблера.
Но C также часто называют языком программирования «общего назначения». Кроме доступа к основным компонентам компьютера, он предоставляет возможности присущие языкам «высокого уровня». Структурированное программирование, структуры данных и модульное программирование (недавние дополнения некоторых языков высокого уровня) — это части языка C начиная с момента его создания.
C предоставляет программисту возможность писать приложения на уровне близком к уровню языка ассемблера без знания языка ассемблера конкретной машины. Раньше и другие компиляторы предоставляли такую возможность, но приложения, которые получались, были «тяжёлыми», так как код, который производили эти компиляторы, не был так же хорош, как написанный хорошим программистом на ассемблере. С современной техникой генерации кода становится трудным, если не совсем невозможным, отличить программу, написанную человеком на языке ассемблера, от программы сгенерированной C–компилятором (основываясь на размере кода). Фактически, сейчас некоторые компиляторы генерируют код на уровне хорошего программиста на ассемблере.
Так для чего использовать C? Теоретически на нём можно писать всё, что можно писать на ассемблере. Другие же языки использовать для специальных задач, где они превосходят остальные.
C более близок к «системному программированию» (программированию операционных систем), к языкам программирования и инструментам, которые не попадают в класс «прикладного программирования». Классический пример — операционная система UNIX, разработанная Bell Laboratories. Она написана почти полностью на языке C и является самой переносимой операционной системой.
C также используют для написания программ, требующих большей эффективности, чем обычные приложения. Типичным примером служат интерпретаторы и компиляторы языков программирования.
Другой областью, где часто используется C, является разработка крупномасштабных программ, таких как СУБД, электронные таблицы, текстовые редакторы и т.д. Это требует высокой эффективности и компактности, так как часто они являются наиболее используемыми отдельным человеком или компанией, и, следовательно, требуют больших ресурсов компьютера.
Может показаться, что C в основном используется для создания коммерческих продуктов, но его можно использовать для написания любого приложения требующего высокой эффективности. Например, большая система управления транзакциями может быть написана на COBOL'е, но чтобы выжать еще немного скорости, её можно переписать на C. Это приложение может быть написано и на ассемблере, но в настоящее время многие программисты предпочитают избегать писать на столь низком уровне, в то время как компилятор C может сгенерировать достаточно эффективный код.
И наконец, конечно, основная причина писать программу на C это то, что она сможет работать после больших или меньших переделок на любой системе, где есть компилятор C. Раньше, из-за быстрого роста количества C–компиляторов и отсутствия стандарта, определяющего их разработку, это было гораздо труднее. Ныне, при наличии стандарта ANSI на язык программирования C, программа, написанная в соответствии со стандартом, может быть перенесена на другой компилятор с минимальными переделками. Конечно, частности, вроде имён файлов, распределения памяти и синтаксиса командной строки отличаются на разных системах, но в правильно разработанном приложении на C эти части выделены в отдельные «системно-зависимые» файлы, которые могут быть заменены для конкретной системы. (Подробнее см. Написание переносимых программ.)
C имеет несколько основных преимуществ по сравнению с другими языками программирования.
Поставщики компьютерных систем хорошо представляют себе, что успех системы зависит от доступности для неё программного обеспечения. Из-за наличия большого количества программ на C большинство поставщиков предлагают компилятор C, поощряя тем самым перенос этих программ на их системы. Для систем у которых отсутствует компилятор C, его могут разработать независимые компании.
После разработки стандарта ANSI тенденции ко всеобщему распространению C–компиляторов возможно усилятся.
Многие языки программирования претендуют на переносимость. Языки FORTRAN, COBOL и Pascal имеют описывающий их стандарт, поэтому программа, написанная полностью в соответствии с определениями стандарта, скорее всего будет переносима. Это истинно и для C. Однако мало языков могут совместить переносимость с другими преимуществами C, включая эффективность генерируемого кода и возможность работать на уровне близком к машинному.
Мало языков могут сравниться с C по эффективности. Хороший программист на ассемблере может сделать код лучше, чем компилятор C, но он (или она) затратят значительно больше времени на разработку приложения, потому что при программировании на ассемблере гораздо проще сделать ошибку. Компиляторы для других языков могут производить эффективный код для приложений своего профиля, но мало какие делают это во всех случаях.
Большинству программ эта возможность не нужна, но, при необходимости, программа может использовать особые возможности компьютера. Например, программа может использовать некоторое значение, находящееся по фиксированному адресу в памяти. Этого просто достигнуть на C, и сложно на других языках. (Конечно, если программа разработана как переносимая, то эта часть кода будет изолирована и помечена как зависимая от операционной системы.)
Код на ассемблере труден для сопровождения, что вызвано очень низким уровнем программирования (регистры, адресация, ветвления). Программы на C обеспечивают сравнимую функциональность, но на более высоком уровне. Программист по-прежнему думает в терминах возможностей машины, но без необходимости точного знания работы аппаратуры, что позволяет ему сконцентрироваться на разработке программы, а не на деталях кодирования конкретного компьютера.
«Просты», конечно, — относительное понятие. C–программы определённо проще для понимания, чем эквивалентная программа на ассемблере. Другой язык может быть проще для понимания в каком-либо конкретном случае, но обычно C — хороший выбор.
Это наибольшее преимущество. Так как программы на C переносимы и C предназначен не только для какого-то определённого класса приложений, то часто он — лучший выбор для разработки приложения.
Эта книга — описание языка программирования C, реализованного компиляторами Watcom C/16 и Watcom C/32 для процессоров 80x86. Здесь сделана попытка дать простое для чтения описание языка C. Стандарт ANSI C — последняя инстанция в деталях языка, но он описывает язык в пределах, которые должны быть охвачены каждой реализацией компилятора.
В этой книге делается попытка описать язык C в общих границах, а также специфические возможности, про которые в стандарте говорится «реализационно-зависимые».
Текст выделенный таким шрифтом описывает особенности, реализованные в компиляторах Watcom C/16 и Watcom C/32.
Программисты, пишущие программы, которые будут переноситься на другие системы, должны обратить особое внимание при использовании этих возможностей, т.к. другие компиляторы могут работать по-другому. Насколько возможно, сделана попытка описать другое подобное поведение.
Эта книга не описывает ни одной из библиотечных функций, которые программа на C может использовать для взаимодействия со своим окружением. В частности, в этой книге не описаны ввод и вывод. Язык C не содержит в себе возможностей ввода/вывода. Руководство «Watcom C Library Reference» описывает все библиотечные функции, включая используемые для ввода и вывода.
Глоссарий включён в приложение и содержит все термины, используемые в книге.