Евгений Степанищев

Этот сайт — моя персональная записная книжка. Интересна мне, по большей части, история, своя жизнь и немного программирование.

Немного о сложностях написания ассемблера на m4

Не думаю, что я много буду использовать язык m4 в будущем, если вообще буду, поэтому хотел дать себе и, возможно, кому-то из читателей чуть больше контекста о том как писался на этом макропроцессоре ассемблер процессора 8080A. А то со временем всё забудется.

Для этого я чуть-чуть больше расскажу о m4 и ассемблере 8080A.

Ассемблер 8080А довольно простой. Будучи школьником, я его выучил по комментариям к кускам кода, которые печатались в журнале «Радио» — другой литературы в моём доступе не было. В нём всего несколько команд, большинство из них имеют один или два параметра.

Посмотрим, например, на команды ADD B и ADD C. Первая эквивалентна A += B на других языках, вторая — A += C. Буквами обозначаются регистры, для простоты можно считать их именованными переменными. Их мало, всего несколько штук и они имеют свои особенности. Например, в этих двух командах мы видим особенность регистра A — к нему можно прибавлять значения других регистров.

Команды ADD B и ADD C для исполнения компьютером переводятся в числа — в машинные коды. Обычно их записывают в шестандцатеричном виде, я тоже так буду делать. Данные команды сложения кодируются как числа 80 и 81 соответственно.

Теперь немного про m4. Я писал выше, что m4 — макропроцессор, если упрощать, язык макропроцессора позволяет указать как одни «слова» заменить на другие. «Слова» при этом должны начинаться с буквы или подчёркивания и содержать только буквы, цифры или знак подчёркивания. Это ограничение очень важное. Не будь его, я бы задал по одному правилу на каждую команду: ADD B → 80, ADD C → 81 и был таков.

К сожалению, так это не работает. Из-за этого ограничения, когда макропроцессор встречает ADD, он ещё не знает что будет дальше, поэтому не может выбрать на какой код заменить это слово, а когда дальше он видит B или C, ему надо помнить, что было до этого, так как с этими регистрами работает много команд. Другими словами, макропроцессору нужно запоминать контекст.

В руководствах по m4 указывается, что этот макропроцессор контекст учитывать не умеет. Так и есть, встроенных конструкций, предназначенных именно для этого нет, но можно выкрутиться менее специфичными. Дело в том, что m4 может проверять как выглядят его текущие правила замен одного слова на другое.

Поэтому мы можем завести специальное правило, куда будем записывать какая команда нам встретилась, а позже, встретив имя регистра, будет проверять чему равно наше специальное правило и производить замену, основываясь на этом.

При этом m4 не делает различий между, так сказать, текстом программы и обрабатываемым текстом, для него это одно и то же. То есть макропроцессор делает возможным метапрограммирование — позволяет менять программе свой собственный текст.

Вот как это выглядит в коде:

