Почему при объявлении переменной в любом языке программирования резервируется весь размер памяти отведённый под тип данных, а не только тот, который данные внутри в действительности занимают
Тут сразу два вопроса:
1. Почему вы решили резервируется весь размер?
2. Как определить сколько данные действительно занимают?
По первому пункту - есть динамические типы данных вроде списков, векторов, деревьев и т.д. Они обычно занимают столько места, сколько нужно + накладные расходы.
По второму - вот создал я структуру из целых чисел. Мне нужно где-то еще завести битовую маску для того чтобы знать какие поля уже используются. И при каждом обращении к полю, нужно сначала вычитать маску, рассчитать смещение от начала структуры и только потом прочитать поле. А если я пишу в поле, которое раньше не использовалось, то мне нужно выделить новый кусок памяти, расположить там старые поля и только потом добавить новые. И потом обновить все указатели на эту структуру. Представляете сколько лишней работы для процессора?
То же самое для примитивных типов - вместо фиксированного числа бит я должен отдельно хранить длину числа и потом само число. При попытке записать в переменную бОльшее число, я буду вынужден искать новый кусок памяти. Кстати, на самом деле такая штука реализована, называется BigInt. Но область применения у нее довольно узкая....
Вообще, попытайтесь поставить себя на место разработчика компилятора (или процессора) и прикинуть как бы вы хранили в памяти такие вот данные переменной длины. Как бы вы решали вопросы с увеличением (и уменьшением) размеров и т.д. Попробуйте просто нарисовать на бумажке участок памяти и поиграйте с операциями записи и чтения.
Кстати, на самом деле память не всегда выделяется под переменную. Современные компиляторы достаточно умны для того что бы держать большинство локальных переменных в регистрах, вообще не трогая стек и/или кучу.