Бьерн Страуструп. Язык программирования С++ Второе дополненное издание



Pdf көрінісі
бет67/256
Дата11.07.2022
өлшемі2,87 Mb.
#37591
1   ...   63   64   65   66   67   68   69   70   ...   256
вызывает expr(). Этот цикл необходимо как-то разорвать, для чего вполне подходит заданное до 
определения prim() описание: 
double expr(); // это описание необходимо 
Функция term() справляется с умножением и делением аналогично тому, как функция expr() со 
сложением и вычитанием: 
double term() 
// умножает и складывает 

double left = prim(); 
for(;;) 
switch(curr_tok) { 
case 
MUL: 
get_token(); 
// 
случай '*' 
left 
*= 
prim(); 
break; 
case 
DIV: 
get_token(); 
// 
случай '/' 
double 


prim(); 
if (d == 0) return error("
деление на 0"); 
left 
/= 
d; 
break; 
default: 
return 
left; 


Проверка отсутствия деления на нуль необходима, поскольку результат деления на нуль неопределен 
и, как правило, приводит к катастрофе. 
Функция error() будет рассмотрена позже. Переменная d появляется в программе там, где она 
действительно нужна, и сразу же инициализируется. Во многих языках описание может находиться 
только в начале блока. Но такое ограничение может искажать естественную структуру программы и 
способствовать появлению ошибок. Чаще всего не инициализированные локальные переменные 
свидетельствуют о плохом стиле программирования. Исключение составляют те переменные, которые 
инициализируются операторами ввода, и переменные типа массива или структуры, для которых нет 
традиционной инициализации с помощью одиночных присваиваний. Следует напомнить, что = является 


Бьерн Страуструп.
Язык программирования С++ 
 
73 
операцией присваивания, тогда как == есть операция сравнения. 
Функция prim, обрабатывающая первичное, во многом похожа на функции expr и term(). Но раз мы 
дошли до низа в иерархии вызовов, то в ней кое-что придется сделать. Цикл для нее не нужен: 
double number_value; 
char name_string[256]; 
double prim() // обрабатывает первичное 

switch (curr_tok) { 
case NUMBER: // константа с плавающей точкой 
get_token(); 
return 
number_value; 
case 
NAME: 
if (get_token() == ASSIGN) { 
name* 


insert(name_string); 
get_token(); 
n->value 

expr(); 
return 
n->value; 

return 
look(name_string)->value; 
case MINUS: // унарный минус 
get_token(); 
return 
-prim(); 
case 
LP: 
get_token(); 
double 


expr(); 
if (curr_tok != RP) return error("
требуется )"); 
get_token(); 
return 
e; 
case 
END: 
return 
1; 
default: 
return error("требуется первичное"); 


Когда появляется NUMBER (то есть константа с плавающей точкой), возвращается ее значение. 
Функция ввода get_token() помещает значение константы в глобальную переменную number_value. Если 
в программе используются глобальные переменные, то часто это указывает на то, что структура не до 
конца проработана, и поэтому требуется некоторая оптимизация. Именно так обстоит дело в данном 
случае. В идеале лексема должна состоять из двух частей: значения, определяющего вид лексемы (в 
данной программе это token_value), и (если необходимо) собственно значения лексемы. Здесь же 
имеется только одна простая переменная curr_tok, поэтому для хранения последнего прочитанного 
значения NUMBER требуется глобальная переменная number_value. Такое решение проходит потому, 
что калькулятор во всех вычислениях вначале выбирает одно число, а затем считывает другое из 
входного потока. В качестве упражнения предлагается избавиться от этой излишней глобальной 
переменной ($$3.5 [15]). 
Если последнее значение NUMBER хранится в глобальной переменной number_value, то строковое 
представление последнего значения NAME хранится в name_string. Перед тем, как что-либо делать с 
именем, калькулятор должен заглянуть вперед, чтобы выяснить, будет ли ему присваиваться значение, 
или же будет только использоваться существующее его значение. В обоих случаях надо обратиться к 
таблице имен. Эта таблица рассматривается в $$3.1.3; а здесь достаточно только знать, что она 
состоит из записей, имеющих вид: 
struct name { 
char* 
string; 
name* 
next; 
double 
value; 
}; 


Бьерн Страуструп.
Язык программирования С++ 
 
74 
Член next используется только служебными функциями, работающими с таблицей: 
name* look(const char*); 
name* insert(const char*); 
Обе функции возвращают указатель на ту запись name, которая соответствует их параметру-строке. 
Функция look() "ругается", если имя не было занесено в таблицу. Это означает, что в калькуляторе 
можно использовать имя без предварительного описания, но в первый раз оно может появиться только 
в левой части присваивания. 


Достарыңызбен бөлісу:
1   ...   63   64   65   66   67   68   69   70   ...   256




©emirsaba.org 2024
әкімшілігінің қараңыз

    Басты бет