рефераты

рефераты

 
 
рефераты рефераты

Меню

Реферат: Язык С рефераты

чати каждой строки ввода, которая содержит определенную ком-

бинацию символов. /Это - специальный случай утилиты GREP

системы “UNIX”/. Например, при поиске комбинации “THE” в на-

боре строк

                                                     

NOW IS THE TIME

FOR ALL GOOD

MEN TO COME TO THE AID

OF THEIR PARTY

в качестве выхода получим

NOW IS THE TIME

MEN TO COME TO THE AID

OF THEIR PARTY

 

 

основная схема выполнения задания четко разделяется на три

части:

 

WHILE (имеется еще строка)

IF (строка содержит нужную комбинацию)

вывод этой строки


·     74 -

 

Конечно, возможно запрограммировать все действия в виде

одной основной процедуры, но лучше использовать естественную

структуру задачи и представить каждую часть в виде отдельной

функции. С тремя маленькими кусками легче иметь дело, чем с

одним большим, потому что отдельные не относящиеся к сущест-

ву дела детали можно включить в функции и уменьшить возмож-

ность нежелательных взаимодействий. Кроме того, эти куски

могут оказаться полезными сами по себе.

 

“Пока имеется еще строка” - это GETLINE, функция, кото-

рую мы запрограммировали в главе 1, а “вывод этой строки” -

это функция PRINTF, которую уже кто-то подготовил для нас.

Это значит, что нам осталось только написать процедуру для

определения, содержит ли строка данную комбинацию символов

или нет. Мы можем решить эту проблему, позаимствовав разра-

ботку из PL/1: функция INDEX(S,т) возвращает позицию, или

индекс, строки S, где начинается строка T, и -1, если S не

содержит т . В качестве начальной позиции мы используем 0, а

не 1, потому что в языке “C” массивы начинаются с позиции

нуль. Когда нам в дальнейшем понадобится проверять на совпа-

дение более сложные конструкции, нам придется заменить толь-

ко функцию INDEX; остальная часть программы останется той же

самой.

После того, как мы потратили столько усилий на разработ-

ку, написание программы в деталях не представляет затрудне-

ний. ниже приводится целиком вся программа, так что вы може-

те видеть, как соединяются вместе отдельные части. Комбина-

ция символов, по которой производится поиск, выступает пока

в качестве символьной строки в аргументе функции INDEX, что

не является самым общим механизмом. Мы скоро вернемся к об-

суждению вопроса об инициализации символьных массивов и в

главе 5 покажем, как сделать комбинацию символов параметром,

которому присваивается значение в ходе выполнения программы.

Программа также содержит новый вариант функции GETLINE; вам

может оказаться полезным сравнить его с вариантом из главы

1.

 

#DEFINE  MAXLINE  1000

MAIN()  /* FIND ALL LINES MATCHING A PATTERN */

 {

CHAR LINE[MAXLINE];

WHILE (GETLINE(LINE, MAXLINE) > 0)

IF (INDEX(LINE, “THE”) >= 0)

PRINTF(“%S”, LINE);

  }

     

·     75 -

    

    

GETLINE(S, LIM) /* GET LINE INTO S, RETURN LENGTH *

CHAR S[];

INT LIM;

  {

INT C, I;

I = 0;

WHILE(--LIM>0 && (C=GETCHAR()) != EOF && C != '\N')

S[I++] = C;

IF (C == '\N')

S[I++] = C;

S[I] = '\0';

RETURN(I);

  }

 

INDEX(S,T) /* RETURN INDEX OF T IN S,-1 IF NONE */

CHAR S[], T[];

  {

INT I, J, K;

FOR (I = 0; S[I] != '\0'; I++) {

FOR(J=I, K=0; T[K] !='\0' && S[J] == T[K]; J++; K++)

     ;

IF (T[K] == '\0')

RETURN(I);

      }

RETURN(-1);

  }

    

Каждая функция имеет вид имя (список аргументов, если они

имеются) описания аргументов, если они имеются

 

  {

описания и операторы , если они имеются

  }

 

 

Как и указывается, некоторые части могут отсутство-

вать; минимальной функцией является

 

DUMMY ()  { }

которая не совершает никаких действий.

/Такая ничего не делающая функция иногда оказывается

удобной для сохранения места для дальнейшего развития прог-

раммы/. если функция возвращает что-либо отличное от целого

