Ответ на ваш первый вопрос можно найти в §6.2.2 стандарта C:
4 Для идентификатора, объявленного со спецификатором класса хранения extern
в области видимости, в которой видно предыдущее объявление этого идентификатора,
если в предыдущей декларации указана внутренняя или внешняя связь,
связь идентификатора при последующем объявлении такая же, как
связь указана в предыдущей декларации. Если нет предварительной декларации
видимый, или если в предыдущем объявлении не указана связь, то
Идентификатор имеет внешнюю связь.
Таким образом, связь a
является внутренней.
По вашему второму вопросу второе предложение следующего за ним абзаца уместно:
5 Если объявление идентификатора для функции не имеет
спецификатор класса хранилища, его связь определяется точно так же, как если бы он
были объявлены со спецификатором класса хранения extern
. Если
объявление идентификатора для объекта имеет область видимости файла и не имеет
спецификатор класса хранилища, его связь внешняя.
Поскольку a
является объектом, а не функцией, объявление int a;
без спецификатора класса хранения дает a
внешнюю связь. Затем в этом же разделе говорится:
7 Если в пределах единицы перевода появляется один и тот же идентификатор с обоими
внутренняя и внешняя связь, поведение не определено.
Поскольку во втором примере a
отображается как с внутренней, так и с внешней связью, этот параграф запускается. Одним (особенно полезным) проявлением неопределенного поведения является ошибка, которую генерирует ваш компилятор.
Все ваши примеры можно понять по следующим правилам:
int a;
всегда объявляет a
с внешней связью;
static int a;
всегда объявляет a
с внутренней связью;
extern int a;
объявляет a
с какой-либо связью, которая у него уже была, или с внешней связью, если у нее ее нет;
- Два объявления
a
в одной и той же области с разными связями дают неопределенное поведение.