> А теперь прикинем, в скольки местах подобное может использоваться уже не внутри
> компилятора, а сознательно?Я не знаю в скольки, но подозреваю, что оно может быть использовалось внутри компилятора в прошлом, но давно уже не используется, потому что исполняемые данные -- это сакс, и отсовсюду выпиливают. Компилятор должен уметь работать на платформе, где нет исполняемых данных.
> Причем оно там двадцать с лишним лет назад
> было, когда никому не приходило в голову панически бояццца исполняемых данных.
Двадцать лет назад процессоры были иные. Сорок лет назад ещё более иные. 40 лет назад можно было писать самомодифицирующийся код, ради того, чтобы сэкономить три байта или два такта на итерацию цикла. 30 лет назад это было уже не очень модно, потому что три байта никого не волновали настолько, чтобы так злостным образом обходиться с читаемостью программы и возможностью долгосрочной поддержки кода. 20 лет назад это стало совсем не модно, потому что модификация кода сбрасывает кеш инструкций, слетает конвеер и дальше ты получаешь столько тормозов, что вся экономия пойдёт лесом. 20 лет назад самомодификации я встречал либо в малварях всяких, либо в развлечениях типа бегающего кода, который хрен поймаешь. Что-то типа такого:
mov %cs, %ax
mov %ax, %ds
mov %ax, %es
mov $run_away, %si
mov %si, %di
add $2, %di ; <- я не уверен насчёт 2, надо размер следующей инструкции вписать
run_away:
rep movsw ; AFAIR это два байта: один байт префикса и один байт для movs
Сегодня модифицирование кода -- это для каких-то очень специальных применений. Часть из них (типа exec(2), dlopen(3) или jit), вообще не очень страдают из-за того, что надо права доступа к странице менять между записью туда и исполнением. Оставшееся -- это либо какие-то костыли, типа запиливания в C вложенных функций без введения нового типа указателя на функцию, либо, возможно, гринтреды/корутины, но они в принципе реализуются и без этого, там достаточно стеки переключать и jmp'ать вовремя. В микроконтроллерах, потенциально, преимущества самомодификации могли бы перевесить недостатки, но те мк которые настолько ужаты в ресурсах, часто на гарвадской архитектуре и там изменения кода если и возможны программно, то сопряжены с кучей геморроя, в том числе и схемотехнического.
> Это сейчас в два клика в поиске можно узнать, зачем оно было.
Чтобы не передавать в функционал _два_ указателя -- один на код, второй на данные для кода. Единственный бонус, который получить без вложенной функции не удастся. Очень помогает в ситуации, когда функционал написан без учёта ограничений C и принимает указатель на функцию, но не принимает void *data, который бы он, не заглядывая внутрь, передавал бы в каждый вызов переданной функции. Я как-то о такое споткнулся, исследуя разные API из glibc для переключения стеков, и один из них был как раз такой убогий. Кончилось тем, что я использовал API из libpth.so, там был самый вменяемый. Хотя, если по хорошему, даже не надо так далеко ходить за примером, можно сделать man 3 qsort, и увидеть там пример функционала, который принимает функцию, не позволяя прокинуть в неё состояние.