Главная страница
qrcode

Б. Керниган, Д. РитчиЯзык программирования Си


НазваниеБ. Керниган, Д. РитчиЯзык программирования Си
АнкорB Kernigan D Ritchi Yazyk programmirovania C.pdf
Дата30.01.2018
Размер1,65 Mb.
Формат файлаpdf
Имя файлаB_Kernigan_D_Ritchi_Yazyk_programmirovania_C.pdf
оригинальный pdf просмотр
ТипКнига
#62273
страница14 из 24
Каталог
1   ...   10   11   12   13   14   15   16   17   ...   24
sbrk(n) в UNIXе возвращает указатель на n байт памяти или -1, если требуемого пространства не оказалось, хотя было бы лучше, если бы в последнем случае он возвращал NULL. Константу -1 необходимо привести к типу char *, чтобы ее можно было сравнить с возвращаемым значением. Это еще один пример того, как операция приведения типа делает функцию относительно независимой от конкретного представления указателей на различных машинах. Есть, однако, одна "некорректность", состоящая а том, что сравниваются указатели на различные блоки, выдаваемые функцией sbrk. Такое сравнение не гарантировано стандартом, который позволяет сравнивать указатели лишь в пределах одного и того же массива.
Таким образом, эта версия malloc верна только на тех машинах, в которых допускается сравнение любых указателей.
В заключение рассмотрим функцию free. Она просматривает список свободной памяти, начиная с freep, чтобы подыскать место для вставляемого блока. Искомое место может оказаться или между блоками, или в начале списка, или в его конце. В любом случае, если подлежащий освобождению блок примыкает к соседнему блоку, он объединяется с ним в один блок. О чем еще осталось позаботиться, - так это о том, чтобы указатели указывали в нужные места и размеры блоков были правильными.
/* free: включает блок в список свободной памяти */
void free(void *ар)
{
Header *bp, *p;
190
bp = (Header *)ap –1; /* указатель на заголовок блока */
for (p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr)
if (p >- p->s.ptr && (bp > p || bp < p->s.ptr))
break; /* освобождаем блок в начале или в конце */
if (bp + bp->s.size == p->s.ptr) { /* слить с верхним */
bp->s.size += p->s.ptr->s.size; /* соседом */
bp->s.ptr = p->s.ptr->s.ptr;
} else bp->s.ptr = p->s.ptr;
if (p + p->s.size == bp) { /* слить с нижним соседом */
p->s.size += bp->s.size;
p->s.ptr = bp->s.ptr;
} else p->s.ptr = bp;
freep = p;
}
Хотя выделение памяти по своей сути - машинно-зависимая проблема, с ней можно справиться, что и иллюстрирует приведенная программа, в которой машинная зависимость упрятана в очень маленькой ее части. Что касается проблемы выравнивания, то мы разрешили ее с помощью
typedef и union (предполагается, что sbrk дает подходящий в смысле выравнивания указатель).
Операции приведения типов позволяют нам сделать явными преобразования типов и даже справиться с плохо спроектированным интерфейсом системы. Несмотря на то, что наши рассуждения касались распределения памяти, этот общий подход применим и в других ситуациях.
Упражнение 8.6. Стандартная функция calloc(n, size) возвращает указатель на n элементов памяти размера size, заполненных нулями. Напишите свой вариант calloc, пользуясь функцией
malloc или модифицируя последнюю.
Упражнение 8.7. Функция malloc допускает любой размер, никак не проверяя его на правдоподобие: free предполагает, что размер освобождаемого блока - правильный.
Усовершенствуйте эти программы таким образом, чтобы они более тщательно контролировали ошибки.
Упражнение 8.8. Напишите программу bfree(p, n), освобождающую произвольный блок p, состоящий из n символов, путем включения его в список свободной памяти, поддерживаемый функциями malloc и free. C помощью bfree пользователь должен иметь возможность в любое время добавить в список свободной памяти статический или внешний массив.
191