значения, то перед ее именем может стоять указатель типа;

этот вопрос обсуждается в следующем разделе.

    

·     76 -

    

Программой является просто набор определений отдельных

функций. Связь между функциями осуществляется через аргумен-

ты и возвращаемые функциями значения /в этом случае/; ее

можно также осуществлять через внешние переменные. Функции

могут располагаться в исходном файле в любом порядке, а сама

исходная программа может размещаться на нескольких файлах,

но так, чтобы ни одна функция не расщеплялась.

Оператор RETURN служит механизмом для возвращения зна-

чения из вызванной функции в функцию, которая к ней обрати-

лась. За RETURN может следовать любое выражение:

RETURN (выражение)

Вызывающая функция может игнорировать возвращаемое

значение, если она этого пожелает. Более того, после RETURN

может не быть вообще никакого выражения; в этом случае в вы-

зывающую программу не передается никакого значения. Управле-

ние также возвращется в вызывающую программу без передачи

какого-либо значения и в том случае, когда при выполнении мы

“проваливаемся” на конец функции, достигая закрывающейся

правой фигурной скобки. EСли функция возвращает значение из

одного места и не возвращает никакого значения из другого

места, это не является незаконным, но может быть признаком

каких-то неприятностей. В любом случае “значением” функции,

которая не возвращает значения, несомненно будет мусор. От-

ладочная программа LINT проверяет такие ошибки.

Механика компиляции и загрузки “C”-программ, располо-

женных в нескольких исходных файлах, меняется от системы к

системе. В системе “UNIX”, например, эту работу выполняет

команда 'CC', упомянутая в главе 1. Предположим, что три

функции находятся в трех различных файлах с именами MAIN.с,

GETLINE.C и INDEX.с . Тогда команда

      

CC MAIN.C GETLINE.C INDEX.C

компилирует эти три файла, помещает полученный настраиваемый объектный код в файлы MAIN.O, GETLINE.O и INDEX.O и загружа-ет их всех в выполняемый файл, называемый A.OUT .

Если имеется какая-то ошибка, скажем в MAIN.C, то этот

файл можно перекомпилировать отдельно и загрузить вместе с

предыдущими объектными файлами по команде

 

CC MAIN.C GETLIN.O INDEX.O

Команда 'CC' использует соглашение о наименовании с “.с” и “.о” для того, чтобы отличить исходные файлы от объектных.

Упражнение  4-1.

Составьте программу для функции RINDEX(S,T), которая

возвращает позицию самого правого вхождения т в S и -1, если

S не содержит T.

·          
77 -

 

4.2.             Функции, возвращающие нецелые значения.

 

До сих пор ни одна из наших программ не содержала како-

го-либо описания типа функции. Дело в том, что по умолчанию

функция неявно описывается своим появлением в выражении или

операторе, как, например, в

 

WHILE (GETLINE(LINE, MAXLINE) > 0)

Если некоторое имя, которое не было описано ранее, появ-

ляется в выражении и за ним следует левая круглая скобка, то

оно по контексту считается именем некоторой функции. Кроме

того, по умолчанию предполагается, что эта функция возвраща-

ет значение типа INT. Так как в выражениях CHAR преобразует-

ся в INT, то нет необходимости описывать функции, возвращаю-

щие CHAR. Эти предположения покрывают большинство случаев,

включая все приведенные до сих пор примеры.

Но что происходит, если функция должна возвратить значе-

ние какого-то другого типа ? Многие численные функции, такие

как SQRT, SIN и COS возвращают DOUBLE; другие специальные

функции возвращают значения других типов. Чтобы показать,

как поступать в этом случае, давайте напишем и используем

функцию ATоF(S), которая преобразует строку S в эквивалент-

ное ей плавающее число двойной точности. Функция ATоF явля-

ется расширением атоI, варианты которой мы написали в главах

2 и 3; она обрабатывает необязательно знак и десятичную точ-

ку, а также целую и дробную часть, каждая из которых может

как присутствовать, так и отсутствовать./эта процедура пре-

образования ввода не очень высокого качества; иначе она бы

заняла больше места, чем нам хотелось бы/.

Во-первых, сама ATоF должна описывать тип возвращаемого

ею значения, поскольку он отличен от INT. Так как в выраже-

ниях тип FLOAT преобразуется в DOUBLE, то нет никакого смыс-

