Однако, если на внешнюю переменную нужно сослаться до того, как она определена, или если она определена в другом файле, то ее объявление должно быть помечено словом extern.
Важно отличать объявление внешней переменной от ее определения . Объявление объявляет свойства переменной (прежде всего ее тип), а определение, кроме того, приводит к выделению для нее памяти. Если строки
int sp;
double val[MAXVAL];
расположены вне всех функций, то они определяют внешние переменные sp и val , т. e. отводят для них память, и, кроме того, служат объявлениями для остальной части исходного файла. А вот строки
extern int sp;
extern double val[];
объявляют для оставшейся части файла, что sp - переменная типа int , а val - массив типа double (размер которого определен где-то в другом месте); при этом ни переменная, ни массив не создаются, и память им не отводится.
На всю совокупность файлов, из которых состоит исходная программа, для каждой внешней переменной должно быть одно-единственное определение ; другие файлы, чтобы получить доступ к внешней переменной, должны иметь в себе объявление extern . (Впрочем, объявление extern можно поместить и в файл, в котором содержится определение.) В определениях массивов необходимо указывать их размеры, что в объявлениях extern не обязательно. Инициализировать внешнюю переменную можно только в определении. Хотя вряд ли стоит организовывать нашу программу таким образом, но мы определим push и pop в одном файле, а val и sp - в другом, где их и инициализируем. При этом для установления связей понадобятся такие определения и объявления:
В файле 1 :
extern int sp;
extern double val[];
void push(double f) {…}
double pop(void) {…}
В файле2 :
int sp = 0;
double val[MAXVAL];
Поскольку объявления extern находятся в начале файла1 и вне определений функций, их действие распространяется на все функции, причем одного набора объявлений достаточно для всего файла1 . Та же организация extern -объявлений необходима и в случае, когда программа состоит из одного файла, но определения sp и val расположены после их использования.
Теперь представим себе, что компоненты программы-калькулятора имеют существенно большие размеры, и зададимся вопросом, как в этом случае распределить их по нескольким файлам. Программу main поместим в файл, который мы назовем main.с ; push , pop и их переменные расположим во втором файле, stack.с ; a getop - в третьем, getop.c . Наконец, getch и ungetch разместим в четвертом файле getch.с ; мы отделили их от остальных функций, поскольку в реальной программе они будут получены из заранее скомпилированной библиотеки.
Существует еще один момент, о котором следует предупредить читателя, - определения и объявления совместно используются несколькими файлами. Мы бы хотели, насколько это возможно, централизовать эти объявления и определения так, чтобы для них существовала только одна копия. Тогда программу в процессе ее развития будет легче и исправлять, и поддерживать в нужном состоянии. Для этого общую информацию расположим в заголовочном файле calc.h , который будем по мере необходимости включать в другие файлы. (Строка #include описывается в параграфе 4.11) В результате получим программу, файловая структура которой показана ниже:
main.с:
#include ‹stdio.h›
#include ‹stdlib.h›
#include "calc.h"
#define MAXOP 100
main() {
…
}
calc.h:
#define NUMBER '0'
void push(double);
double pop(void);
int getop(char[]);
int getch(void);
void ungetch(int);
getop.c:
#include ‹stdio.h›
#include ‹ctype.h›
#include "calc.h"
getop (){
…
}
getch.c:
#include ‹stdio.h›
#define BUFSIZE 100
char buf[BUFSIZE];
intbufp = 0;
int getch(void) {
…
}
void ungetch(int) {
…
}
stack.с:
#include ‹stdio.h›
#include "calc.h"
#define MAXVAL 100
int sp = 0;
double val[MAXVAL];
void push(double) {
…
}
double pop(void) {
…
}
Неизбежен компромисс между стремлением, чтобы каждый файл владел только той информацией, которая ему необходима для работы, и тем, что на практике иметь дело с большим количеством заголовочных файлов довольно трудно. Для программ, не превышающих некоторого среднего размера, вероятно, лучше всего иметь один заголовочный файл, в котором собраны вместе все объекты, каждый из которых используется в двух различных файлах; так мы здесь и поступили. Для программ больших размеров потребуется более сложная организация с большим числом заголовочных файлов.
Читать дальше