char. Поэтому мы присвоили значение этого указателя элементу массива starts[index] при помощи оператора starts[index] = malloc(BLOCK);
Хорошо, давайте теперь рассмотрим проект программы, заключающийся в том, чтобы запомнить все исходные строки подряд в большом массиве store. Мы хотим использовать
starts[0] для ссылки на начало первой строки, starts[l] - второй строки и т. д. На промежуточном этапе программа вводит строку в массив symph. Мы использовали fgets( ) вместо gets( ), чтобы ограничить входную строку длиной массива symph.
313
РИС. 15.6. Последовательные строки symph, записанные в массив store.
Прежде чем копировать
symph в
store, мы должны проверить, достаточно ли для нее оставшегося места. Указатель
end ссылается на конец памяти, а текущее значение
starts[index]ссылается на начало неиспользованной памяти. Таким образом, мы можем сравнить разницу между этими двумя указателями с длиной
symph и определить, достаточно ли осталось памяти.
Если места недостаточно, вызываем
malloc( ), чтобы подготовить дополнительную память.
Мы устанавливаем
starts[index] на начало нового блока памяти, a
end - на конец нового блока.
Заметим, что у нас нет имени этой новой памяти. Она не является, например, расширением
store. У нас есть только обозначения указателей, ссылающихся на новую область памяти.
Когда программа работает, на каждую новую строку ссылается элемент массива указателей
starts. Некоторые строки находятся в
store, другие - в одной или нескольких новых областях памяти.
Но
пока у нас есть указатели, мы можем работать со строками, как показывает нам часть программы, выполняющая вывод на печать.
Таким образом используется
mа11ос( ). Но предположим, что вы хотите работать с памятью типа
int, а не
char. Можете и здесь использовать
mа11ос( ). Вот как это делается:
char *malloc( ); /* по-прежнему описываем как указатель на char */
int *newmem;
newmem = (int *) malloc(l00); /* используем операцию приведения типа */
Снова требуется 100 байт. Операция приведения типа преобразует значение, возвращенное указателем на тип
char, в указатель на тип
int. Если, как в нашей системе,
int занимает два байта памяти, это значит, что
newmem + 1 будет увеличивать указатель на два байта, т. е.
передвигать его к следующему целому. Это также означает, что 100 байт можно использовать для запоминания 50 целых чисел.
Другую возможность распределения памяти дает нам применение функции
са11ос( ):
char *calloc( );
long *newmem;
newmem = (long *) calloc(100, sizeof(long));
Подобно
malloc( ) функция
са11ос( ) возвращает указатель на
char. Нужно использовать оператор приведения типа, если вы хотите запомнить другой тип. Эта новая функция имеет два аргумента, и оба они должны быть целыми без знака. Первый аргумент содержит количество требуемых ячеек памяти. Второй аргумент - размер каждой ячейки в байтах. В нашем случае
long использует четыре байта, поэтому оператор выделит 100 четырехбайтных элементов,
используя в целом 400 байтов памяти.
Применяя
sizeof (long) вместо 4, мы сделали эту программу более мобильной. Она будет работать на системах, где
long имеет размер, отличный от четырех.
Функция
са11ос( ) имеет еще одну особенность; она обнуляет содержимое всего блока.
Ваша библиотека языка Си, вероятно, предоставляет несколько других функций управления памятью, и вы можете захотеть проверить их.
314
ДРУГИЕ БИБЛИОТЕЧНЫЕ ФУНКЦИИДалее
Содержание
Большинство библиотек будут выполнять и ряд дополнительных функций в тех случаях,
которые мы рассмотрели. Кроме функций, распределяющих память, есть функции,
освобождающие память после работы с нею. Могут быть другие функции, работающие со строками, например такие, которые ищут в строке определенный символ или сочетание символов.
Некоторые функции, работающие с файлами, включают
ореn( ),
close( ),
create( ),
fseek( ),
read( ) и
write( ). Они выполняют почти те же самые задачи, что и функции, которые мы обсудили, но на более фундаментальном уровне. Действительно, функции, подобные
fopen( ),
обычно пишутся с применением этих более общих функций. Они немного более трудны в использовании, но могут работать с двоичными файлами так же, как и с текстовыми.
Ваша система может иметь библиотеку математических функций. Обычно такая библиотека будет содержать функции квадратного корня, степенные, экспоненциальные, различные тригонометрические функции и функцию получения случайных чисел.
Вам нужно время, чтобы освоить то, что предлагает наша система. Если у нее нет того, что вам нужно, создайте свои собственные функции. Это часть языка Си. Если вы полагаете, что можете улучшить работу, скажем, функции ввода, сделайте это! А когда вы усовершенствуете и отшлифуете свои методы программирования, вы перейдете от обычного языка Си к блестящему языку Си.
ЗАКЛЮЧЕНИЕДалее
Содержание
Мы прошли долгий путь от начала этого руководства. Теперь вы уже познакомились с большинством основных свойств языка Си. (Главное из того что, мы опустили,- операции с разрядами и расширения UNIX 7 - рассматриваются кратко в приложении Б). Вы узнали и использовали все изобилие его операторов, огромное разнообразие основных и производных типов данных, его "умные" управляющие конструкции и мощную систему указателей. Мы надеемся, что подготовили вас к использованию языка Си в ваших собственных целях.
Поэтому начинайте программировать, и удачи вам!
ЧТО ВЫ ДОЛЖНЫ БЫЛИ УЗНАТЬ В ЭТОЙ ГЛАВЕДалее
Содержание
Что такое библиотека языка Си и как ее использовать.
Как открывать и закрывать текстовые файлы:
fopen( ) и
fclose( )Что такое тип
FILEКак читать из файла и записывать в файл:
getc( ),
putc( ),
fgets( ),
fputs( ),
fscanf( ),
fprintf( )Как проверять классы символов:
isdigit( ),
isalpha( ) и т. д.
Как превращать строки в числа:
atoi( ) и
atof( )Как осуществлять быстрый выход:
exit( )Как распределять память:
malloc( ),
са11ос( )ВОПРОСЫ И ОТВЕТЫДалее
Содержание
Вопросы315
1. Что неправильно в этой программе?
main( )
{ int *fp;
int k;
fp = fopen("желе");
for(k = 0; k < 30; k++)
fputs(fp, "Нанетта ест желе.");
fclose("желе");
}
2. Что будет делать следующая программа?
#include
#include
main(argc, argv)
int argc;
char *argv[ ];
{ int ch;
FILE *fp;
if((fp=fopen(argv[1], "r")) == NULL)
exit(1);
while((ch=getc(fp)) != EOF)
if(isdigit(ch))
putchar(ch);
fclose (fp);
}
3. Вce ли правильно в выражении isalpha(c[i]), где с является массивом типа char. Что можно сказать о isalpha(c[i ++])?
4.Используйте функции классификации символе" для подготовки выполненния atoi( ).
5. Как вы могли бы распределить, память для размещения массива структур?
Ответы
1. Должна быть директива #include для определения ее файлов. Следует описать указатель fp файла: FILE *fp; функция fopen( ) должна иметь вид fopen("желе", "w"), или,
может быть, включать "а" . Порядок аргументов в fputs( ) должен быть обратным. Функция
fclose( ) требует указателя файла, а не имени файла: fclose(fp).
2. Она будет открывать файл, заданный как аргумент командной строки, и выво дить на печать все цифры в файле. Программа должна проверять (но не делает этого), не аргумент ли это командной строки.
3. Первое выражение правильно, так как с[i] имеет значение типа char. Второе выражение не выводит компьютер из строя, но может давать непредсказуемый результат. Причина в том, что
isalpha( ) является макроопределением, у которого, по всей вероятности, аргумент появляется дважды в определяющем выражении (проверка на принадлежность к регистру строчных букв, а зачем - прописных букв) и это дает в результате два увеличения i. Лучше всего избегать использования оператора увеличения в аргументе макрофункции.
4.
#include
#include
#define issign(c) (((c) == '-' || (c) == '+') ? (1) : (0)) atoi(s);
316
char *s;
{
int i = 0;
int n, sign;
while(isspace(s[i]))
i ++; /* пропуск пустого символа */
sign = 1;
if(issign(s[i])) /* установка необязательного знака */
sign = (s[i++] == '+') ? 1 : -1;
for(n = 0; isdigit(s[i]); i++)
n = 10*n + s[i] - '0';
return(sign * n);
}
5. Предположим, что wine является именем структуры. Эти операторы, надлежащим образом расположенные в программе, будут выполнять данную работу.
struct wine *ptrwine; char *calloc( );
ptrwine = (struc wine *) calloc(100, sizcof(struct wine));
УПРАЖНЕНИЯ
1. Напишите программу копирования файла, которая использует имена исходного файла файла и копируемого файла как аргументы командной строки.
2. Напишитe программу, которая будет принимать все файлы, заданные радом аргументов командной строки, и печатать их один за другим. Используйте argc для создания цикла.
3. Модифицируйте вашу программу инвентаризации книг в гл. 14 так, чтобы информация,
которую вы вводите, добавлялась в файл, названный mybooks.
4. Используйте gets( ) и atoi( ) для создания функции, эквивалентной нашей getint( ) в гл. 10.
5. Перепишите нашу программу из гл. 7, считающую слова, используя макроопределения ctype.h
и аргумент командной строки для обработки файла.
[Содержание]
[Вверх]
Язык Си
Библиотека языка Си и файлы ввода-
вывода
M. Уэйт, С. Прата, Д. Мартин
Язык Си
[Содержание]
[Вниз]
ПРИЛОЖЕНИЕ А
ДОПОЛНИТЕЛЬНАЯ ЛИТЕРАТУРА
317
Если вы хотите больше узнать о языке Си и вообще о программировании, то найдете полезной следующую литературу:
Язык СиДалее
Kernighan Brian W. and Ritchie Dennis M., The С Programming Language, Prentice-Hall, 1978.
(Имеется перевод: КЕРНИГАН Б., Ритчи Д.
Язык програлширования Си.- M.: Финансы и статисти ка,
1985.)
Первая и наиболее авторитетная книга по языку Си. (Заметим. что один из авторов этой книги
Деннис Ритчи - создатель языка Си.) Практически она является официальным описанием языка и включает много интересных примеров. Однако авторы предполагают, что читатель знаком с системным программированием.
Feuer Alan R., The С Puzzle Hook, Prentice-Hall, 1982. (Имеется перевод: Фьюэр А.
Задачи поязыку Си.- M.: Финансы и статистика, 1985.)
Книга содержит большое количество программ, результат работы которых вы можете предсказать.
Она дает хорошую возможность проверить и расширить ваши знания о языке Си. Книга включает ответы и пояснения.
Ritchie D. M., Johnson S. С., Lesk M. E., and Kernighan В. W., The С Programming Language, TheBell System Technical Journal, Vol. 57, No. 6, July-August 1978.В статье обсуждается история создания языка Си и дается обзор особенностей программирования с использованием этого языка.
BYTE, Vol. 8, No. 8, August 1983.Этот выпуск журнала "Байт" посвящен языку Си. Он содержит статьи, где обсуждаются история его создания, концепции и применения. Проверяются и оцениваются двенадцать компиляторов языка
Си для микропроцессоров. Включена также обширная современная библиография книг и статей по языку Си. Каждая книга и статья включает краткое содержание.
ПрограммированиеДалее
Содержание
Kernighan Brian W. and Plauger P. J., The Elements of Programming Style (Second Edition),McGraw-HiII, 1978.В этом стройном
классическом произведении используются примеры, взятые из других книг, для иллюстрации того, что нужно и что не нужно делать для однозначного и результативного программирования.
Kernighan Brian V. and Plauger P. J., Software Tools, Addison-Wesley, 1976.В книге описывается несколько полезных программ и программных систем, причем делается упор на эффективное проектирование структур программ. Представлены описания языка RATFOR
(рационализированного Фортрана) и одной из версий Паскаля. Так как создание языка RATFOR - это попытка сделать сходной работу языков Фортран и Си, он лучше всего подходит пользователям языка как для знакомства с ним.
Операционная система UNIXДалее
Содержание
Waite Mitchell, Martin Don and Praia Stephen , UNIX Primer Plus, Howard W. Sams and Company,318
Inc., 1983.
Эта книга - легко читаемое введение в операционную систему UNIX. В неe включены некоторые мощные расширения этой системы, реализованные в Калифорнийском университете (Беркли).
ПРИЛОЖЕНИЕ Б
КЛЮЧЕВЫЕ СЛОВА ЯЗЫКА СИ
Далее
Содержание
Ключевые слова в языке являются словами, выражающими действия этого языка. Ключевые слова языка Си зарезервированы, т. е. вы не можете использовать их для других целей, таких как задание имени переменной.
Ключевые слова выполнения программы
Циклы
for while do
Принятие решения и выбор
if else switch case default
Переходы
break continue goto
Типы данных
char int short long unsigned float double struct union typedef
Классы памяти
auto extern register static
Разное
return sizeof
Еще не реализованное
entry
Применяемые только в некоторых системах
asm endasm fortran enum
ПРИЛОЖЕНИЕ В
ОПЕРАЦИИ ЯЗЫКА СИ
Далее
Содержание
В языке Си предусмотрено множество операций. Затем мы приводим здесь таблицу операций,
располагая их по приоритетам и показывая порядок выполнения. Мы рассказываем о всех операциях, за исключением поразрядных, которые будут рассмотрены в приложении Е.
319
I. Арифметические операции+
Прибавляет величину, находящуюся справа, к величине, стоящей слева
-
Вычитает величину, стоящую справа, из величины, указанной слева
-
Будучи унарной операцией, изменяет знак величины, стоящей справа
*
Умножает величину справа на величину, находящуюся слева
/
Делит величину, стоящую слева, на величину справа. Результат усекается,
если оба операнда целые числа%
Дает остаток от деления величины слева на величину, стоящую справа (только для целых чисел)
++
Прибавляет 1 к значению переменной, стояшей слева (префикная форма), или к переменной, стояшей справа (постфиксная форма)
--
Аналогично ++, но вычитает 1
Операции (от высшего приоритета к низшему)
Порядок выполнения
() {} -> .
Л-П
! ++ -- - (тип) * & sizeof (все унарные)
П-Л
* / %
Л-П
+ -
Л-П
<< >>
Л-П
< <= > >=
Л-П
== !==
Л-П
&
Л-П
/\
Л-П
|
Л-П
&&
Л-П
||
Л-П
?:
Л-П
= += -= *= /* %=
П-Л
,
Л-П
Условные обозначения: Л-П - порядок выполнения слева направо, а П-Л - наоборот.
II. Операции присваивания= Присваивает значение, указанное справа, переменной, стоящей слева
Каждая из приводимых ниже операции изменяет переменную, стоящую слева, на величину,
находящуюся справа. Мы используем следующие обозначения: П для правой части и Л для левой.
+ = прибавляет величину П к переменной Л
-=
вычитает величину П из переменной Л
*=
умножает переменную Л на величину П
/=
делит переменную Л на величину П
%= выдает остаток от деления переменной Л на величину П
320
Пример:
rabbits *= 1.6; то же самое, что rabbits = rabbits * 1.6;
III. Операции отношения
Каждая из этих операций сравнивает значение слева со значением справа. Оператор отношения,
состоящий из операции и двух ее операндов, имеет значение 1, если выражение истинно, и значение 0, если выражение ложно.
< меньше
<= меньше или равно
== равно
>= больше или равно
> больше
!= не равно
IV. Логические операции
Обычно логические операции "считают" условные выражения операндами. Операция ! имеет один операнд, расположенный справа. Остальные операции имеют два операнда: один слева и один справа.
&&
Логическое И: результат операции имеет значение "истина", если оба операнда истинны, и "ложь" в противном случае.
||
Логическое ИЛИ: результат операции имеет значение "истина", если один или оба операнда истинны, и "ложь" в противном случае.
!
Логическое НЕ: результат имеет значение "истина", если операнд имеет значение "ложь", и наоборот.
V. Операции над указателями
&
Операция получения адреса: выдаст адрес переменной, имя которой стоит за обозначением операции: &nurse является адресом переменной nurse
*
Косвенная адресация: выдает значение, записанное по адресу, на который ссылается указатель:
nurse = 22;
ptr = &nurse; /* указатель на nurse */
val = *ptr
В результате работы этих операторов переменной val присваивается значение 22.
VI. Операции над структурами и объединениями
321
.Операция принадлежности (точка) используется совместно с именем структуры (или объединения) для задания элемента этой структуры (иди объединения). Если name является именем структуры, а
member - элементом, указанным в структурном шаблоне, то
name.member определяет этот элемент структуры. Операцию принадлежности можно аналогичным образом применять и к объединениям.
Пример:struct {
int code;
float cost;
} item;
item.code = 1265;
Выполняется присваивание значения элементу
code структуры
item.
->
Косвенная адресация (определение принадлежности) элемента используется совместно с указателем на структуру (или объединение) для идентификации того или иного элемента этих структур (или объединения). Предположим, что