Дополнительные сведения о препроцессоре языка C. Препроцессор обрабатывает текст программы перед компиляцией. 1. Включение файлов (рассмотрено ранее): #include <conio.h> - файл из специального каталога; #include "d:\\user\\ff.h" - файл ищется по правилам MS DOS. 2. Текстовые подстановки (рассматривалось ранее): #define N 21 #define ABC (a*b*c+\ d*sin(x)) \ - переход на следующую строку. 3. Создание макросов. #define SQR(x) ((x)*(x)) В результирующую строку подставляется фактическая строка x. Например, если в тексте программы встречается SQR(y), то после макрораскрутки получим ((y)*(y)). Скобки нужны для того, чтобы не получилось недоразумений, например #define SQR(x) x*x SQR(y+2); превратится в y+2*y+2; Конечно, это не то, что хотелось. 4. Отмена ранее созданного имени: #undef SQR. 5. Условная компиляция: #if константное выражение . . строки программы . #else . . строки программы . #endif Если константное выражение истинно, то в программу будут включены строки из первого блока, иначе из второго. Пример: #define DEBUG 1 . . . #if DEBUG printf("%d", x); #endif Можно проверить наличие или отсутствие какого-либо имени: #ifdef имя (если имя определено, то истина) . . . #else . . . #endif #ifndef имя (если имя не определено) . . . #else . . . #endif Это свойство широко используется в библиотечных включаемых файлах, чтобы избежать противоречия. Например, в conio.h имеются следующие строки: #ifndef COLORS enum COLORS {BLACK, ...}; #endif Условное выражение. Эта конструкция языка C в некоторых случаях позволяет заменить оператор if и сократить запись программы. условное выр = выр0 "?" выр1 ":" выр2 Значение условного выражения равно выр1, если выр0 не равно 0 и выр2 впротивном случае. Пример: a = b>c ? b:c; тоже самое, что if (b>c) a=b; else a = c; С помощью условного выражения можно, например, определить макросы max и min: #define max(x,y) ((x)>(y) ? (x):(y)) #define min(x,y) ((x)<(y) ? (x):(y)) При этом макросы max и min будут работать для любых типов арифметических данных. Например: a = max(b,c); Приоритеты и направления операций. ---------------------------------------------------------- | | | |--------------------------------------------------|-----| | () вызов функции | | | [] выделение элемента массива | | | . выделение элементов структуры или объеди- | -> | | нения | | | -> выделения элементов структуры или объеди- | | | нения | | |--------------------------------------------------|-----| | ! логическое отрицание | | | ~ побитовое отрицание | | | - изменение знака | | | ++ увеличение на единицу | | | -- уменьшение на единицу | <- | | & определение адреса | | | * обращение по адресу | | | (тип) преобразование типа | | | sizeof определение размера в байтах | | |--------------------------------------------------|-----| | * умножение | | | / деление | -> | | % остаток от деления | | |--------------------------------------------------|-----| | + сложение | | | - вычитание | -> | |--------------------------------------------------|-----| | << сдвиг влево | | | >> сдвиг вправо | -> | |--------------------------------------------------|-----| | < меньше | | | <= меньше или равно | | | > больше | | | >= больше или равно | | |--------------------------------------------------|-----| | == равно | | | != не равно | -> | |--------------------------------------------------|-----| | & побитовое и | -> | |--------------------------------------------------|-----| | ^ побитовое исключающее или | -> | |--------------------------------------------------|-----| | | побитовое или | -> | |--------------------------------------------------|-----| | && логическое и | -> | |--------------------------------------------------|-----| | || логическое или | -> | |--------------------------------------------------|-----| | ? : условная операция | -> | |--------------------------------------------------|-----| | = присваивание | | | *= /= %= += -= | <- | | <<= >>= &= ^= |= | | |--------------------------------------------------|-----| | , операция запятая | -> | |__________________________________________________|_____| Динамические данные. Линейные списки. Использование указателей на структуры позволяет использовать весьма сложно организованные данные типа различного рода списков, очередей и деревьев. Рассмотрим так называемые линейные списки. Линейный список - это упорядоченная структура данных, каждый элемент которой содержит ссылку (указатель), связывающую его со следующим элементом. Для организации списков служат структуры, состоящие из двух смысловых частей: основной, содержащей полезную информацию, и дополнительной, содержащей ссылку на следующий элемент списка. Графически это можно представить следующим образом: ......... ......... ......... ......... ........... : : : : : : : : : : : : : :NULL: ......... ......... ......... ......... ........... В виде списков удобно представлять большие объемы информации, размер которых заранее неизвестен. Рассмотрим, например, как можно было бы организовать хранение информации о товарах для программы "магазин". typedef struct _GOODS{ char name[21]; int number; float price; struct _GOODS *next; } GOODS; Очевидно, программа должна иметь переменную - указатель на первый элемент списка. Последний элемент списка отличается тем, что в поле next имеет константу NULL - то есть пустой указатель. Тогда список может быть представлен так: ......... ......... ......... ........... TOP : : : : : : : : : : :NULL: ......... ......... ......... ........... Программа формирования списка товаров выглядит следующим образом: GOODS *new_GOODS ( void) { GOODS *p; p = (GOODS* ) malloc( sizeof(GOODS) ); if (p==NULL) {printf("Недостаточно памяти\n"); exit(1);} return p; } GOODS *in_goods( void ) { GOODS *top, *q; printf("Введите характеристики товаров в виде:\n" \ "наименование количество цена\n" \ "-------окончание ввода \"end\"-------\n"); top = NULL; while (1) { q = new_GOODS(); if( !in_goods1(q) ) { free(q); return top; } q->next = top; top = q; } } Обращение к этой программе будет выглядеть так: top = in_goods(); Вспомогательная программа new_GOODS осуществляет все необходимые действия по выделению памяти и контролю за ее наличием. Достоинство такой организации данных в том, что используется ровно столько памяти, сколько надо (накладные расходы - поле адреса). Вместе с тем список может быть сколь угодно большим. Ограничение - память ЭВМ. Недостаток - мы не имеем возможности прямого доступа к памяти. Рассмотрим процедуру вывода на печать содержимого списка. Обратить внимание на то, что содержимое выводится в порядке обратном вводу (в принципе можно было бы и наоборот). void out_goods( GOODS *top) { printf("*--------------------------------------*\n"); printf("| Наименование | Кол-во | Цена |\n"); printf("|--------------------|--------|--------|\n"); while( top != NULL ) { printf( "| %20s | %10.2f |\n", top->name, top->number, top->price ); top = top->next; } printf("*--------------------------------------*\n"); } Головная функция должна выглядеть следующим образом: void main( void ) { GOODS *top; top = in_goods(); out_goods ( top ); } Как же быть с сортировкой, а ее вообще не надо делать, так как можно в процессе ввода получить отсортированный список, вставляя очередную запись в нужное место. Рассмотрим вспомогательную задачу: необходимо вставить запись, на которую указывает указатель g в список за записью, на которую указывает указатель p. g ......... : :. : ......|.. 2 ^ | 1 | V TOP ......... ......... ......... ......... .......... : : : : : p : : : : : : : : :NULL: ......... ......... ......... 2 ......... .......... p g->next = p->next; 1 p->next = g; 2 Текст программы выглядит следующим образом: GOODS *in_goods( void ) { GOODS *top, *q, *p; printf("Введите характеристики товаров в виде:\n" \ "наименование количество цена\n" \ "-------окончание ввода \"end\"-------\n"); /* Получение списка из двух элементов */ top = NULL; q=new_GOODS(); if(!in_goods1(q)) {free(q); return top;} q->next = NULL; top = q; q=new_GOODS(); if(!in_goods1(q)) {free(q); return top;} if(q->price < top->price ) { q->next = top; top = q; } else { q->next = NULL; top->next = q; } /* Получение списка из остальных элементов */ while( 1 ) { q=new_GOODS(); if(!in_goods1(q)) {free(q); return top;} if(q->price < top->price ) { q->next = top; top = q; } else { p = top; while(p->next && (q->price > p->next->price)) p=p->next; q->next = p->next; p->next = q; } } } /* Пример использования списка # 1 */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <alloc.h> #include <math.h> typedef struct _GOODS{ char name[21]; int number; float price; struct _GOODS *next; } GOODS; GOODS in_goods ( void ); GOODS new_goods ( void ); int in_goods1 ( GOODS *g ); void out_goods (GOODS *top ); void main( void ) { GOODS *top; top = in_goods(); out_goods ( top ); { float f=0; sin(f); } } GOODS *new_GOODS ( void ) { GOODS *p; p = (GOODS*) malloc( sizeof(GOODS) ); if (p==NULL) {printf("Недостаточно памяти\n"); exit(1);} return p; } GOODS *in_goods( void ) { GOODS *top, *q; printf("Введите характеристики товаров в виде:\n" \ "наименование количество цена\n" \ "-------окончание ввода \"end\"-------\n"); top = NULL; while (1) { q = new_GOODS(); if( !in_goods1(q) ) { free(q); return top; } q->next = top; top = q; } } int in_goods1( GOODS *g ) { scanf( "%s", q->name ); if ( strcmp(g->name, "end")== ) return 0; scanf( "%d%f", &g->number, &g->price ); return 1; } void out_goods( GOODS *top) { printf("*--------------------------------------*\n"); printf("| Наименование | Кол-во | Цена |\n"); printf("|--------------------|--------|--------|\n"); while( top != NULL ) { printf( "| %20s | %10.2f |\n", top->name, top->number, top->price ); top = top->next; } printf("*--------------------------------------*\n"); } /* Пример использования списка с сортировкой */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <alloc.h> #include <math.h> typedef struct _GOODS{ char name[21]; int number; float price; struct _GOODS *next; } GOODS; GOODS in_goods ( void ); GOODS new_goods ( void ); int in_goods1 ( GOODS *g ); void out_goods (GOODS *top ); void main( void ) { GOODS *top; top = in_goods(); out_goods ( top ); { float f=0; sin(f); } } GOODS *new_GOODS ( void ) { GOODS *p; p = (GOODS*) malloc( sizeof(GOODS) ); if (p==NULL) {printf("Недостаточно памяти\n"); exit(1);} return p; } GOODS *in_goods( void ) { GOODS *top, *q, *p; printf("Введите характеристики товаров в виде:\n" \ "наименование количество цена\n" \ "-------окончание ввода \"end\"-------\n"); /* Получение списка из двух элементов */ top = NULL; q=new_GOODS(); if(!in_goods1(q)) {free(q); return top;} q->next = NULL; top = q; q=new_GOODS(); if(!in_goods1(q)) {free(q); return top;} if(q->price < top->price ) { q->next = top; top = q; } else { q->next = NULL; top->next = q; } /* Получение списка из остальных элементов */ while( 1 ) { q=new_GOODS(); if(!in_goods1(q)) {free(q); return top;} if(q->price < top->price ) { q->next = top; top = q; } else { p = top; while(p->next && (q->price > p->next->price)) p=p->next; q->next = p->next; p->next = q; } } } int in_goods1( GOODS *g ) { scanf( "%s", q->name ); if ( strcmp(g->name, "end")== ) return 0; scanf( "%d%f", &g->number, &g->price ); return 1; } void out_goods( GOODS *top) { printf("*--------------------------------------*\n"); printf("| Наименование | Кол-во | Цена |\n"); printf("|--------------------|--------|--------|\n"); while( top != NULL ) { printf( "| %20s | %10.2f |\n", top->name, top->number, top->price ); top = top->next; } printf("*--------------------------------------*\n"); } |