Препроцессор

Пример

int func();

Функция, не возвращающая значения, должна описываться как имеющая тип void. Если тип функции явно не задан, считается, что она имеет тип int. Областью действия описания на внешнем уровне является остаток файла; внутри блока областью действия является данный блок. Обычно внешние описания располагаются в начале файла. Некоторые компиляторы допускают описание переменных на внешнем уровне без прилагательного extern. Многократные описания внешних переменных компоновщик сводит к одному определению.

Если в качестве первого символа в строке программы используется символ #, то эта строка является командной строкой препроцессора (макропроцессора). Командная строка препроцессора заканчивается символом перевода на новую строку. Если непосредственно перед концом строки поставить символ обратной косой (\), то командная строка будет продолжена на следующую строку программы.

Замена идентификаторов

#define идентификатор строка

Пример

#define ABC 100

Заменяет каждое вхождение идентификатора ABC а тексте программы на 100.

#undef идентификатор

Пример

#undef ABC Отменяет предыдущее определение для идентификатора ABC.

Макросы

Замечание. Во избежание ошибок при вычислении выражений параметры макроопределения необходимо заключать в скобки.

#define идентификатор! (идентификатор'1,. .. ) строка

Пример

#define abs(A) (((A) > 0) ? (А) . -(А)) Каждое вхождение выражения abs(arg) в тексте программы заменяется на ((arg) > 0) ? (arg) : -(arg), причем параметр макроопределения А заменяется на arg.

Пример

#define nmem (P, N)\

(Р) -> p_mem [N] .u_long Символ \ продолжает макроопределение на вторую строку. Это макроопределение уменьшает сложность выражения, описывающего массив объединений внутри структуры.

Включение файлов

Замечание. Командная строка #include может встречаться в любом месте программы, но обычно все включения размешаются в начале файла исходного текста.

#include (имя _файла'1

Пример

#mclude <riath.h>

Препроцессор заменяет эту строку содержимым файла math.h. Угловые скобки обозначают, что файл math.h будет взят из некоторого стандартного каталога (обычно это /usr/mclude). Текущий каталог не просматривается. #include "имя_файла"

Пример

#mclude "ABC"

Препроцессор заменяет эту строку содержимым файла ABC. Так как имя файла заключено в кавычки, то поиск производится в текущем каталоге (в котором содержится основной файл исходного текста). Если в текущем каталоге данного файла нет, то поиск производится в каталогах, определенных именем пути в опции -1 препроцессора Если и там файла нет, то просматривается стандартный каталог

Условная компиляция

Командные строки препроцессора используются для условной ком пиляции различных частей исходного текста в зависимости от внешних условий # if константное _выражение

Пример

#if АВС + 3

Истина, если конвтантное выражение АВС + 3 не равно нулю #fdef идентификатор

Пример

#ifdef АВС

Истина, если идентификатор АВС определен ранее командой #define #ifndef идентификатор

Пример

#ifndef АВС

Истина, если идентификатор АВС не определен в настоящий момент #else

#endif

Если предшествующие проверки #if, #ifdef или #ifndef дают значение Истина, то строки от #else до #endif игнорируются при компиляции

Если эти проверки дают значение Ложь, то строки 01 проверки до #else (а при отсутствии #else — до #endif) игнорируются Команда #endif обозначает конец условной компиляции

Пример

#ifdef DEBUG

iprmtf (stderr, "location x = %d\ n", x), #endif

Номер строки и имя файла

#line целая _константа "имя __файла"

Пример

#line 20 "АВС"

Препроцессор изменяет номер текущей строки и имя компилируемого файла Имя файла может быть опущено

СТРУКТУРА ПРОГРАММЫ

Программа, описанная в следующем примере, вводит до MAXLINES строк со стандартного входа, сортирует строки в лексикографическом порядке, возрастающем или уменьшающемся в зависимости от признака, передаваемого функции mainQ через аргумент argv Затем программа записывает отсортированные строки на стандартный выход Стандартными входом и выходом (stdin и stdout) могут быть терминал, канал или некоторый файл

//файл bblsort.h

#define MAXLINES 100

#define LINLSIZE (132+1)

main.c

include <stdio h>

include "bblsort h"

char Lme[MAXLINES]ELINESIZE], /* буфер строк */ С int Revfig , /* признак направления * сортировки */

/*

* сортировка строк текста в лексикографическом порядке

*/

