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



Pdf көрінісі
бет84/256
Дата11.07.2022
өлшемі2,87 Mb.
#37591
1   ...   80   81   82   83   84   85   86   87   ...   256
Байланысты:
Бьерн Страуструп. Язык программирования С . М Бином, 2011

4.3.1 Единственный заголовочный файл 
Проще всего разбить программу на несколько файлов следующим образом: поместить определения 


Бьерн Страуструп.
Язык программирования С++ 
 
101 
всех функций и данных в некоторое число входных файлов, а все типы, необходимые для связи между 
ними, описать в единственном заголовочном файле. Все входные файлы будут включать заголовочный 
файл. Программу калькулятора можно разбить на четыре входных файла .c: lex.c, syn.c, table.c и main.c. 
Заголовочный файл dc.h будет содержать описания каждого имени, которое используется более чем в 
одном .c файле: 
// dc.h: общее описание для калькулятора 
#include  
enum token_value { 
NAME, NUMBER, END, 
PLUS='+', MINUS='-', MUL='*', DIV='/', 
PRINT=';', ASSIGN='=', LP='(', RP=')' 
}; 
extern int no_of_errors; 
extern double error(const char* s); 
extern token_value get_token(); 
extern token_value curr_tok; 
extern double number_value; 
extern char name_string[256]; 
extern double expr(); 
extern double term(); 
extern double prim(); 
struct name { 
char* 
string; 
name* 
next; 
double 
value; 
}; 
extern name* look(const char* p, int ins = 0); 
inline name* insert(const char* s) { return look(s,1); } 
Если не приводить сами операторы, lex.c должен иметь такой вид: 
// lex.c: ввод и лексический анализ 
#include "dc.h" 
#include  
token_value curr_tok; 
double number_value; 
char name_string[256]; 
token_value get_token() { /* ... */ } 
Используя составленный заголовочный файл, мы добьемся, что описание каждого объекта, введенного 
пользователем, обязательно окажется в том файле, где этот объект определяется. Действительно, при 
обработке файла lex.c транслятор столкнется с описаниями 
extern token_value get_token(); 
// ... 
token_value get_token() { /* ... */ } 
Это позволит транслятору обнаружить любое расхождение в типах, указанных при описании данного 
имени. Например, если бы функция get_token() была описана с типом token_value, но определена с 
типом int, трансляция файла lex.c выявила бы ошибку: несоответствие типа. 
Файл syn.c может иметь такой вид: 
// syn.c: синтаксический анализ и вычисления 
#include "dc.h" 
double prim() { /* ... */ } 
double term() { /* ... */ } 
double expr() { /* ... */ } 
Файл table.c может иметь такой вид: 


Бьерн Страуструп.
Язык программирования С++ 
 
102 
// table.c: таблица имен и функция поиска 
#include "dc.h" 
extern char* strcmp(const char*, const char*); 
extern char* strcpy(char*, const char*); 
extern int strlen(const char*); 
const int TBLSZ = 23; 
name* table[TBLSZ]; 
name* look(char* p, int ins) { /* ... */ } 
Отметим, что раз строковые функции описаны в самом файле table.c, транслятор не может проверить 
согласованность этих описаний по типам. Всегда лучше включить соответствующий заголовочный 
файл, чем описывать в файле .c некоторое имя как extern. Это может привести к включению "слишком 
многого", но такое включение нестрашно, поскольку не влияет на скорость выполнения программы и ее 
размер, а программисту позволяет сэкономить время. Допустим, функция strlen() снова описывается в 
приведенном ниже файле main.c. Это только лишний ввод символов и потенциальный источник ошибок, 
т.к. транслятор не сможет обнаружить расхождения в двух описаниях strlen() (впрочем, это может 
сделать редактор связей). Такой проблемы не возникло бы, если бы в файле dc.h содержались все 
описания extern, как первоначально и предполагалось. Подобная небрежность присутствует в нашем 
примере, поскольку она типична для программ на С. Она очень естественна для программиста, но часто 
приводит к ошибкам и таким программам, которые трудно сопровождать. Итак, предупреждение 
сделано! 
Наконец, приведем файл main.c: 
// main.c: инициализация, основной цикл, обработка ошибок 
#include "dc.h" 
double error(char* s) { /* ... */ } 
extern int strlen(const char*); 
int main(int argc, char* argv[]) { /* ... */ } 
В одном важном случае заголовочные файлы вызывают большое неудобство. С помощью серии 
заголовочных файлов и стандартной библиотеки расширяют возможности языка, вводя множество 
типов (как общих, так и рассчитанных на конкретные приложения; см. главы 5-9). В таком случае текст 
каждой единицы трансляции может начинаться тысячами строк заголовочных файлов. Содержимое 
заголовочных файлов библиотеки, как правило, стабильно и меняется редко. Здесь очень пригодился 
бы претранслятор, который обрабатывает его. По сути, нужен язык специального назначения со своим 
транслятором. Но устоявшихся методов построения такого претранслятора пока нет. 


Достарыңызбен бөлісу:
1   ...   80   81   82   83   84   85   86   87   ...   256




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

    Басты бет