Приложение A. Справочное руководство
A1. Введение
A2. Соглашения о лексике
A2.1. Лексемы (
tokens
)
A2.2. Комментарий
A2.3. Идентификаторы
A2.4. Ключевые слова
A2.5. Константы
A2.5.1. Целые константы
A2.5.2. Символьные константы
А2.5.3. Константы с плавающей точкой
A2.5.4. Константы-перечисления
A2.6. Строковые литералы
A3. Нотация синтаксиса
A4. Что обозначают идентификаторы
A4.1. Класс памяти
A4.2. Базовые типы
A4.3. Производные типы
A4.4. Квалификаторы типов
A5. Объекты и Lvalues
A6. Преобразования
A6.1. Целочисленное повышение
A6.2. Целочисленные преобразования
A6.3. Целые и числа с плавающей точкой
A6.4. Типы с плавающей точкой
А6.5. Арифметические преобразования
A6.6. Указатели и целые
A6.7. Тип
void
192

А6.8. Указатели на
void
A7. Выражения
A7.1. Генерация указателя
A7.2. Первичные выражения
A7.3. Постфиксные выражения
A7.3.1. Обращение к элементам массива
A7.3.2. Вызов функции
A7.3.3. Обращение к структурам
A7.3.4. Постфиксные операторы инкремента и декремента
А7.4. Унарные операторы
А7.4.1. Префиксные операторы инкремента и декремента
A7.4.2. Оператор получения адреса
A7.4.3. Оператор косвенного доступа
A7.4.4. Оператор унарный плюс
A7.4.5. Оператор унарный минус
A7.4.6. Оператор побитового отрицания
A7.4.7. Оператор логического отрицания
A7.4.8. Оператор определения размера sizeof
A7.5. Оператор приведения типа
A7.6. Мультипликативные операторы
A7.7. Аддитивные операторы
A7.8. Операторы сдвига
A7.9. Операторы отношения
A7.10. Операторы равенства
A7.11. Оператор побитового
И
A7.12. Оператор побитового исключающего
ИЛИ
A7.13. Оператор побитового
ИЛИ
A7.14. Оператор логического
И
A7.15. Оператор логического
ИЛИ
А7.16. Условный оператор
A7.17. Выражения присваивания
A7.18. Оператор запятая
A7.19. Константные выражения
A8. Объявления
A8.1. Спецификаторы класса памяти
А8.2. Спецификаторы типа
A8.3. Объявления структур и объединений
A8.4. Перечисления
А8.5. Объявители
A8.6. Что означают объявители
A8.6.1. Объявители указателей
А8.6.2. Объявители массивов
А8.6.3. Объявители функций
A8.7. Инициализация
A8.8. Имена типов
А8.9. Объявление
typedef
A8.10. Эквивалентность типов
A9. Инструкции
A9.1. Помеченные инструкции
193