main(argc, argv)

D char **argv, /* аргументы вызова программы */

int argc, /* число аргументов */

{

int rdlinesO;

void bblsortO, wrlines();

int numlines;

G Revflg = (argc > 1 && |argv[l][0] [ == '-');

numlines = rdlinesO;

bblsort(numlmes);

wrlines(numlines),

/* запись строк со стандартного выхода*/

Н static int rdlinesO

chai^ *fgets(), / register int l;

/ foi(i=0,i< MAXLINES; i++)

if (fgets(Lme[i], LINESIZE, stdin)

/ == ] (char *)NULL ) break;

return (i);

}

/* запись строк на стандартный выход*/

Структура программы 57

К static void wrhnes(n) L register mt n; /* число строк */

{

register int l,

for (i = 0; i < n; i++) fputs(Lme [i], stdout);

bblsort.c

# include "bblsort.h"

N extern char Line [] [LINESIZE]; [

/* bubble sort */

void bblsort(n) register int n; /* число строк */

int lexcmp();

void swap();

regicterint i,j;

for (i = 1; i <= n - 1; i++) for(J=n- 1;j>= i;J—) if (lexcmp(j - 1, J)) swapO - l.j);

/* лексикографическое сравнение двух строк */

static int lexcmp(i,J)

register int i, j; /* элементы массива строк */ {

strcmp();

extern int Revflg;

register int Ic;

Ic = strcmp(Line [i], Line [j]);

return ((Ic < 0 && Revflg)

(Ic > 0 && '.Revflg));

/* обмен строк */

static void swap(iJ) register int i, j; /* элементы массива строк */

char *strcpy();

char temp [LINESIZE];

strcpy(temp. Line [i]);

strcpy(Line [i], Lme [j]);

strepy(Line [j], temp);

Пояснения к программе

А) Поименованные константы, используемь-е во всей программе, обычно помещаются в отдельный файл, включаемый в другие файлы программы по мере необходимости. Поэтому при изменении этих параметров программы будет затронут только один файл.

В) Включаемые файлы обычно помещаются в начало некоторого файла программы. Файл stdio h содержит описания файлов stdm. stdout и константы NULL, необходимых для использования функций fgets() и fputsQ

С) Описания внешних переменных обычно размещаются в начале файла. В данной программе определены глобальный массив буфера строк и глобальный признак направления сортировки.

D) Если функция main() использует формальные параметры, то они должны быть описаны. Функция main() выполняется первой.

Е) Типы функций, вызываемых в теле функции, обычно описываются в начале тела функции.

F) Переменная numlmes описана как локальная в блоке автоматическая целая переменная

G) По соглашению первый аргумент argv, передаваемый функции main(), является именем программы; argv [l] — это второй аргумент и argv[l] [О] - это первый символ второго аргумента. Обратите внимание, что перед обращением к argv [l ] [0] проверяется число аргументов argc, так как при отсутствии второго аргумента выражение argv [l] [О] не имеет смысла.

H) Функция rdlmesQ возвращает целое число прочитанных строк, так как она определена как имеющая тип mt. Прилагательное static указывает, что функция используется только в данном файле.

I) Переменная i определена как локальная в блоке. Описание register — это попытка ускорить выполнение цикла for

J) Поскольку функция fgetsO возвращает значение типа char *,то это значение должно сравниваться с указателем на символ. Поэтому нулевой указатель NULL преобразуется к типу (char *).

К) Функция wrhnesO не возвращает значения, поэтому она опреде лена как имеющая тип void.

L) Формальный параметр п определен как register для ускорения цикла.

М) Включение файла bblsort.h определяет поименованную константу LINESIZE.

N) Это описание массива Line относится ко всему последующему файлу исходного кода. Здесь описывается тип Line, но сам массив определен в предыдущем файле.

О) Функция bblsortO не описывается как static, потому что она вызывается функцией main(), которая определена в другом файле.

Р) Формальные параметры i и j объявлены с помощью одного описания. Порядок параметров в таком описании несуществен.

Q) Глобальный признак Revfig должен быть описан как extern, чтобы показать, что эта переменная определена в другом файле. Это описание может располагаться в начале текущего файла вместе с описанием массива Line

R) Значением этого выражения является или Истина (1), или Ложь (0), кодируемые целыми значениями. Поэтому функция 1ехстр() определена как имеющая тип int.