R.7.1 Спецификации
В описании можно использовать следующие спецификации:
спецификация-описания:
спецификация-класса-памяти
спецификация-типа
спецификация-fct
спецификация-шаблона-типа
friend
typedef
спецификации-описания:
спецификации-описания opt спецификация-описания
Самая длинная последовательность конструкций спецификация-описания, которая, возможно, является именем типа, образует в описании конструкцию спецификации-описания. Последовательность должна быть согласованной, что объясняется ниже. Например,
typedef char* Pc;
static Pc; // ошибка: нет имени
Здесь описание static Pc является незаконным, поскольку не указано никакого имени статической переменной типа Pc. Чтобы иметь переменную типа int с именем Pc, необходимо задать спецификацию-типа int, чтобы показать, что (пере)определяется имя Pc из typedef, а не просто Pc является одним из элементов последовательности конструкций спецификация-описания, например,
void f(const Pc); // void f(char* const)
void g(const int Pc); // void g(const int)
Укажем, что поскольку signed, unsigned, long и short по умолчанию трактуются как int, конструкция имя-typedef, которая появляется после одной из перечисленных спецификаций типа, должна задавать (пере)определяемое имя, например,
void h(unsigned Pc); // void h(unsigned int)
void k(unsigned int Pc); // void k(unsigned int)
R.7.1.1 Спецификации класса памяти
Спецификации класса памяти могут быть такие:
спецификация-класса-памяти:
auto
register
static
extern
Спецификации auto и register могут применяться только для имен объектов, которые описаны в блоке (§R.6.3), или для формальных параметров (§R.8.3). Почти всегда спецификация auto избыточна и используется не часто, так, auto используется, чтобы явно отделить оператор-описание от оператора-выражения (§R.6.2).
Описание register является описанием auto, которое подсказывает транслятору, что описываемые переменные будут использоваться достаточно интенсивно. Подсказка может быть проигнорирована, и во многих реализациях она игнорируется в том случае, когда берется адрес переменной.
Описание объекта считается определением, если только оно не содержит спецификации extern и инициализации (§R.3.1).
Определение приводит к выделению памяти соответствующего размера и выполнению соответствующей инициализации (§R.8.4).
Спецификации static и extern могут применяться только к именам объектов или функций или к анонимным объединениям. Внутри блока недопустимы описания функций со спецификацией static или формальных параметров со спецификацией static или extern. Статические члены класса описываются в §R.9.4. Спецификация extern недопустима для членов класса.
Имя со спецификацией static подлежит внутреннему связыванию. Объекты, описанные как const, подлежат внутреннему связыванию, если только они не были описаны с внешней связью. Имя со спецификацией extern подлежит внешнему связыванию, если только ранее оно не было описано с внутренней связью. Имя с файловой областью видимости и без спецификации-класса-памяти подлежит внешнему связыванию, если только ранее оно не было описано с внутренней связью или со спецификацией const. В смысле связывания для функций, не являющихся членами, спецификация inline эквивалентна static (§R.3.3). Для одного имени все его спецификации, определяющие связывание, должны быть согласованы. Например,
static char* f(); // f() имеет внутреннее связывание
char* f() // f() все еще внутреннее
{/*… */}
char* g(); // g() имеет внешнее связывание
static char* g() // ошибка: противоречие в связывании
{/*… */}
static int a; // `a' имеет внутреннее связывание
int a; // ошибка: второе определение
static int b; // `b' имеет внутреннее связывание
extern int b; // `b' все еще внутреннее
int c; // `c' имеет внешнее связывание
static int c; // ошибка: противоречие в связывании
extern int d; // `d' имеет внешнее связывание
static int d; // ошибка: противоречие в связывании
Имя неопределенного класса можно использовать в описании extern. Однако, такое описание нельзя использовать прежде, чем класс будет определен, например,
struct S;
extern S a;
extern S f();
extern void g(S);
void h()
{