A9.2. Инструкция-выражение
A9.3. Составная инструкция
A9.4. Инструкции выбора
A9.5. Циклические инструкции
A9.6. Инструкции перехода
А10. Внешние объявления
A10.1. Определение функции
A10.2. Внешние объявления
A11. Область видимости и связи
A11.1. Лексическая область видимости
A11.2. Связи
A12. Препроцессирование
A12.1. Трехзнаковые последовательности
A12.2. Склеивание строк
А12.3. Макроопределение и макрорасширение
A12.4. Включение файла
A12.5. Условная компиляция
A12.6. Нумерация строк
A12.7. Генерация сообщения об ошибке
A12.8. Прагма
A12.9. Пустая директива
A12.10. Заранее определенные имена
A13. Грамматика
A1. Введение
Данное руководство описывает язык программирования Си, определенный 31 октября 1989 г. в соответствии с проектом, утвержденным в ANSI в качестве Американского национального стандарта для информационных систем: Язык программирования Си, X3.159-1989 ("American
National Standard for Information Systems - Programming Language C, X3.159-1989").
Это описание - лишь один из вариантов предлагаемого стандарта, а не сам стандарт, однако мы специально заботились о том, чтобы сделать его надежным руководством по языку.
Настоящий документ в основном следует общей схеме описания, принятой в стандарте
(публикация которого в свою очередь основывалась на первом издании этой книги), однако в организационном плане есть различия. Если не считать отклонений в названиях нескольких продуктов и отсутствия формальных определений лексем и препроцессора, грамматика языка здесь и грамматика в стандарте эквивалентны.
Далее примечания (как и это) набираются с отступом от левого края страницы. В основном эти примечания касаются отличий стандарта от версии языка, описанной в первом издании этой книги, и от последующих нововведений в различных компиляторах.
194

A2. Соглашения о лексике
Программа состоит из одной или нескольких единиц трансляции, хранящихся в виде файлов.
Каждая такая единица проходит несколько фаз трансляции, описанных в
A12
. Начальные фазы осуществляют лексические преобразования нижнего уровня, выполняют директивы, заданные в программе строками, начинающимися со знака #, обрабатывают макроопределения и производят макрорасширения. По завершении работы препроцессора (
A12
) программа представляется к виде последовательности лексем.
A2.1. Лексемы (tokens)
Существуют шесть классов лексем (или токенов): идентификаторы, ключевые слона, константы, строковые литералы, операторы и прочие разделители. Пробелы, горизонтальные и вертикальные табуляции, новые строки, переводы страницы и комментарии (имеющие общее название символы-разделители) рассматриваются компилятором только как разделители лексем и в остальном на результат трансляции влияние не оказывают. Любой из символов-разделителей годится, чтобы отделить друг от друга соседние идентификаторы, ключевые слова и константы.
Если входной поток уже до некоторого символа разбит на лексемы, то следующей лексемой будет самая длинная строка, которая может быть лексемой.
A2.2. Комментарий
Символы /* открывают комментарий, а символы */ закрывают его. Комментарии нельзя вкладывать друг в друга, их нельзя помещать внутрь строк или текстовых литералов.
A2.3. Идентификаторы
Идентификатор — это последовательность букв и цифр. Первым символом должна быть буква; знак подчеркивания _ считается буквой. Буквы нижнего и верхнего регистров различаются. Идентификаторы могут иметь любую длину; для внутренних идентификаторов значимыми являются первые 31 символ; в некоторых реализациях принято большее число значимых символов. К внутренним идентификаторам относятся имена макросов и все другие имена, не имеющие внешних связей (
A11.2
). На идентификаторы с внешними связями могут накладываться большие ограничения: иногда воспринимаются не более шести первых символов и могут не различаться буквы верхнего и нижнего регистров.
A2.4. Ключевые слова
Следующие идентификаторы зарезервированы в качестве ключевых слов и в другом смысле использоваться не могут:
auto
break
char
case
char
const
continue
default
do
double
195

else
enum
extern
float
for
goto
if
int
long
register
return
short
signed
sizeof
static
struct
switch
typedef
union
unsigned
void
volatile
while
В некоторых реализациях резервируются также слова fortran и asm.
Ключевые слова const, signed и volatile впервые появились в стандарте ANSI;
enum и void - новые по отношению к первому изданию книги, но уже использовались; ранее зарезервированное entry нигде не использовалось и поэтому более не резервируется.
A2.5. Константы
Существует несколько видов констант. Каждая имеет свой тип данных; базовые типы рассматриваются в
A4.2
константа:
целая–константа
символьная-константа
константа-с-плавающей-точкой
константа-перечисление
A2.5.1. Целые константы
Целая константа, состоящая из последовательности цифр, воспринимается как восьмеричная, если она начинается с 0 (цифры нуль), и как десятичная в противном случае. Восьмеричная константа не содержит цифр 8 и 9. Последовательность цифр, перед которой стоят 0x или 0X, рассматривается как шестнадцатеричное целое. В шестнадцатеричные цифры включены буквы от a (или A) до f (или F) co значениями от 10 до 15.
Целая константа может быть записана с буквой-суффиксом u (или U) для спецификации ее как беззнаковой константы. Она также может быть с буквой- суффиксом l (или L) для указания, что она имеет тип long.
196

Тип целой константы зависит от ее вида, значения и суффикса (о типах см.
A4
). Если константа - десятичная и не имеет суффикса, то она принимает первый из следующих типов, который годится для представления ее значения: int, long int, unsigned long int. Восьмеричная или шестнадцатеричная константа без суффикса принимает первый возможный из типов: int,
unsigned int, long int, unsigned long int. Если константа имеет суффикс u или U, то она принимает первый возможный из типов: unsigned int, unsigned long int. Если константа имеет суффикс l или L, то она принимает первый возможный из типов: long int, unsigned long int.
Если константа имеет суффикс ul или UL, то она принимает тип unsigned long int.
Типы целых констант получили существенное развитие в сравнении с первой редакцией языка, в которой большие целые имели просто тип long. Суффиксы U и u введены впервые.
A2.5.2. Символьные константы
Символьная константа - это последовательность из одной или нескольких символов, заключенная в одиночные кавычки (например 'x'). Если внутри одиночных кавычек расположен один символ, значением константы является числовое значение этого символа в кодировке, принятой на данной машине. Значение константы с несколькими символами зависит от реализации.
Символьная константа не может содержать в себе одиночную кавычку ' или символ новой строки; чтобы изобразить их и некоторые другие символы, могут быть использованы эскейп-
последовательности:
новая строка (newline, linefeed)
NL (LF)
\n
горизонтальная табуляция (horizontal tab)
HT
\t
вертикальная табуляция (vertical tab)
VT
\v
возврат на шаг (backspace)
BS
\b
возврат каретки (carriage return)
CR
\r
перевод страницы (formfeed)
FF
\f
сигнал звонок (audible alert, bell)
BEL
\a
обратная наклонная черта (backslash)
\
\\
знак вопроса (question mark)
?
\?
одиночная кавычка (single quote)
'
\'
двойная кавычка (double quote)
"
\"
восьмеричный код (octal number)
ooo
\ooo
шестнадцатеричный код (hex number)
hh
\xhh
197

