По моему, на русском чаще используют термин объявление, а не декларация. По крайней мере мне так привычнее.
Объявление в одном из возможных переводов на английский звучит как declaration.
https://en.cppreference.com/w/c/language/declarations
https://en.cppreference.com/w/c/language/type
Вы не правильно сопоставляете инициализацию и декларацию (объявление). Сопоставлять надо объявление и определение.
Инициализация - всего лишь присвоение переменной некоторого начального значения. Она может быть при определении переменной или потом - не важно. Важно то, что перед инициализацией память для переменной должна быть выделена.
Память выделяется когда переменная определяется.
При объявлении память не выделяется, а только описывается (объявляется) тип. Тип включает в себя информацию о размере, выравнивании, возможных операциях, что-еще. Встроенные типы (int и т.п.) уже объявлены заранее и известны компилятору.
Может быть предварительное объявление. Предварительных объявлений может быть сколько угодно много, если они не противоречат друг другу. Настоящее объявление может быть только одно.
Понять различие между объявлением и определением на простом встроенном типе (int) довольно сложно, потому что сам тип уже известен, его не нужно объявлять. Для примера буду использовать структуру.
Кроме того важно где конкретно в коде программы определена переменная - глобально (по отношению к файлу исходного кода) или локально (в функции).
struct s; // предварительное объявление
struct s { int v; }; // объявление структуры
strcut s s1; // определение глобальной структуры
struct s s2 = { 0 }; // определение глобальной инициализированной структуры
int main()
{
struct s s3; // определение локальной структуры
}
Предварительное объявление - говорит о том, что где-то есть полное объявление. Тип объявленный только предварительным объявлением - не завершенный (не полный). Нельзя определить переменную по неполному типа. НО можно определить указатель на неполный тип. Но обращаться по указателю на неполный тип нельзя. Но присвоить адрес можно :-) Это можно использовать в своих интересах.
После полного объявления типа уже можно определять переменные этого типа.
s1 - глобальная не инициализированная структура. Память под нее выделяется в секции bss исполняемого файла. В исполняемом файле обычно не выделяется память непосредственно, но сохраняется размер секции. Загрузчик ОС читает размер секции из файла и выделяет память нужного размера. Глобальные не инициализированные переменные на самом деле инициализируются неявно нулем.
s2 - глобальная инициализированная структура. Память под нее выделяется в секции data исполняемого файла. Тут уже не достаточно сохранить информацию о секции, т.к. есть начальные значения. Поэтому такие переменные непосредственно присутствуют в исполняемом файле (точнее выделена память под них и присвоено начальное значение). Есть еще секция rodata - для константных данных.
s3 - локальная не инициализированная переменная. Память под нее выделяется на стеке. Не зависимо от того инициализирована переменная или нет, память выделена, а значит в этой памяти уже что-то лежит - не бывает "пустой" памяти. В случае не инициализированной переменной на стеке, в памяти лежит мусор, который остался тут от прошлых действий.