ла в том, чтобы ATOF возвращала FLOAT; мы можем с равным ус-

пехом воспользоваться дополнительной точностью, так что мы

полагаем, что возвращаемое значение типа DOUBLE. Имя типа

должно стоять перед именем функции, как показывается ниже:

 

DOUBLE ATOF(S) /* CONVERT STRING S TO DOUBLE */

CHAR S[];

 {

DOUBLE VAL, POWER;

INT  I, SIGN;

·                
78 -

    

FOR(I=0; S[I]==' ' \!\! S[I]=='\N' \!\! S[I]=='\T'; I++)

    ;       /* SKIP WHITE SPACE */

   SIGN = 1;

   IF (S[I] == '+' \!\! S[I] == '-')   /* SIGN */

SIGN = (S[I++] == '+') ? 1 : -1;

FOR (VAL = 0; S[I] >= '0' && S[I] <= '9'; I++)

VAL = 10 * VAL + S[I] - '0';

IF (S[I] == '.')

I++;

FOR (POWER = 1; S[I] >= '0' && S[I] <= '9'; I++) {

VAL = 10 * VAL + S[I] - '0';

POWER *= 10;

    }

RETURN(SIGN * VAL / POWER);

 }

    

Вторым, но столь же важным, является то, что вызывающая

функция должна объявить о том, что ATOF возвращает значение,

отличное от INT типа. Такое объявление демонстрируется на

примере следующего примитивного настольного калькулятора

/едва пригодного для подведения баланса в чековой книжке/,

который считывает по одному числу на строку, причем это чис-

ло может иметь знак, и складывает все числа, печатая сумму

после каждого ввода.

 

#DEFINE   MAXLINE   100