Эскейп-последовательность \ooo состоит из обратной наклонной черты, за которой следуют одна, две или три восьмеричные цифры, специфицирующие значение желаемого символа.
Наиболее частым примером такой конструкции является \0 (за которой не следует цифра); она специфицирует NULL-символ. Эскейп-последовательность \xhh состоит из обратной наклонной черты с буквой x, за которыми следуют шестнадцатеричные цифры, специфицирующие значение желаемого символа. На количество цифр нет ограничений, но результат будет не определен, если значение полученного символа превысит значение самого "большого" из допустимых символов. Если в данной реализации тип char трактуется как число со знаком, то значение и в восьмеричной, и в шестнадцатеричной эскейп-последовательности получается с помощью "распространения знака", как если бы выполнялась операция приведения к типу char.
Если за \ не следует ни один из перечисленных выше символов, результат не определен.
В некоторых реализациях имеется расширенный набор символов, который не может быть охвачен типом char. Константа для такого набора пишется с буквой L впереди (например L'x') и называется расширенной символьной константой. Такая константа имеет тип wchar_t
(целочисленный тип, определенный в стандартном заголовочном файле ). Как и в случае обычных символьных констант, здесь также возможны восьмеричные и шестнадцатеричные эскейп- последовательности; если специфицированное значение превысит тип wchar_t, результат будет не определен.
Некоторые из приведенных эскейп-последовательностей новые
(шестнадцатеричные в частности). Новым является и расширенный тип для символов. Наборам символов, обычно используемым в Америке и Западной
Европе, подходит тип char, а тип wchar_t был добавлен главным образом для азиатских языков.
А2.5.3. Константы с плавающей точкой
Константа с плавающей точкой состоит из целой части, десятичной точки, дробной части, e или
E и целого (возможно, со знаком), представляющего порядок, и, возможно, суффикса типа, задаваемого одной из букв: f, F, l или L. И целая, и дробная часть представляют собой последовательность цифр. Либо целая часть, либо дробная часть (но не обе вместе) могут отсутствовать; также могут отсутствовать десятичная точка или E с порядком (но не обе одновременно). Тип определяется суффиксом: F или f определяют тип float, L или l - тип long
double; при отсутствии суффикса подразумевается тип double.
Суффиксы для констант с плавающей точкой являются нововведением.
A2.5.4. Константы-перечисления
Идентификаторы, объявленные как элементы перечисления (
A8.4
), являются константами типа
int.
A2.6. Строковые литералы
Строковый литерал, который также называют строковой константой, - это последовательность символов, заключенная в двойные кавычки (Например, "..."). Строка имеет тип "массив символов" и память класса static (
A4
), которая инициализируется заданными символами.
Представляются ли одинаковые строковые литералы одной копией или несколькими, зависит от реализации. Поведение программы, пытающейся изменить строковый литерал, не определено.
198

Написанные рядам строковые литералы объединяются (конкатенируются) в одну строку. После любой конкатенации к строке добавляется NULL-байт (\0), что позволяет программе, просматривающей строку, найти ее конец. Строковые литералы не могут содержать в себе символ новой строки или двойную кавычку; в них нужно использовать те же эскейп- последовательности, что и в символьных константах.
Как и в случае с символьными константами, строковый литерал с символами из расширенного набора должен начинаться с буквы
1   ...   10   11   12   13   14   15   16   17   ...   24

перейти в каталог файлов


связь с админом