Нет, здесь могут быть две вещи - вы не указываете ОС, поэтому я собираюсь дать общий ответ.
Во-первых, исполняемый файл редко находится в окончательном формате. Для упрощения компиляция превращает исходный код в объектные файлы, а компоновка объединяет объектные файлы в исполняемый файл.
Но исполняемый файл должен быть загружен в память, и на этом этапе может быть сделано еще больше изменений. Одна из этих модификаций может заключаться в исправлении ссылок на память в исполняемом файле, чтобы указывать на память, которая была загружена в разных местах.
Это может быть достигнуто с помощью исполняемого файла, содержащего список адресов внутри себя, которые необходимо исправить во время выполнения.
Существует также разрыв между виртуальной памятью и физической памятью во многих современных операционных системах.
Когда ваш процесс запускается, вы получаете свое собственное (4G для Windows 32bit, я полагаю) адресное пространство, в которое загружается ваш процесс. Адреса в этом адресном пространстве мало связаны с вашими фактическими адресами физической памяти, и преобразование между ними осуществляется блоком управления памятью (MMU).
Фактически, ваш процесс может летать по всему физическому адресному пространству, когда он выгружается и разворачивается. Однако виртуальные адреса не изменятся.