Бьерн Страуструп.
Язык программирования С++
98
int a = 1;
int f() { /* какие-то операторы */ }
// file2.c
extern int a;
int f();
void g() { a = f(); }
В функции g() используются те самые a и f(), которые определены в файле file1.c. Служебное слово
extern показывает, что описание a в файле file2.c является только описанием, но не определением.
Если бы присутствовала инициализация a, то extern просто проигнорировалось бы, поскольку описание
с инициализацией всегда считается определением. Любой объект в
программе может определяться
только один раз. Описываться же он может неоднократно, но все описания должны быть согласованы
по типу. Например:
// file1.c:
int a = 1;
int b = 1;
extern int c;
// file2.c:
int a;
extern double b;
extern int c;
Здесь содержится три ошибки: переменная a определена дважды ("int a;" - это определение,
означающее "int a=0;"); b описано дважды, причем с
разными типами; c описано дважды, но
неопределено. Такие ошибки (ошибки связывания) транслятор, который обрабатывает файлы по
отдельности, обнаружить не может, но большая их часть обнаруживается редактором связей.
Следующая программа допустима в С, но не в С++:
// file1.c:
int a;
int f() { return a; }
// file2.c:
int a;
int g() { return f(); }
Во-первых, ошибкой является вызов f() в file2.c, поскольку в этом файле f() не описана. Во-вторых,
файлы программы не могут быть правильно связаны, поскольку a определено дважды.
Если имя описано как static, оно становится локальном в этом файле. Например:
// file1.c:
static int a = 6;
static int f() { /* ... */ }
// file2.c:
static int a = 7;
static int f() { /* ... */ }
Приведенная программа правильна, поскольку a и f определены как статические. В каждом файле своя
переменная a и функция f().
Если переменные и
функции в данной части программы описаны как static, то в этой части программы
проще разобраться, поскольку не нужно заглядывать в другие части. Описывать
функции как
статические полезно еще и по той причине, что транслятору предоставляется возможность создать
более простой вариант операции вызова функции. Если имя объекта или функции локально в данном
файле, то говорят, что объект подлежит внутреннему связыванию. Обратно, если имя объекта или
функции нелокально в данном файле, то он подлежит внешнему связыванию.
Обычно говорят, что имена типов, т.е. классов и перечислений, не подлежат связыванию. Имена
Бьерн Страуструп.
Язык программирования С++
99
глобальных классов и перечислений должны быть уникальными во всей программе и иметь
единственное определение. Поэтому, если есть два даже идентичных определения одного класса, это -
все равно ошибка:
// file1.c:
struct S { int a; char b; };
extern void f(S*);
// file2.c:
struct S { int a; char b; };
void f(S* p) { /* ... */ }
Но будьте осторожны: опознать идентичность двух описаний класса не в состоянии большинство
систем программирования С++. Такое дублирование может вызвать довольно тонкие ошибки (ведь
классы в разных файлах будут считаться различными).
Глобальные
функции-подстановки подлежат внутреннему связыванию, и то же по умолчанию
справедливо для констант. Синонимы типов, т.е. имена typedef, локальны в своем файле, поэтому
описания в двух данных ниже файлах не противоречат друг другу:
// file1.c:
typedef int T;
const int a = 7;
inline T f(int i) { return i+a; }
// file2.c:
typedef void T;
const int a = 8;
inline T f(double d) { cout<
Константа может получить внешнее связывание только с помощью явного описания:
// file3.c:
extern const int a;
const int a = 77;
// file4.c:
extern const int a;
void g() { cout<
В этом примере g() напечатает 77.
Достарыңызбен бөлісу: