если стек пуст. */
int empty_stack(Stack stack) {
return stack == 0;
}
/* Удаление числа, находящегося на вершине стека.
Если стек пуст, программа аварийно завершается. */
number pop_stack(Stack* stack) {
number answer;
Stack rest_of_stack;
assert(!empty_stack(*stack));
answer = (*stack)->element_;
rest_of_stack = (*stack)->next_;
free(*stack);
*stack = rest_of_stack;
return answer;
}
/* Добавление числа в начало стека. */
void push_stack(Stack* stack, number n) {
Stack new_stack =
malloc(sizeof(struct StackElement));
new_stack->element_ = n;
new_stack->next_ = *stack;
*stack = new_stack;
}
/* Очистка стека. */
void clear_stack(Stack* stack) {
while(!empty_stack(*stack)) {
number top = pop_stack (stack);
destroy_number(top);
}
}
В листинге А.6 показаны объявления типов данных и функций работы со стеком и унарными числами.
Листинг А.6. ( definitions.h ) Файл заголовков для файлов number.c
и stack.c
#ifndef DEFINITIONS_H
#define DEFINITIONS_H 1
/* Представление числа в виде связного списка. */
struct LinkedListNumber {
struct LinkedListNumber* one_less_;
};
typedef struct LinkedListNumber* number;
/* Реализация стека чисел, представленных в виде
связных списков. Значение 0 соответствует
пустому стеку. */
struct StackElement {
number element_;
struct StackElement* next_;
};
typedef struct StackElement* Stack;
/* Операции над стеком. */
Stack create_stack();
int empty_stack(Stack stack);
number pop_stack Stack* stack);
void push_stack(Stack* stack, number n);
void clear_stack(Stack* stack);
/* Операции над числами */
number make_zero();
void destroy_number(number n);
number add(number n1, number n2);
number subtract(number n1, number n2);
number product(number n1, number n2);
number even(number n);
number odd(number n);
number string_to_number(char* char_number);
unsigned number_to_unsigned_int(number n);
#endif /* DEFINITIONS_H */
Приложение Б
Низкоуровневый ввод-вывод
Программисты, пишущие Linux-программы на языке С. имеют в своем распоряжении два набора функций ввода-вывода. Один из них включен в стандартную библиотеку языка С: printf()
, fopen()
и т.д. [41] В стандартной библиотеке языка C++ аналогичным целям служат потоки ввода-вывода .
Мы предполагаем, что читатели уже знакомы с языком С и знают, как использовать эти функции ввода-вывода, поэтому не будем их подробно описывать.
Ядро Linux предоставляет собственные операции ввода-вывода, работающие на более низком уровне. В основном они имеют вид системных вызовов и обеспечивают самый непосредственный доступ к файловой системе. По сути, стандартные библиотечные функции реализованы на их основе. Низкоуровневые вызовы обеспечивают наибольшую эффективность операций ввода-вывода.
Б.1. Чтение и запись данных
Первая функция ввода-вывода, с которой сталкиваются те, кто начинают изучать язык С, называется printf()
. Она форматирует текстовую строку и записывает ее в стандартный выходной поток. Обобщенная ее версия fprintf()
записывает текст в заданный поток. Поток данных представляется в программе указателем типа FILE*
. Чтобы получить этот указатель, необходимо открыть файл с помощью функции fopen()
. По завершении работы с файлом его необходимо закрыть с помощью функции fclose()
. Помимо функции fprintf()
существуют также функции fputc()
, fputs()
и fwrite()
, записывающие данные в поток. Функции fscanf()
, fgetc()
, fgets()
и fread()
читают данные из потока.
В низкоуровневых операциях ввода-вывода участвуют не файловые указатели, а дескрипторы. Дескриптор представляет собой целое число, обозначающее конкретный экземпляр файла, открытого в одном процессе. Файл можно открыть для чтения, записи, а также одновременно для чтения и записи. Файловому дескриптору не обязательно соответствует файл: это может быть другой системный компонент, способный передавать или принимать данные (аппаратное устройство, сокет, противоположный конец канала).
Для работы с описанными ниже низкоуровневыми функциями необходимо включить в программу файлы , , и .
Чтобы открыть файл и получить дескриптор для работы с ним, необходимо вызвать функцию open()
. В качестве аргументов она принимает строку с путевым именем файла и флаги, определяющие способ открытия. С помощью функции open()
можно также создать новый файл. Для этого ей нужно передать третий аргумент, определяющий права доступа к файлу.
Читать дальше