Ведь можно то же самое делать при помощи команд.
При помощи инструкций кода всё делается ВО ВРЕМЯ РАБОТЫ программы в МК. А препроцессинг происходит во время генерации самого кода. Например, директива определения значения константы через ее имя используется для читабельности кода и удобства изменения значения константы при ее многократном использовании в коде. Достаточно изменить #define и препроцессор автоматически подставит вместо символического имени реальное значение во всех местах использования этого символического имени.
Так же директивы препроцессора позволяют управлять самим процессом трансляции текста в бинарный код, создавая возможности для условной трансляции, изменять расположение в памяти программ участков кода, размещать дефолтные (исходные при программировании МК) значения данных в ЕЕПРОМе, выделять память для переменных ну и так далее.
Вы же не будете писать непосредственные адреса переменных в ОЗУ. Вы используете символические имена, а препроцессор выделит память под эти имена и подставит значение адреса в код.