dnl заменяем строку ADD на определение макроса, сама строка ADD при этом пропадёт
define(`ADD', `define(`__cmd', `__s_ADD')')dnl

dnl когда встречаем B, смотрим чему равно определение макроса __cmd
define(`B', `
ifelse(
    defn(`__cmd'), `__s_ADC', 88,
    defn(`__cmd'), `__s_ADD', 80,
    …тут остальные команды…
    `')
')dnl

dnl когда встречаем C, смотрим чему равно определение макроса __cmd
define(`C', `
ifelse(
    defn(`__cmd'), `__s_ADC', 89,
    defn(`__cmd'), `__s_ADD', 81,
    …тут остальные команды…
    `')
')dnl

Чтобы дойти до этого трюка, пришлось поломать голову, но тем интереснее. Возможно он где-то уже описан, но искать информацию по m4 очень тяжело — поисковые машины охотно путают его с одноимённым процессором, а нейросети пишут какую-то пургу, не имеющую никакого отношения к реальности.

Аллергия на пчёл

Эх, похоже у меня всё-таки есть аллергия на пчёл. Утром на месте укуса стала видна чрезмерная реакция иммунной системы — покраснение сантиметров десять в диаметре и солидная припухлость.

Снял место укуса тепловизором в смартфоне на память.

Интересно, кстати, сравнить свои детские ощущения от укуса со взрослыми. До этого меня кусал только шмель в детском саду, ощущения были очень сильными и длились долго, как и последствия — палец в месте укуса опух и не сгибался несколько дней. Сейчас же было ощущение ожога на несколько секунд и только. Правда я не знаю насколько корректно сравнивать укус пчелы и шмеля, может быть яд у них разный по силе.

Открыли сезон с другом

С прошлой осени никуда не выезжал, тем более с металлоискателем. Растерял всю форму, да ещё и в последние месяцы ленился и не занимался спортом. Сегодня с другом съездили в сторону Болгара — навестили его отца и помахали металлоискателем над грядками его друзей и знакомых.

Надо сказать, открытие сезона далось с трудом — уехали утром, а вернулись под ночь, теперь у меня отваливается рука. Металлоискатель с непривычки кажется пудовым, плюс в место, куда приходится его ремень, меня укусила пчела.

Как раз в тот момент, когда мы подъезжали, папа друга, он разводит пчёл, разозлил один из ульев. А я, на беду, выйдя из машины, начал мазался мазью, чтобы не обгореть.

Пчеле и так настроение испортили с самого утра, да ещё я тут стою, невкусно воняю мазью от солнца. Тут у кого угодно нервы сдадут.

Положительный момент тоже есть — аллергии на пчёл у меня, по всей видимости, нет (увы, есть). Правда, это не факт, — я быстро, только почувствовав укол, на полном автомате вырвал жало из места укуса — читал когда-то, что так надо сделать, чтобы меньше получить яда. Так что может аллергия и есть, просто на бо́льшую дозу. Проверять я не стал, — когда начали атаковать остальные пчёлы, убежал подальше.

Погода была шикарная, отлично провели время, есть интересные находки и новые знакомства. В качестве одного из пунктов развлекательной программы обошли школу, где когда-то учился мой друг и забрались на колокольню заброшенной церкви по остаткам лестницы. Последнее было непросто, надо худеть, мда…

Вид из окна колокольни церкви Казанской иконы Божией Матери в Красной Слободе

Ассемблер процессора i8080A на m4

Сегодня опять сычевал почти до полуночи — вечером попался кусок кода на m4 и я, пробормотав вечное «когда-нибудь надо бы его выучить», вдруг подумал — а почему бы и не сейчас? Я и так всё время откладываю, а этот вечер ничем для этой цели не хуже, чем любой другой.

Чтобы разбираться было веселее, придумал себе задачу — написать на m4 ассемблер, желательно не очень сложный, чтобы занятие на вечерок не разрослось потом до недельного проекта. Взял инструкции интеловского процессора 8080A — это был мой первый ассемблер. Советской аналог процессора стоял на компьютере «Радио-86РК», с  которого я когда-то начинал.

Строка «HELLO WORLD», выведенная программой, переведённой в машинный код моим ассемблером

Несколько слов о том что такое m4.

Это довольно простой макропроцессор, умеющий заменять одни указанные строки на другие. Описание что на что заменить делается параметрическими макросами. В языке есть собственные макросы, но можно определить и пользовательские. Поддерживаются так же условия и математика. В продвинутых версиях m4 есть даже циклы, но их в любой версии можно эмулировать рекурсией.

Парочка примеров того как выглядит его синтаксис:

dnl Макрос, заменяющий одну строку на другую
define(`WORLD', `KAZAN')dnl

dnl Макрос, оставляющий первый свой параметр как есть, а
dnl второй складывающий со значением другого макроса,
dnl если второй параметр не указан, его значение — единица
define(`__pr', `define(`__ip', eval(__ip + ifelse($2, `', 1, $2)))$1')dnl

Чтобы реализовать задуманное, я взял список команд процессора с кодами — их там чуть меньше 250, если брать варианты с разными регистрами, и написал на Пайтоне два генератора.

Два, потому что мне нужно было как-то решить проблему расчёта адресов ссылок. Первая программа генерирует файл linker8080.m4, который этим и занимается. Адреса он на следующем этапе передаёт в виде макросов второму файлу — asm8080.m4, который делает основную работу — переводит ассемблер в машинные коды.

Сначала у меня всё было сделано в одном файле, но я быстро выяснил, что макросы распространяются сверху вниз, то есть определённые ниже макросы не влияют на текст выше. Если ссылка, для которой я подсчитал адрес, находится ниже точки вызова, я этот вызов уже не могу заменить.

Вот программа на этом ассемблере, в действии её можно посмотреть на скриншоте:

ORG(h1100)
LXI H, ADDR(TEXT)
CALL hF818
JMP hF86C

LABEL(TEXT)
BYTE(h48, h45, h4C, h4C, h4F, h20, h57, h4F, h52, h4C, h44, hD, hA, h0)

Не слишком отличается от привычного вида, но отличия всё-таки есть.

Во-первых, числа должны быть только шестнадцатеричными. Часто так и бывает, но тут запись немного непривычная — hFF вместо более привычной FFh.

Во-вторых, многие вещи, не генерирующие код, выглядят чуть иначе — адрес запуска задаётся конструкцией ORG(…), метка — через LABEL(…), а её адрес получается через ADDR(…).

В-третьих, произвольные цепочки байт задаются не директивой DB, а конструкцией BYTE(…).

Результат можно увидеть у меня в репозитории.

ДНК-портрет китайца

Пять лет назад я мечтал о том, что когда-нибудь появится возможность рисовать портрет человека по ДНК. Очевидно, что до какой-то степени это должно быть возможно — генетика, например, совершенно точно влияет на цвет глаз. Вопрос в том, насколько точным будет такой портрет? Ведь на нашу внешность влияют образ жизни, среда и бог знает что ещё.

Утверждается, что теперь у нас есть ответ. Эксперименты идут давно, но недавно результат улучшили ещё на 12% и достигли совершенно былинных результатов.

Китайцы представили модель, которая умеет генерировать трёхмерное изображение лица по ДНК с погрешностью в 3,5 миллиметра. Причём она умеет генерировать лицо в любом возрасте, что ещё более полезно. Лица, конечно, китайские, но вряд ли сложно перенести тот же опыт на другие типажи.

Платная нейросеть ChatGPT

Решил попробовал оплатить ЧатГПТ на месяц (стоит 20 долларов). Интересно было какие плюсы это даёт. «На берегу», до решения, казалось, что это эксперимент на раз — попробую для успокоения любопытства и забуду. Но я ошибался. Самой убойной фишкой оказалось «глубокое исследование» — нейросеть за десятки минут гуглит информацию, относящуюся к запросу, а в конце составляет отчёт.

Я для теста задал найти мне информацию о «штурвале Бауэра», через полчаса у меня был отчёт, занявший несколько экранов. Я бы взял из него лишь малую часть, но удобно, что такое можно кому-то делегировать!

Сам я когда-то потратил выходной день целиком. Конечно мне было намного сложнее — ни на русском, ни на английском нужной информации не было, а нейросеть знает все основные языки, ей всё равно что читать. Для таких исследований, а я ими в некоторые периоды много занимаюсь, этот инструмент очень хочется иметь под рукой.

Да и с более прозаичными вещами эта фишка справляется отлично — например, я заказал ей найти мне переключатель дешевле 15 тысяч, чтобы подключать два разных компьютера к одной клавиатуре и мыши и двум мониторам. Он нашёл мне две модели, одна из них продаётся на «Озоне». То, что нужно!

В общем, чувствую, это будет лучшее моё вложение двадцати долларов за последнее время!

«Тайна Карпатского замка», 1981

У меня время от времени бывают навязчивые идеи. Не мучительно-навязчивые, тем не менее, жить они мешают — иногда зудят, хочется что-нибудь с ними сделать.

Таких идей у меня — целый список, самых разнообразных. Сложнее всего с идеями разыскать какую-нибудь книгу или фильм, которые я в последний раз видел ещё в детстве и помню о них очень мало. К счастью, современные большие языковые модели умеют сами искать в интернете, обобщая найденное и формируя поисковые запросы самостоятельно.

Например, два месяца назад они помогли мне разыскать фильм «Охота на роботов» 1984 года по тем деталям, которые я вспомнил. Я подумал, что разыщу и всё остальное, но потом дело встало. Как я ни пытался, искусственный интеллект ничем мне помочь не смог.

На днях я купил платный доступ к ЧатуГПТ, а сегодня подумал — не поможет ли мне доступ к новейшим моделям разыскать ещё что-нибудь? Напрягся, попробовал вспомнить максимум ещё об одном фильме, который я когда-то видел и мучительно пытался разыскать.

На этот раз сработало! Модель ChatGPT o3 нашла фильм по описанию — им оказалась «Тайна Карпатского замка» 1981 года, причём я наврал в деталях — мне казалось, что это черно-белый старый фильм, годов, вероятно 1920-х. Наверное я видел его в чёрно-белом виде — он шёл в моём детстве, а тогда чёрно-белых телевизоров было очень много.

Одна из сцен, которая мне запомнилась, видна на скриншоте — девушка, которую искал главный герой, оказалась механической куклой. Ну, это я так помнил эту сцену. В реальности девушка была забальзамирована после смерти, а в этой сцене её мумия разрушилась от контакта с воздухом.

Кадр из чехословатского фильма «Тайна Карпатского замка», 1981

Портреты предков

Я тут необычный эксперимент проделал — загрузил фотографии жены и дочки в «ЧатГПТ» и написал такой промпт:

Я тебе загрузил фотографии мамы и дочки. Сделай тщательно описание их портретов, как это делают в полиции. Вычти черты лица мамы из черт лица дочки и нарисуй портрет вероятного отца девочки в возрасте 47 лет.

Намеренно не стал дописывать, что я на самом деле давно облысел и что на лице у меня усы и эспаньолка. Тем не менее, получилось очень похоже на меня и даже на моего папу. Можно определённо утверждать, что это какой-то наш родственник.

Окрылённый успехом, я сделал то, ради чего, собственно, и затеял этот эксперимент — взял фотографии бабушки со стороны мамы и её сестёр, фотографию их мамы и похожим промптом попробовал нарисовать портрет моего прадеда с их стороны.

Он жил в небольшой деревне Старое Исаково и пропал на войне. Ни одной его фотографии нет — вероятно в те годы фотоаппараты не были распространены. А из-за того, что бумаги военкомата сгорели в 50-е, не получилось проследить его судьбу.

Предполагаемая внешность Степана Павловича Стрелкова (1902—1941) — моего прадеда со стороны мамы

Не знаю уж насколько похоже вышло, сравнить не с чем, но определённо кое-какие родовые черты просматриваются — например, нависающее веко. Кстати, непонятно почему нейросеть в этом случае решила сделать портрет в виде карандашного наброска, вероятно потому что почти все фотографии были чёрно-белыми.

Предполагаемая внешность Феодора Николаевича Старостина (1904—1942) — моего прадеда со стороны папы

Попробовал то же самое со стороны папы — нарисовать Феодора Николаевича Старостина. Одно время я надеялся разыскать его могилу. Он так же пропал на войне и его военные бумаги хранились в том же сгоревшем военкомате и ни одной фотографии тоже нет — в Старой Письмянке, где он жил, видимо, тоже негде было сфотографироваться.

Тут к фотографиям я добавил несколько словесных деталей: что вероятный цвет глаз — карий, что он работал агротехником в колхозе, что надо добавить небольшую седину и что он, вероятнее всего, был скорее поджарым, чем страдал от излишнего веса.

«Курсор»

Что-то я опять по кривой Гартнера свалился на дно разочарования в «Курсоре» — это редактор, где можно программировать с помощью нейросетей. Из семи последних задач «Курсор» запорол все семь, да ещё как эпично! Задачи, на мой взгляд, я описывал понятно — так, как описал бы живому программисту.

Чтобы отойти от разочарования, решил как-то реабилитировать технологию в своих глазах, тем более, что некоторые мои задачи нейросети всё-таки выполняют. Взял библиотеку функций из продвинутой реализации bc, чтобы попробовать портировать её на версию GNU.

Попросил три понятных изменения сделать, любой дурак справится:

  • a@b заменить на вызов функции floor(a, b), которую я написал;
  • a$ заменить на вызов floor(a, 0);
  • там, где оператор else перенесен на другую строку от конструкции if, надо либо заэкранировать перенос через обратный слеш, либо вернуть else на одну строку с if.

Задача простая, но даже тут «Курсор» накосячил, не справившись с подсчётом скобок. В итоге, тщательно проверял все замены и считал скобки я сам. К чёрту такую автоматизацию, по сумме, я бы сам быстрее сделал.

Раньше я бы к этому спокойно отнёсся, но в последнее время раздражительность повысилась — мёрзну и всё время на грани простуды. Отопление давно отключили, а на улице резко похолодало — снег уже несколько раз шёл. Но, видимо, недостаточно холодно, чтобы включать отопление обратно.

Вокруг очень многие болеют пневмонией, это ещё что такое, откуда? Я уверен, что слово «пневмония» я слышал за прошедшие два года больше, чем за всю прошлую жизнь. Сам я всё время кашляю, хотя не чувствуется, что болею, временами только какая-то слабость накатывает.

Эрудиция, ум, предприимчивость

Вряд ли стоит говорить о том, как ценен для человека ум. В конце концов, именно ум дистанцирует нас от других животных. Но ум очень сложно померить. Как быстро понять, что кто-то — умный? Тем более, что многие путают ум с другими понятиями. Например, эрудицией. Годы прошли, прежде чем я понял, что Анатолий Вассерман не умный, а эрудированный. Другой пример — ум путают с предприимчивостью. Существует даже поговорка «если ты такой умный, то почему такой бедный?».

Это потому, что люди от необходимости придумали несколько признаков, чтобы быстро определить — умный человек или нет, все они грешат неточностями, но других у нас нет. Например, мы признаём умными тех, кто хорош в науках — например, математике или физике. При этом они могут казаться откровенно тупыми в других областях. Эрудированный человек тоже кажется умным, мы привыкли к тому, что умные люди эрудированны, а исключения редки. Нам кажутся умными грамотные люди — как правило грамотность приходит с упорной работой над собой, а она развивает интеллект.

Я не буду пытаться дать определение уму или интеллекту, я хочу поговорить о другом. Мы столкнулись с новым объектом, который кажется умным, но умным не является — речь, конечно, о больших языковых моделях.

Человек оказался не готов к тому, что чат из машины хранит в своих формулах столько знаний, вычисляет некие связи между ними, так хорошо пишет тексты, что они кажутся рассуждениями, но при этом не является умным.

Некоторые мои знакомые, всерьёз обманувшись этим, навсегда (по их словам) разочаровались в больших языковых моделях, когда столкнулись с «галлюционациями» — это когда языковая модель в процессе генерации текста в некоторых местах далеко отходит от реальности, просто потому что работает так, что её задача — сгенерировать правдоподобный текст, а не рассуждать, придерживаясь фактов. Вероятности выпадения следующего слова настроены в ней так, чтобы как можно ближе быть к реальным знаниям, но и только.

Но хуже всего, что другие мои знакомые вдруг уверовали в мудрость языковых моделей и просят у них точного рецепта как поправить дела в ситуациях, которые определяют их жизнь. Я к ним не лезу, у меня нет синдрома мессии, да кажется, что они так глубоко верят, что мои слова для них куда менее весомы, чем их заблуждения. Это похоже на психотерапию, но разница огромна — психотерапевт никогда не будет делать выбор за вас, языковые модели же подобной этикой не связаны, в них нет такого понятия.

В этой связи я даже как-то стал по-другому смотреть на ксенофобов, которым всё мерещится сюжет фильмов «Терминатор», «Матрица» и других, где роботы порабощают людей. Их идеи — перегиб в другую сторону, но в среднем по больнице у нас в обществе как будто у всех взвешенное отношение к этому этапу развития искусственного интеллекта.

Анатолий Вассерман по версии нейросети ChatGPT 4 — пример эрудиции, но не ума

Природа и дети

Присматривал вчера за дочкой, которая играла на речке — строила мостик от одного островка к другому из прутиков и камней. Пока я смотрел на то как она, оступаясь, перебиралась по веткам, подумал, что не знаю как относиться к нейропсихологам, которые занимаются детьми.

Сейчас считается, что средний современный ребёнок хуже владеет своим телом, хуже его чувствует, хуже оценивает риски, чем дети моего, например, поколения, потому что ему негде вот так полазить, рискуя упасть в холодную воду, ободраться и наставить себе синяков.

Я не исследовал тему глубоко, но знаю, что проблема обсуждается, а специалисты существуют, мы и сами водили дочку к одной из них.

С одной стороны — получение опыта на природе кажется чем-то важным, особенно в плане оценки рисков. С другой стороны я помню своё детство, когда мы лазили по разрушающейся заброшенной стройке и прыгали с её третьего этажа в снег.

Один парень там сломал ногу, другой пропоролся о торчащую, как оказалось, в месте приземления арматуру, а мне упал на голову кирпич, правда это была другая стройка… Не лучше ли контролируемая среда под присмотром взрослого?

Ответа у меня нет, наверное, потому что и вопрос меня не настолько интересует. Мы дочке разрешаем взаимодействовать со средой столько, сколько она хочет, вопрос только времени и доступности среды. Это не значит, что я стану кричать на стройке «молодец, дочка, заберись повыше», но и у неё желания лезть на стройку не возникает.

Таблица умножения

Еве в школе задали выучить таблицу умножения. Пытались учить по столбикам — умножение сначала на одно число, потом на следующее и так далее. Не особо-то это работает — выученное столбиками воспроизводится потом тоже только столбиками. Надо как-то учить вразнобой, а это совсем скучно, дочка от такого быстро устаёт.

Против такого я знаю только один способ — геймификацию. Задумался что за игру можно было бы сделать, почему-то вначале мне пришло в голову, что это должно быть что-то настольно-карточное. Потом осенило — надо на Скретче написать! Мы же как раз с ним возимся!

Сделали программу, где ёжик задаёт вопросы, на неправильные ответы недовольно верещит, а на правильные — ест банан. Недавно ходили с дочкой кормить и гладить живых ёжиков, бананы они уминают за милую душу.

За правильный ответ начисляется одно очко, на неправильный — вычитается. На сто очков сделали танец ёжика под музыку.

Программировал, в основном, я, для дочки пока такое сложно — взаимодействие двух спрайтов через сообщения у неё пока в голове не укладывается, но когда она загоралась что-то несложное доделать, давал клавиатуру ей.

Оно того стоило — дочка вчера на интересе дошла до танца, хотя уже очень устала от прошлых повторений таблицы и от программирования.

Дочкин конный спорт

Из всего многообразия того, чем занималась или занимается сейчас дочка, конный спорт стоит немного особняком — несмотря на то, что тренировки проходят относительно редко — всего раз в неделю, это, тем не менее, занятие которым она увлечена уже несколько лет с перерывами только на болезнь, отпуска и каникулы вне дома.

Дочка без ума от лошадей и всегда рада занятиям конным спортом — это у неё от её мамы. Думаю, она совсем бы была не против ходить туда чаще, если бы мы нашли для этого время. У неё сейчас прекрасный возраст — есть желание и энергия пробовать себя во всём подряд. Мы это только поддерживаем, но надо оставлять и время на отдых.

Тем не менее, хотя и редкие, но регулярные занятия столько лет подряд, дают результаты. Дочка часто выступает на соревнованиях, а в прошлое воскресенье выступила в новом качестве — впервые была одна на маршруте, но заняла второе место. До этого, если не считать парного выступления с мамой, её лошадь под уздцы всегда водил тренер, дочкина же задача была в том, чтобы принимать правильную посадку.

Перед выступлением волновалась, но выступила, как по мне, шикарно. Сделала две ошибки — вместо остановки перевела лошадь на шаг и была помарка после вольта (круга) — надо было вывести лошадь позже, чтобы чётче попасть между балок препятствия.

Что происходит, когда main — переменная

Хочу провести работу над ошибками.

Вчера я писал о сбойной программе на Си с самым коротким текстом и, как оказалось, неправильно описал почему, собственно, она вызывает сбой.

Напомню, речь идёт о программе, которая состоит из одного слова — main; или int main;, если забыть о стандарте Си89. Я думал дело в том, что попытка запустить программу приходит к чтению переменной и передаче управления на адрес ноль, но один читателей попенял, что в оригинальной статье указана другая причина.

Я решил глянуть получившийся ассемблер, чтобы разобраться где же правда. Для этого удобно использовать утилиту objdump:

objdump -D -d -M intel ./small

Оказалось, что я был неправ.

Чтобы это увидеть, надо посмотреть в ассемблерном листинге код, который вызывает функцию __libc_start_main() — она передаёт управление функции main(), указатель на которую указывается в регистре rdi. Как мы видим, адрес, записываемый в этот регистр указывает на секцию .data, код в которой запрещён к запуску, именно поэтому программа и падает.

Почему именно туда? Там хранятся все переменные, в том числе переменная main, то есть используется не адрес, записанный в этой переменной, как я думал раньше, а выполнение передаётся прямо на адрес, где хранится сама переменная.

Кстати, это означает, что на оборудовании, где защиты памяти нет или при запуске в реальном режиме (например, из-под DOS), программа может и не упасть — просто начнёт выполнять какой-то мусор, в котором вполне могут попасться инструкции корректного завершения.

Маленькая сбойная программа на Си

Попалась мне тут статья про задачку, которую автору задали в университете — написать самую маленькую (по объёму текста) программу на Си, которая компилируется, но выполняется с ошибкой. Прежде чем читать, я попробовал написать такую самостоятельно.

Сначала я подумал о рекурсии:

main(){main();}

Для экономии байт можно использовать стандарт Си89, где всё, у чего нет при определении задания типа, имеет тип int — это позволяет убрать тип функции main.

Программа компилируется, но, как и задумывалось, падает с ошибкой:

bolk@note ~$ gcc-14 -std=c89 small.c && ./a.out
Segmentation fault: 11

Дальше я подумал, что вряд ли в статье используется такой очевидный способ и, перебрав в уме несколько вариантов, решил сосредоточится на делении на ноль. Лучший вариант, который мне пришёл в голову, вот такой, он на один байт меньше:

main(a){a/=0;}

Очевидная идея — поделить число на ноль и никуда не присваивать, работать не будет, компилятор просто выкинет константное выражение. Поэтому его надо или присвоить переменной или вернуть. return занимает больше присваивания, но куда бы мы могли присвоить? Нужна же переменная.

Переменную можно взять из аргументов функции main, в первый её аргумент записывается число параметров при вызове. Если ноль поделить на ноль, это ошибки не вызовет, но там всегда не ноль, потому что нулевой параметр всегда существует и содержит имя программы.

Поэтому мы можем поделить его на ноль и программа упадёт при вызове:

bolk@note ~$ gcc-14 -std=c89 -Wno-div-by-zero small.c && ./a.out
Floating point exception: 8

Но самая короткая сбойная программа оказалась почти втрое меньше. Выглядит она так:

main;

Она компилируется, хоть и с замечанием, и падает, как и требуется по условию:

bolk@note ~$ gcc-14 -std=c89 small.c && ./a.out
small.c:1:1: warning: data definition has no type or storage class
    1 | main;
      | ^~~~
Bus error: 10

Тут определяется статическая переменная main с неявным заданием типа int, которая, как все такие переменные, инициализируется нулём. Поскольку компоновщик видит имена, но ничего не знает о типах, он связывает адрес переменной с адресом точки входа в программу. Когда программа запускается, управление передаётся по адресу ноль, что вызывает ошибку.

Ошибка вызывается, кстати, потому что адрес ноль является зарезервированным значением в Си. Ноль обозначает, что указатель пустой (имеет значение NULL), поэтому при обращении к нему срабатывает специальная ловушка, вызывающая ошибку. Правда это возможно только на платформах с защитой памяти.

Добавлено: Оказалось, что я неправильно разобрался в происходящем.

Ранее Ctrl + ↓