MAIN()  /* RUDIMENTARY DESK CALKULATOR */

 {

DOUBLE SUM, ATOF();

CHAR LINE[MAXLINE];

SUM = 0;

WHILE (GETLINE(LINE, MAXLINE) > 0)

PRINTF(“\T%.2F\N”,SUM+=ATOF(LINE));

 

Оисание

DOUBLE  SUM, ATOF();

 

говорит, что SUM является переменной типа DOUBLE , и что

ATOF является функцией, возвращающей значение типа DOUBLE .

Эта мнемоника означает, что значениями как SUM, так и

ATOF(...) являются плавающие числа двойной точности.

    

·     79 -

    

Если функция ATOF не будет описана явно в обоих местах,

то в “C” предполагается, что она возвращает целое значение,

и вы получите бессмысленный ответ. Если сама ATOF и обраще-

ние к ней в MAIN имеют несовместимые типы и находятся в од-

ном и том же файле, то это будет обнаружено компилятором. Но

если ATOF была скомпилирована отдельно /что более вероятно/,

то это несоответствие не будет зафиксировано, так что ATOF

будет возвращать значения типа DOUBLE, с которым MAIN будет

обращаться, как с INT , что приведет к бессмысленным резуль-

татам. /Программа LINT вылавливает эту ошибку/.

Имея ATOF, мы, в принципе, могли бы с ее помощью напи-

сать ATOI (преобразование строки в INT):

ATOI(S)   /* CONVERT STRING S TO INTEGER */

CHAR S[];

  {

DOUBLE ATOF();

RETURN(ATOF(S));

  }

 

 

Обратите внимание на структуру описаний и оператор RETURN.

Значение выражения в

 

RETURN (выражение)

всегда преобразуется к типу функции перед выполнением самого

возвращения. Поэтому при появлении в операторе RETURN значе-

ние функции атоF, имеющее тип DOUBLE, автоматически преобра-

зуется в INT, поскольку функция ATOI возвращает INT. (Как

обсуждалось в главе 2, преобразование значения с плавающей

точкой к типу INT осуществляется посредством отбрасывания

дробной части).

Упражнение  4-2.

Расширьте ATOF таким образом, чтобы она могла работать с

числами вида

123.45е-6

где за числом с плавающей точкой может следовать 'E' и пока-

затель экспоненты, возможно со знаком.

 

4.3.   Еще об аргументах функций.

 

В главе 1 мы уже обсуждали тот факт , что аргументы фун-

кций передаются по значению, т.е. вызванная функция получает

свою временную копию каждого аргумента, а не его адрес. это

означает, что вызванная функция не может воздействовать на

исходный аргумент в вызывающей функции. Внутри функции каж-

дый аргумент по существу является локальной переменной, ко-

торая инициализируется тем значением, с которым к этой функ-

ции обратились.

    

·     80 -

    

Если в качестве аргумента функции выступает имя массива,

то передается адрес начала этого массива; сами элементы не

копируются. Функция может изменять элементы массива, исполь-

зуя индексацию и адрес начала. Таким образом, массив переда-

ется по ссылке. В главе 5 мы обсудим, как использование ука-

зателей позволяет функциям воздействовать на отличные от

массивов переменные в вызывающих функциях.

Между прочим, несуществует полностью удовлетворительного

способа написания переносимой функции с переменным числом

аргументов. Дело в том, что нет переносимого способа, с по-

мощью которого вызванная функция могла бы определить, сколь-

ко аргументов было фактически передано ей в данном обраще-

нии. Таким образом, вы, например, не можете написать дейст-

вительно переносимую функцию, которая будет вычислять макси-

мум от произвольного числа аргументов, как делают встроенные

функции MAX в фортране и PL/1.

Обычно со случаем переменного числа аргументов безопасно

иметь дело, если вызванная функция не использует аргументов,

которые ей на самом деле не были переданы, и если типы сог-

ласуются. Самая распространенная в языке “C” функция с пере-

менным числом - PRINTF . Она получает из первого аргумента

информацию, позволяющую определить количество остальных ар-

гументов и их типы. Функция PRINTF работает совершенно неп-

равильно, если вызывающая функция передает ей недостаточное

количество аргументов, или если их типы не согласуются с ти-

пами, указанными в первом аргументе. Эта функция не является

переносимой и должна модифицироваться при использовании в

различных условиях.

Если же типы аргументов известны, то конец списка аргу-

ментов можно отметить, используя какое-то соглашение; напри-

мер, считая, что некоторое специальное значение аргумента

(часто нуль) является признаком конца аргументов.

 

4.4.   Внешние переменные.

 

Программа на языке “C” состоит из набора внешних объек-

тов, которые являются либо переменными, либо функциями. Тер-

мин “внешний” используется главным образом в противопостав-

ление термину “внутренний”, которым описываются аргументы и

автоматические переменные, определенные внурти функций.

Внешние переменные определены вне какой-либо функции и, та-

ким образом, потенциально доступны для многих функций. Сами

функции всегда являются внешними, потому что правила языка

“C” не разрешают определять одни функции внутри других. По

умолчанию внешние переменные являются также и “глобальными”,

так что все ссылки на такую переменную, использующие одно и

то же имя (даже из функций, скомпилированных независимо),

будут ссылками на одно и то же. В этом смысле внешние пере-

менные аналогичны переменным COмMON в фортране и EXTERNAL в

PL/1. Позднее мы покажем, как определить внешние переменные

и функции таким образом, чтобы они были доступны не глобаль-

но, а только в пределах одного исходного файла.

    

·     81 -

    

В силу своей глобальной доступности внешние переменные

предоставляют другую, отличную от аргументов и возвращаемых

значений, возможность для обмена данными между функциями.

Если имя внешней переменной каким-либо образом описано, то

любая функция имеет доступ к этой переменной, ссылаясь к ней

по этому имени.

В случаях, когда связь между функциями осуществляется с

помощью большого числа переменных, внешние переменные оказы-

ваются более удобными и эффективными, чем использование

длинных списков аргументов. Как, однако, отмечалось в главе

1, это соображение следует использовать с определенной осто-

рожностью, так как оно может плохо отразиться на структуре

программ и приводить к программам с большим числом связей по

данным между функциями.

Вторая причина использования внешних переменных связана

с инициализацией. В частности, внешние массивы могут быть

инициализированы а автоматические нет. Мы рассмотрим вопрос

об инициализации в конце этой главы.

Третья причина использования внешних переменных обуслов-

лена их областью действия и временем существования. Автома-

тические переменные являются внутренними по отношению к фун-

кциям; они возникают при входе в функцию и исчезают при вы-

ходе из нее. Внешние переменные, напротив, существуют посто-

янно. Они не появляютя и не исчезают, так что могут сохра-

нять свои значения в период от одного обращения к функции до

другого. В силу этого, если две функции используют некоторые

общие данные, причем ни одна из них не обращается к другой ,

Страницы: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28