Почему возможно объявление глобальной переменной
Потому что объявление всего лишь говорит о том, что имя существует и имеет такой-то тип. Оно не вызывает выделения памяти или каких-либо других действий для которых нужно знать как устроен тип связанный с идентификатором.
В твоём втором примере
struct interval b
-- это определение переменной, выделяющее для неё место на стеке. Но его можно переписать, чтобы b тоже стало объявлением, например так:
int main ()
{
extern struct interval b;
struct interval
{
int first;
int second;
};
}
Возвращаясь обратно к первому примеру,
struct interval b;
-- это tentative definition с внешней линковкой. Стандарт (С99) говорит о нём следующее (6.9.2:2):
A declaration of an identifier for an object that has file scope without an initializer,
and without a storage-class specifier or with the storage-class specifier static, constitutes
a tentative definition. If a translation unit contains one or more tentative definitions for
an identifier, and the translation unit contains no external definition for that identifier,
then the behavior is exactly as if the translation unit contains a file scope declaration of
that identifier, with the composite type as of the end of the translation unit, with an
initializer equal to 0.
Самое важное здесь -- as of the end of the translation unit, т.е. тип объекта из tentative definition таки должен быть определён, но не до появления этого tentative definition, а до конца единицы трансляции (или раньше, если вдруг встретится определение с инициализацией этого объекта).