60 Глава 1. Проблемы разработки языка
При такой структуре хранение данных осуществляется очень просто. Каждая
функция имеет локальную память или локальную активационную запись, кото
рая является динамической и допускает рекурсию; также каждая функция имеет
доступ к глобальным переменным. Реально не существует никакой блочной струк
туры. Каждый отдельный объект данных реализован эффективно. Например, мно
гомерные массивы строятся из одномерных массивов, а индексы одномерных мас
сивов начинаются с 0. Это исключает необходимость применения дескрипторов
для вычисления смещения, что, в свою очередь, исключает все сложные вычисле
ния для определения размещения в памяти элементов массива.
В языке C используются указатели, а массивы и указатели эквивалентны, что
позволяет программам использовать любой из наиболее подходящих методов для
доступа к элементам массивов. Строки реализованы как массивы символов. Эта
реализация совершенно прозрачна, поэтому обращаться к строкам можно не
сколькими способами: как к строкам, как к массивам или (как уже отмечалось)
как к указателям на отдельный символ.
В языке C имеется большой набор арифметических операций, которые позво
ляют писать очень эффективные, а иногда и очень лаконичные программы. Также
имеется полный набор структур управления с очень гибкой семантикой, иногда
допускающей довольно необычное использование.
Кроме того, в C предусмотрена возможность гибкого определения типов. Тем
не менее можно утверждать, что C является одновременно и сильно типизирован
ным, и не сильно типизированным языком. Такая неоднозначность возникает из
за того, что большинство отдельных элементов данных являются подтипами цело
го типа. Хотя транслятор и обнаружит ошибки, связанные с несоответствием типов,
но поскольку большинство элементов данных в конечном счете относятся к цело
численному типу, многие ошибки могут остаться незамеченными.
Язык C изначально был тесно связан с функциональными возможностями опе
рационной системы. В операционной системе UNIX некоторые функции операци
онной системы (например, функция malloc для динамического выделения области
памяти) определены как вызовы соответствующих функций языка C. По соглаше
нию все они определяются в системных заголовочных файлах с расширением .h.
Так, например, для вызова malloc из программы на языке C в начале программы
должен быть подключен соответствующий файл
#include <malloc.h>
а при дальнейшем использовании этой функции в программе следует просто пи
сать malloc(StorageSize), указывая в качестве параметра требуемый объем памяти.
При компиляции и запуске такой программы определения соответствующих функ
ций (например, malloc) подключаются из библиотеки C.
Компилятор C сначала запускает препроцессор. Такие команды, как #define
и #include выполняются в первую очередь, а затем уже транслятором C компили
руется вся остальная программа.
При разработке операторов вводавывода в качестве образца была взята кон
цепция оператора FORMAT языка FORTRAN, но получившиеся в результате опера
торы лучше приспособлены для интерактивных программ, чем операторы READ и
WRITE языка FORTRAN. Большинство полезных функций определено в системном
файле stdio.h, который должен подключаться к любой программе на языке C. Та