Pull to refresh

Comments 262

Неконструктивная статья. Да, проверять возвращаемое malloc значение необходимо, иначе неопределенное поведение. Но и обрабатывать по-умному ошибку, когда malloc вернул NULL, тоже нельзя, поскольку этот случай толком невозможно протестировать. Только еще ошибок при обработке ошибок наделаете.

Распространенное конструктивное решение (за отсутствие упоминания которого я и ставлю минус) состоит в написании функций-врапперов для выделения памяти. Распространенное название одной из них — xmalloc(). Поведение: вызывает malloc() с тем же параметром, если вернулся NULL — вызывает abort() для явного аварийного завершения программы, если не NULL — возвращает указатель на выделенную память. За xmalloc() проверять не надо.

Понимаю, что решение не универсальное, и что за него в некоторых случаях тоже ругают.
А что мешает везде проверять и валиться с «Out of memory», вместо непонятных segmentation faults? Одно дело я вижу проблему, а другое — внезапный crash, да и ещё, как я понимаю, может мне затереть память в неизвестном месте.

Поправьте меня, пожалуйста, если я не прав.
вызывает malloc() с тем же параметром, если вернулся NULL — вызывает abort()
Ради бога. Это как раз и есть та самая проверка, на которой я настаиваю и реакция на неё. Как решить проблему — это вторично. Главное начать её решать, а не надеяться, что если ничего не делать, то так всё само-собой хорошо будет ;).
Но и обрабатывать по-умному ошибку, когда malloc вернул NULL, тоже нельзя, поскольку этот случай толком невозможно протестировать.

Очень даже можно. На Linux'е есть LD_PRELOAD, на macOS DYLD_INSERT_LIBRARIES. Пишем библиотеку которая оборачивает системный malloc. В своей обёртке над malloc'ом вы вольны возвращать NULL когда вам захочется.


Для Windows решение тоже имеется, но реализуется намного сложнее чем на NIX'ах.


Правда вынужден сказать, что заваливание malloc'а достойно переживает только Linux'вый рантайм. На macOS даже printf падает при невозможности выделить память. Эти неприятные мелочи приходится учитывать при тестировании.

Легко сказать «пишем библиотеку». Вопрос в том, как обвалить конкретно этот вызов malloc(), а не какой-то другой, чтобы протестировать код, обрабатывающий именно этот NULL, а не NULL где-то еще.
В gcc можно смотреть на __builtin_return_address (https://gcc.gnu.org/onlinedocs/gcc/Return-Address.html), а из него уже понять имя функции/строку (если собраны с дебагом)
Иногда прерывать нельзя, но все же: поставить под отладчиком остановку по условию и поменять результат на NULL чем не нравится?
Очень много воды и ни одного довода по существу. Если malloc/new вернула null, то лучше падать сразу, чем писать дикие ветвления кода и всё равно падать в конце концов, когда не смогли отследить не корректность данных, так как проверить как поведёт себя вся прога во всех случаях при new==null просто не возможно.
P.S. Для надёжности в серьёзных проектах делают свой new/malloс/..., что бы он сразу валил прогу во внешний try/catch, и весь головняк этой проверки снимается. Странно если в хроме этого нет. Точно нет (не смотрел)?
Вы неправы. Так делать неправильно.
Простейший пример: из-за ошибки программиста программа просить выделить ОЧЕНЬ большой буфер, которого нет.
При правильной обработке ошибок мы просто выйдем с ошибкой из функции, показав пользователю понятное сообщение и дав ему сохранить имеющуюся работу. А возможно и продолжить работу нормально, просто какая-то часть функционала будет недоступна.
Пример простой, но если проверять КАЖДОЕ выделение памяти (и освоождать все выделенное даже при пробросе исключения), то программа станет гораздо надежнее.
Так для этого случая как раз и нужен try/catch. Если какое-то большое действие внутри завершается неудачей, программа работает дальше, если может. При правильной архитектуре в следующем try/catch даже память выделенную внутри этого действия пытается чистить за собой. А потом и выводит пользователю какое-то сообщение если может/надо.
Весь код надо тестировать. И если раздувать его подобными ветвлениями, которые ни когда не должны исполняться — это усложняет всё очень сильно там, где ни какого смысла усложнять нет.
1. try/catch в С нет
2. использовать malloc в C++ довольно странная затея
3. стандартный new (насколько мне известно) никогда nullptr не возвращает.
В C есть для этого goto и setjmp/longjmp
2 Да странно, но ручки у malloc которая затронет все приложение есть (mallopt) В C++ такои случае нужно либо везде пихать свои аллокаторы либо дергать тот же mallopt надеясь что все юзают стандартные аллокаторы с new который свалится в тот же malloc. А так да malloc в C++ выглядит чужеродно
3) Возвращал но до офф версии стандарта 98 года
Есть new (std::nothrow), которая возвращает nullptr. А без дополнительных аргументов new бросит исключение std::bad_alloc.
Очень много воды и ни одного довода по существу. Если malloc/new вернула null, то лучше падать сразу,


Так против этого автор статьи ничего и не говорит. Нужно падать сразу. Но явно, сделав проверку на Null.

во всех случаях при new==null

Здесь чистый косяк компилятора — new (без опции std::nothrow) обязан либо вернуть не null, либо выбросить исключение.
Это не косяк компилятора, просто в статье описывается именно поведение malloc. Насколько я понимаю, при выделении памяти оператором new такой проблемы не возникнет
Да, прекрасное решение, особенно для встраиваемых непрерывных систем. Хотели бы Вы полетать на самолете запрограммированном таким образом? или лифту хотя бы поездить?
А не надо для встраиваемых систем динамически выделять память. Только преаллокация при компиляции.
Слышал интересное утверждение (там, кажется, ссылались конкретно на правила Сименса), что у самолетов в бортовом оборудовании вся память должна быть выделена до взлета.
Так и есть. То же самое относится и к космической технике. И вообще к любой встраиваемой.
До этой глубокой мысли авторы FreeRTOS дошли в позапрошлом году. До 9-й версии все объекты freertos'а (задачи, очереди, семафоры и т.д.) выделялись malloc'ом. Сейчас хотя бы выбор появился…

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

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

поэтому для встраиваемых систем вообще malloc использовать не рекомендуется.
чего там лифту выделять динамически? можно эти 3 байта статически глобально объявить.
Подскажите стандарт на счет использовать не рекомендуется, мне на ум только Misra приходит. Тут возникает вопрос в другом, где взять программиста который будет делать без malloc в другом стиле мышления, когда все кругом привыкли писать уже и с автоматическим сборщиком мусора.
я мисру и имел в виду
предполагается, что во встраиваемых системах не будут поступать неизвестные объемы данных, а место под известные объемы можно выделить вручную заранее.
для самолета подход вряд ли прокатит — там скорее всего используется куча готовых библиотек, а для лифта — запросто, и программист скорее всего даже не задумываясь не станет вызывать malloc для хранения массива нажатых кнопок итп, а просто объявит его с известным размером.
у нас в программе при старте используется malloc для создания обьектов (потому что удобнее это делать в унифицированном виде, к примеру, массива обьектов разнообразных фильтров разной длинны), но созданные обьекты НИКОГДА до перезагрузки не уничтожаются…
Поздравляю: вы изобрели JavaCard — великую версию Java без GC.

У них, правда, круче: GC нет, память можно только выделить, но не освободить… и никакой перезагрузки тоже нет — если хотите память освободить извольте приложение (вернее «кардлет») снести, а потом поставить заново.

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


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

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

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

Если устройство имеет очень серьезную программную часть (насыщенный интерфейс пользователя, содержательные вычисления и т.п.), то обычно разработчик(и) железа оборачивают взаимодействие с аппаратурой в API, учитывающий все нюансы, и уже его отдают программистам.
Получается причина одна, просто приведены разные сценарии ее проявления. Но в любом случае Вы правы.
Если честно, я теряюсь в догадках, какая связь между проектом Chromium и LLVM, но она есть

Наверное генерят нативный код для исполнения JavaScript виртуальной машиной.
Для WebAssembly наверное используется. Да и шейдеры можно им компилировать.
Subzero — Fast code generator for PNaCl bitcode

Возможно я отстал от жизни и что-то изменилось (прошло уже лет 10, как я проверял это поведение, но поскольку на тот момент это была "не бага, а фича", то...), но чтобы получить NULL от malloc под Линуксом, нужно еще постараться — скорее система сама прибьёт процесс при попытке выделить память. Вроде можно повозиться с настройкой каких-то лимитов (я виндузятник, под линуксом только время от времени), но с настройками по умолчанию по исчерпанию памяти система просто прибивала процесс. Т.е. до проверки на NULL дело не доходило, хотя она и была.

… но чтобы получить NULL от malloc под Линуксом, нужно еще постараться — скорее система сама прибьёт процесс при попытке выделить память

Сейчас, допустим, трудно «получить NULL от malloc под Линуксом». Где гарантия, что через год будет также? В статье как раз и говорится, что это неопределённое поведение и не нужно до него доводить.

Я про то, что можно городить хитрые проверки, загромождая и запутывая код, а это все будет совершенно без толку, потому что на клиентской машине настройки по умолчанию и проверки просто не сработают. Кстати, вспомнилось, что линукс процесс убьет даже не при выделении памяти, а только при попытке обращения к ней. Привет проверкам! :)


Проверять результат malloc стоит, когда выделяются существенные объемы памяти под какие-то нужды программы. Тогда, получив NULL (что не факт — см. линукс), есть шанс отработать ошибку. В остальных случаях может оказаться, что даже функции вывода сообщения на экран может потребоваться память для форматирования сообщения, которую так же не удастся выделить. В таких случаях сам факт исчерпания памяти — уже неопределенное поведение. А вот множество проверок на результат выделения памяти так засорят код, что врагу не пожелаешь. Так что проверки — только в ключевых местах, где происходит выделение больших регионов памяти.


Речь, конечно, не идет и микроконтроллерах и т.п. системах с жесткими ограничениями на размер памяти.

Я про то, что можно городить хитрые проверки, загромождая и запутывая код,


В статье речь про ровно одну проверку. Которую можно убрать в свою обёртку на malloc'ами.

Без этой проверки — неприятное UB, за которое цепляется глаз при чтении кода.

Кстати, вспомнилось, что линукс процесс убьет даже не при выделении памяти, а только при попытке обращения к ней. Привет проверкам! :)


Убьёт — так убьёт. Нам в общем-то от проверки это и нужно — проверить на null и умереть. Не побив по дороге данные.

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

В таких случаях сам факт исчерпания памяти — уже неопределенное поведение.


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

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


Premature optimization.
К слову, допустим, эта самая оптимизация вам прямо-таки нужна.
Ну то есть, давайте я возьму и поверю, что убирание одной проверочки на null в вызовах malloc'а в местах, где большие регионы памяти не выделяются, делает вам погоду. Что ж, время задуматься о применении (или даже написании) кастомного алокатора.

Речь, конечно, не идет и микроконтроллерах и т.п. системах с жесткими ограничениями на размер памяти.


Речь идёт о коде кроссплатформенных библиотек. Сегодня Вася её в код браузера впихивает под какой-нибудь десктопный линукс или винду, а завтра та же самая библиотека будет пересобрана под minix в Intel ME на ограниченном количестве памяти хрен знает как замапаном. Это нормальное использование Open Source библиотек. И это нормально такие проверочки делать.

Под линуксом (без специальных настроек системы) malloc вернет не NULL, даже если память закончилась. И программа упадет уже при попытке обращения к этой памяти. Т.е. проверка на NULL не имеет смысла. Ну да не линуксом единым...


Под виндой ситуация лучше — malloc вернет NULL, если память выделить не удалось. Но что с этим знанием делать? Если просто молча аварийно завершить выполнение, то разыменование нулевого указателя сделает это за вас (да, иногда адресная арифметика может преподнести сюрпризы, но когда речь идет о достаточно больших регионах памяти, проверка результата malloc даже под линуксом не повредит). Если же пытаться как-то обрабатывать эту ситуацию, то обработка ошибки выделения памяти тоже может потребовать немало памяти. Это в эпоху MSDOS можно было вывести строку на экран, вызвав прерывание BIOS, до байта понимая, сколько памяти потребуется и как ее зарезервировать. В современных системах это уже гораздо сложнее. А привести к отказу выделения памяти может запрос даже одного байта.


С библиотеками ситуация немного проще — им обычно не требуется обработка ошибки. Достаточно просто установить как-то ее признак и завершить выполнение текущей библиотечной функции (тоже может оказаться нетривиально).


Что касается обертки вокруг malloc… Если бы это был выход, библиотечный malloc вызывал бы exit вместо возвращения NULL. Если обертка вокруг malloc будет всегда убивать процесс при получении любого нулевого указателя, вы лишите программу возможности обрабатывать ситуации, когда есть шанс обработать эту ошибку более корректно (выдав пользователю сообщение, что, к сожалению, его гигабайтный файл не удалось загрузить в память). Если не всегда, то обертка теряет смысл.


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


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

Если же пытаться как-то обрабатывать эту ситуацию, то обработка ошибки выделения памяти тоже может потребовать немало памяти.


. Это в эпоху MSDOS можно было вывести строку на экран, вызвав прерывание BIOS, до байта понимая, сколько памяти потребуется и как ее зарезервировать. В современных системах это уже гораздо сложнее. А привести к отказу выделения памяти может запрос даже одного байта.


Позвольте полюбопытствовать.

Вот вы видите, что я написал следующее:
Убьёт — так убьёт. Нам в общем-то от проверки это и нужно — проверить на null и умереть. Не побив по дороге данные.


Так же вы видите слова автора статьи в соседней ветке:
вызывает malloc() с тем же параметром, если вернулся NULL — вызывает abort()

Ради бога. Это как раз и есть та самая проверка, на которой я настаиваю и реакция на неё.


Зачем же вы (и другие комментаторы) продолжаете писать большие и длинные аргументы про то, что содержательную обработку такой ошибки сделать не получится, если очевидно, что абсолютно никто о ней не говорил ни в посте, ни в комментариях?

Вода — мокрая.
Содержательная обработка в такой ситуации — не нужна.

Ни с этим, ни с другим вроде бы никто не спорит, не?

Под линуксом (без специальных настроек системы) malloc вернет не NULL, даже если память закончилась. И программа упадет уже при попытке обращения к этой памяти. Т.е. проверка на NULL не имеет смысла. Ну да не линуксом единым...


Да, под линуксом при нехватки памяти malloc скорее всего не вернёт null — это истинное утверждение.

Под линуксом (без специальных настроек системы) malloc вернет не NULL


Нет, ситуации, когда malloc вернёт null под линуксом возможны.

Опять же, причём здесь линукс? Речь идёт о библиотеке вроде как.

Если не всегда, то обертка теряет смысл.

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

По мне проблема — дурацкая.

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


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

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


Тогда не прибивайте.

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

Я решительно не понимаю, с каким тезисом вы спорите и что вы пытаетесь кому-то доказать.

Вот вы сначала говорите «Ну так пусть приложение возьмёт и умрёт! Не надо обрабатывать ошибку! У вас и не получится её обработать нормально — памяти-то нет!»

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

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


Теперь вы говорите нечто противоположное.

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

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


Стоит читать хотя бы те комментарии, на которые вы отвечаете.
пардон, отвлёкся и не успел поправить комментарий в установленный срок, чтобы это считалось редактированием.
И в этом случае (когда вы всё-таки хотите уйти в умный обработчик)

Имелось в виду:
И в этом случае (когда вы всё-таки хотите уйти в умный обработчик), проверка на null вам тем более не будет лишней. Вам, более того, скорее всего даже захочется ещё дополнительно проверить и убедиться на месте, что система память сможет выделить. (При помощи memset_s'а, к примеру).
Сейчас да — происходит page fault, памяти для страницы физической нет -> процесс умирает. А указатель malloc чаще всего выдаёт нормальный.

Это всё при включённом overcommit. Если порог понизить или выключить совсем — можно получить NULL.

Итого мораль простая — если хочется, чтобы программа корректно без сюрпризов работала и сегодня при любых настройках и в будущем — проверка нужна. Если целевая платформа это Linux с настройками по умолчанию: в принципе не то чтобы и нужна.
Если целевая платформа это Linux с настройками по умолчанию: в принципе не то чтобы и нужна.
Хочу заметить, что такое недопустимо для библиотек. Библиотека не знает, где и как её будут использовать. Например, тот-же Chromium, использующий кучу библиотек, работает в Windows, где malloc/realloc вполне может дать нулевой указатель, если запросили много или если память приложения сильно фрагментирована.
Согласен. От библиотек всегда хочется стабильности и предсказуемости в самых разных ситуациях, часть из которых даже в голову разработчиками не могла прийти на момент написания кода.
Нет ни чего сложного. У меня несколько раз получалось, когда из-за ошибки я просил выделить слишком много памяти, в результате malloc возвращал NULL. Я так как я проверяю, что вернула функция, то я просто получал в логе ошибку, что не удалось выделить память…

Есть ситуации, когда runtime сам может понять, что запрошено слишком много памяти и нет шансов разместить данный блок в куче. Тогда до обращения с запросом памяти к системе дело и не дойдет, можно сразу вернуть NULL. А вот вы попробуйте в цикле небольшими блоками выделять память, пока она не закончится...

Зачем? Я просто написал, что это сделать не так сложно… По моему опыту ошибки с выделением памяти были связаны только с ошибками в коде, мне ни когда не требовалось очень много памяти… А ошибку я вспомнил, она была простая: увеличиваем пул памяти в полтора раза когда не хватает, при этом забываем увеличить счётчик выделенной памяти и очень быстро получаем ошибку :)
На 32-битной системе (любой) получилить очень просто — размер адресного пространства 3 Gb, в которые еще и спроэцирована куча библиотек и которое еще и фрагментировано. 2-2.5 Gb выделили — и всё nullptr.
Самый очевидный пример: базы данных. Неожиданное падение на транзакции, да еще с порчей неизвестных данных в разделяемой памяти. Пользователь будет рад, что в его базе появилась крутая библиотечка, которая круто парсит json или еще чего.
Кому-то очевидно, что падать при первом поводе это плохая идея, а кому-то нет. :) Вот на реддите, например, статью и заминусовали и закритиковали.
FailFast — прекрасный паттерн. Но это не значит, что его нужно применять бездумно.
Если падение в определенный момент приводит к порче данных — от этого нужно защищаться, это очевидно и это не противоречит Fail Fast.
По опыту обсуждения подобной темы складывается ощущение, что в Интернетах есть две секты. Приверженцы первой свято уверены в том, что под Linux-ом malloc никогда не возвращает NULL. Приверженцы второй свято уверены в том, что если память в программе выделить не удалось, но ничего уже в принципе сделать нельзя, нужно только падать.

Переубедить их никак нельзя. Особенно когда эти две секты пересекаются. Можно только принять это как данность. Причем не суть важно, reddit это, Хабр, LOR или еще какой профильный ресурс.
Это бездумная попытка доказать свою правоту, при этом все попытки — суть подлоги. Я ведь знаю, кого ты цитируешь и что именно тот, кого ты цитируешь говорил. Ведь говорил это я. И никаких «никогда» не было. Был описан набор ограничений, в раках которых malloc() вернуть NULL не может. Это объективный факт.

Ты можешь рассуждать о чём угодно, но это не изменит этого факты. Всё что ты можешь — это апеллировать к «на это нельзя рассчитывать», но тогда ты уже не расскажешь про секту.
Ой, да это же LOR-овский Царь-Сишки-Балабол сюда приплыл.

Эти ограничения находятся под контролем разработчика? Или это ограничения на среду исполнения, повлиять на которые разработчик может, максимум, проверяя среду при старте программы и в случае несоответствия умирая с сообщение типа "программа будет работать только с такими-то настройками ядра"? Если второе, то как часто вы видели такие проверки в реальном коде?

Эти ограничения находятся под контролем разработчика?

Да.

Или это ограничения на среду исполнения

Это манёвры, а не конструктив. Проверяете ли вы среду на наличие необходимых ресурсов( той же памяти) — нет. Вас это так же интересует и в контексте динамический аллоцируемых массивов/строк в тех же крестах. Они разбрасываются ВМ( кстати, раз начали говорить о ВМ, типичный срыватель покровов и его представления о матчасти продемонстрировано тут) на право и налево, и при изменении той ручки — получаешь в среднем увеличение потребляемой векторами/строками памяти на 25% даже на больших векторах/строках. Это кого-то волнует?

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

Точно так же, на аллокациях меньше страницы шанс не получить NULL стремиться к 100%, как и шанс словить ООМ. С любыми ручками. Обходится это только лимитами, но в рамках вашей же логики — вы не можете рассчитывать на среду исполнения, т.е. это не работает.

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

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

И всё было бы хорошо, если бы нам рассказали как именно добиться проверки «есть память, либо нет», но нам дали какую-то соломинку, которая что-то гарантирует автору/нам лишь по причине незнания, либо умалчивания деталей.

И именно поэтому, когда программист знает — что может вернуть NULL, а что не может. Он сможет проверить — есть ли у него память, либо нет( если это действительно важно для корректной работы приложения), а вот вот человек, уверенный в том, что NULL его спасёт — нет. И он повесит систему, либо словит sigkill.

Если второе, то как часто вы видели такие проверки в реальном коде?

Дело тут не совсем в проверках. Есть механизмы, которые 100% дадут NULL( на самом деле никакие NULL'ы там не используются) и это не malloc(). В частности mmap() + MAP_POPULATE.

Откуда взялись эти 64кб из статьи? Мне неведомо. Подобного я никогда не видел.

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

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

Третий пример вообще паста с первого. Зачем это?

Перед нами типовая ситуация: в буфере не хватает свободного места для хранения данных, и его следует увеличить. Для увеличения размера буфера используется функция realloc, которая может вернуть NULL.

Здесь явный подлог. С чего автор взял, что его рассуждения про «может вернуть NULL» имеют какое-то отношения к реальности? Он живёт в своём мире и натягивает его свойства на реальность.

Вообще заполнять память сразу после выделения буфера достаточно странная идея. Странная потому, что есть функция calloc.


Это так же говорит о том, что представления автора слабо коррелируют с реальностью. Я советую погуглить автору по ключевому слову «префолт», а потом уже о чём-то рассуждать.

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

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

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

проверка NULL == безопасно

Не передергивайте. Проверка NULL закрывает лишь один вектор атаки или сбоя, она увеличивает безопасность, но не гарантирует её.


Если кто-то крутит ручки, которые крутить не надо — это его проблема.

Если ручки есть, то они сделаны, чтобы их крутить. В каких ситуациях и как крутить отдельный разговор.


Существует окружение по у молчанию, и всегда можно покрутить ручки так, что никакая программа работать не будет. Следует ли из этого что-то? Нет.

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


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

Не передергивайте. Проверка NULL закрывает лишь один вектор атаки или сбоя, она увеличивает безопасность, но не гарантирует её.

Это ваши фантазии, которые нигде в статье не определены. В статье используются явные противопоставления «небезопасного кода»(без проверок) коду с проверками, т.е. безопасному. Это первое.

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

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

Вы пытаетесь подменять понятия, а именно выдавать свои фантазии за позицию оппонентов. Позиция оппонентов заключается не в «не надо проверять», а в «я понимаю, что делаю» и на это даже в статье ссылка есть. Хотя это неважно, ведь я ни за кого не отвечаю, как и не утверждаю «не надо проверять».

Дак вот, проверяя — вы теряете «я знаю что делаю», а значит теряете хоть какую-то возможность создать хоть что-то безопасное. Вы не получаете преимуществ — вы получаете дыры.

Если ещё проще. Вы противопоставляете «проверке» импорт гарантий снизу, а значит — вы никогда и никаких гарантий не получите, а вот ваш оппонент получит.

Если ручки есть, то они сделаны, чтобы их крутить. В каких ситуациях и как крутить отдельный разговор.

Очень глупо. Stack limit нигде не определён, а такая ручка есть. Почему ваша программа не работает, ведь ручки созданы для того, чтобы их крутить?

И да, я вам открою тайну. В ядре нет ручек. В ядре есть кастыль, который специально сделан для того, чтобы эмулировать подобное поведение. Т.е. это именно ваша проверка работает только тогда, когда включен специальный флаг в окружении( и то на самом деле нет).

Это ваш подход напрямую зависит от ручек.

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

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

Если я могу гарантировать, что в случае отсутствия проверки — NULL у меня не будет, то и неопределённого поведения у меня не будет.
Есть третья группа, но они грамотно прячутся.
Нарисовали бы единорога с циркуляркой на КДПВ, выбивается из фирменного стиля )

Можно не минусить, проверки на NULL я делаю
Если не ошибаюсь, то обход массива от последнего к первому чуточку эффективнее (лет 10 назад пытался изучать ассемблер, декремент указателя эффективнее работает, кажется), так что проблема весьма реальна. Не мемсетом едины.)

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

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

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

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

Или вот, например:

void draw_pixel(char* framebuffer, int x, int y)
{
//SHOULD I DO THAT?
if(framebuffer==nullptr) return;

//draw pixel actually
..................
}

void draw_line(char* framebuffer, int x1, int y1, int x2, int y2)
{
//SHOULD I DO THAT?
if(framebuffer==nullptr) return;
for( ..N.. ) {
draw_pixel(framebuffer,x,y);
}
}

main()
{
char* screen = new char[width*height];
//actual check for null
if(screen==nullptr) { cout << OOPS; return -1; }
draw_line(screen, 100,50,500,800);
}

char* screen = new char[width*height];
//actual check for null
if(screen==nullptr) { cout << OOPS; return -1; }


new не вернёт null.
А оптимизатор. к слову, все проверки на null в этом примере, вырежет (хотя закладываться на это не стоит и писать их в данной ситуации как минимум странно).
Ну окей, ошибся, быстро писал.
Например, там malloc.
Вопрос по существу — нужно ли проверять везде на nullptr или нет.
Например, там malloc.
Вопрос по существу — нужно ли проверять везде на nullptr или нет.


Теперь представьте, что в проверках этих внутри методов не «return;», а какая-то содержательная фигня. В логи написать «ФРЕЙМБУФЕР УПАЛ», например. Байтик переслать на отладочный девайс. Ещё что-то.
А ещё представьте, что «if(screen==nullptr) { cout << OOPS; return -1; }» в main'е вы не написали.
И перед вызовом draw_line умудрились разыменовывать screen и не упасть.

Отправится ли теперь заветный байтик? Найдём ли мы теперь «ФРЕЙМБУФЕР УПАЛ» в логе? Теперь это загадка.

Вопрос по существу — нужно ли проверять везде на nullptr или нет


Если нужно — проверяйте, не нужно — не проверяйте. Вопрос проектирования API и оптимизации бутылочных горлышек в вашем коде.
Может быть эти проверки погоды вам не делают.
А может быть половину этих методов надо заменить на ассемблерные вставки с огромным комментарием «НА ВХОД ОЖИДАЕТСЯ ВОТ ЭТО И ВОТ ТО — КОД НЕ ТРОГАТЬ!1111».

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


Если очень хочется, можно даже на ноль делить. Только т-ссс, никому!

Закон Мерфи. Если программа может вылететь из-за того, что в какую-то функцию на 115м уровне вложенности, какой-то Вася Пупкин передал нулевую ссылку, то она обязательно вылетит.
Следствие: Вылетит она в тот момент, когда программа будет управлять взлетом ракеты с комсмодрома Восточный
Следствие: Если суммарная экономия на том, что не надо выполнять проверку на NULL, составляет 3 секунды процессорного времени за 10 лет непрерывной работы программы, то стоимость поиска ошибки будет астрономической
Следствие: Если программа полагается на системные средства Линух (например), чтобы генерировать сообщения об ошибке, то код этой программы обязательно будет перенесен в Виндоуз/Мак/Ардуино, где эта фича работает по другому и программа навернется


Вывод: Лучше ВСЕГДА перебздеть, чем недобздеть.

Странно, что когда я привожу конкретный пример про рисование линии с помощью функции рисования пикселей мне приводят контр доводы про полет спутника.
Несопоставимые вещи.
В конкретно моем примере проверка на ноль в функции рисования пикселя может значительно сказаться на быстродействии программы, так как эта проверка будет вызываться миллионы раз в графической программе, а может быть вызвана всего один раз при инициализации графической системы.
Гораздо разумнее задокументировать АПИ и описать требования к параметрам, чем проверять «везде» и «всегда».
Давайте я другой пример приведу.
Представьте, что вы пишите программу майнинга криптовалюты и ваш код на OpenCL будет работать на тысяче процессоров в видеокарте. Вы передаете в процедуру параметры и указатели на блоки памяти. Будете всегда проверять указатели на ноль? Или все же не станете этого делать? Ведь от быстродействия кода зависит прибыль майнинга…
Вот именно поэтому и надо проверять в том месте где память выделяется, а не там где она используется.

Вы как-то мелко придираетесь. Давайте уж сразу проверять в каждой функции, хватит ли ей оставшегося места на стеке (с учётом всех дочерних вызовов). А иначе программа аварийно завершится (и вы даже обработать это не факт, что сможете) и очень важные данные, которые в этот момент сохранял CAD, потеряются навсегда. Полнейшая безответственность!
Уже трижды сталкивался с нехваткой места в стеке (а его обычно аж целый 1Мб). Один раз даже такая программа привела к глюкам ядра: система, вроде бы, и продолжала работать, но любой запрос списка процессов приводил к зависанию запрашивающей программы с невозможностью даже её убить.

Лично меня больше задела мысль о том, что программа может обратиться к чужой памяти и испортить ее. Когда-то давно, когда я прочитал про 386 процессор и его защищенный режим, я понял, что нормально написанная ОС не дает процессу возможности записи туда, куда не положено.
Здесь же утверждается, что ОС с ядром Линукс защищают от порчи данных только первые 64Кб некоторого адресного пространства. Но если это так, не значит ли это, что в этих ОС большие проблемы с безопасностью?
И если это — общеизвестный факт, то почему с этим ничего не делают разработчики ядра?
Подскажите, пожалуйста, те, кто разбираются, неужели это правда?
Становится страшно жить.
Да, данные сторонних приложений в современных ОС так просто не испортить. Однако, программе достаточно испортить собственные данные, чтобы возникли непредсказуемые эффекты. С начала испортили, потом перехватили исключение/сигнал и продолжили работать. Начали, например, аварийно данные сохранять. А ведь где-то что-то испорчено.
Или испортили что-то в данных собственной параллельной нити. И пока программа из-за нехватки памяти готовилась к завершению работы (в лог про ошибку писала), эта параллельная нить с неверными данными способна ужас разный наделать.
А ещё собственные данные могут обрабатываться для кого-то. Пример — те же базы данных (выше в комментариях было).
Камень с души упал. Значит, я просто неверно понял суть проблемы.
А почему бы не убрать в ОС эту проверку на обращение к нулевому адресу, чтобы дисциплинировать программистов? :)
Наверняка этим же методом можно что-то еще подпортить в шаренной между процессами памяти, например.
UFO just landed and posted this here
в чем разница: 1234->member и null->member?
Прошу уточнить вопрос.

порча да, но только данных этого приложения же
Этого мало? Уточню: надо думать не в контексте порчи памяти в игре тетрис :).
Было бы интересно в тетрисе найти уязвимость, которая при определённой конфигурации фигурок приводила бы к увеличению счёта до достаточно большой величины :)
UFO just landed and posted this here
В целом согласен, но хочу отметить:
навредить другому приложению нет возможности.
Достаточно навредить себе! Особенно если это большое, сложное параллельное приложение, где malloc вернул 0 в одном из потоков и поток был прибит, а приложение продолжит работать. Вот только, где-то что-то могло быть испорчено.
UFO just landed and posted this here
вот Вы выделили память, а ее нет. Что может сделать приложение? Освободить какую-то память и повторить попытку выделения. Других вариантов у него нет.

Есть. Вывести сообщение пользователю или в лог. Подождать. Отменить текущую команду. Убить текущую нить. Умереть. Комбианции всего этого в разных варинатах.

PS: вот Вы выделили память, а ее нет. Что может сделать приложение? Освободить какую-то память и повторить попытку выделения. Других вариантов у него нет.
Почему нет? Есть. Самый простой вариант: выделить память из резервного буффера. Так работал Turbo Pascal 6.0 четверть века назад.

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

Если нас просят открыть какое-нибудь окно, то проверяем — не перешли ли мы на использование резервного буффера и если да — сообщаем об ошике. Создаём окно (так как у нас есть резервный буффер, то проблем с выделением памяти не возникнет), после чего ещё раз проверяем — не начали ли мы использовать «резерв». Если начали — закрываем окно, сообщаем об ошибке.

Просто, надёжно — и никаких проблем с «exception safety» и прочими разными NPE. Очень жаль, что за прошедшие с тех пор годы индустрия почти разучилась писать надёжные программы. «И так сойдёт» — девиз современного IT. Вот это вот — всего лишь вишенка на тортике…
UFO just landed and posted this here
UFO just landed and posted this here
Это спор деонтологиста с консеквентиалистом, просто разные точки зрения.
Я (как представитель первого типа) бы не хотел пользоваться, БД, которая использует mmap или ведёт журнал в памяти и который, при отсутствии возможности выделить память, «портится». По поводу адресов и overcommit — это может быть другая платформа (в т.ч. embedded), другая ОС, что угодно может отличаться от того, что «обычно бывает».

Самое отвратительное, что это может ухудшить обработку ошибок, допустим, приведя к записи на диск из recovery path повреждённых данных, причём выявить такое «обычным» тестированием может быть весьма проблематично.

Отдельно отмечу, что я знаком с мнением, что обработка ошибок вторична, а первична основная функциональность, но не согласен с ним, в любой системе с требованиями к надёжности, отличными от «всё равно что получится», политика обработки ошибок и восстановления после них как минимум не менее важна чем эта основная функциональность (как пример приведу ту же самую БД, я не думаю что было бы много желающих пользоваться БД, которая почти всегда работает нормально, но если на ФС заканчивается место или возникает ошибка записи на диск, то все данные повреждаются). Дам мою любимую ссылку: www.usenix.org/system/files/conference/osdi14/osdi14-paper-yuan.pdf
UFO just landed and posted this here
UFO just landed and posted this here
$ cat /proc/self/maps
00400000-0040c000 r-xp 00000000 08:01 3932328                            /bin/cat
0060b000-0060c000 r--p 0000b000 08:01 3932328                            /bin/cat
0060c000-0060d000 rw-p 0000c000 08:01 3932328                            /bin/cat
01bb9000-01bda000 rw-p 00000000 00:00 0                                  [heap]
...

$ uname -a
Linux 4.10.0-42-generic #46~16.04.1-Ubuntu SMP Mon Dec 4 15:57:59 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux


Уже не так невозможно выглядит, правда?
UFO just landed and posted this here
перфекционист говорит: «надо всегда выполнять проверки/реакцию»


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

И ситуации с UB для перфекциониста неприятны.
Вот перфекционист хочет, чтобы приложение просто взяло и упало, если malloc вернул null.

Можно написать явно «если null — возьми и упади».

А можно положиться на знания того, что вот оно в этой конкретной ситуации упадёт сразу, вот в той конкретной ситуации, которая возникает в сто раз реже, упадёт предварительно непредсказуемым образом попортив свои же данные, но для того множества приложений в которые мой код попадёт это не критично, всё равно ведь упадёт, вот в этой ситуации, которая происходит один раз на миллион, создаст вполне себе уязвимость, но такая ситуация не случится (наверное. Правда ведь не случится?), а послезавтра вообще появится необходимость эту библиотеку собрать под телевизор или IoT устройство и появится ещё две-три новых ситуаций.

И вот как бы на кой чёрт этому самому перфекционисту держать в своей голове все эти инварианты и объяснения, почему оно всё-равно-упадёт-как-надо, если можно взять и написать явно. Тем более, цена такого решения — нулевая.
UFO just landed and posted this here
то есть в hello world она нулевая, а в реальных приложениях вполне будет ненулевой.
потому что помимо вопроса с malloc будет вопрос с open, перечнем errno, итп итд.


Ага. Или с fork.

и использовать new/malloc только там где она может доставить неприятности. как-то так.


Почему вы таки смешиваете new и malloc? Конкретная проблема, которая обсуждается в этой статье, касается только с malloc (ну или nothrow-вариации оператора new разве что).
UFO just landed and posted this here
скажут «достаточно знать о проблеме и применять превентивные меры только там где они необходимы»


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

«достаточно знать о проблеме и применять превентивные меры только там где они необходимы»


Ну, да. Так в общем-то и предлагается делать.
Есть проблема (которую описал автор статьи). Есть превентивная мера, которую предлагает автор статьи — писать без UB.

Вот в рантайме сплошная неопределённость, сами приводите пример с ней, зачем нам дополнительная неопределённость на этапе компиляции — чтобы ещё больше усложнить отладку?
UFO just landed and posted this here
какие проблемы на стадии компиляции от не проверки результата malloc?


Ну, гипотетический пример:
Выделили буфер в 100мб, malloc вернул null, не проверили сразу -> начали разыменовывать (например, положили в конец буфера что-то и попали на свою память).
Передали указатель дальше.
Дальше передали указатель туда-сюда и отправили и имеем код вида

if (p == null)
{
какая-то умная обработка ошибки
}
else
{
начинаем заполнять p целиком
}.

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

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

Ну вы то умный, через пару дней мучения джуна покажете ему что такое UB и как оптимизатор работает, но зачем оно вам нужно, если с самого начала можно было писать не допуская UB бесплатно и без смс?
UFO just landed and posted this here
по моему что-то очень надуманное.


Надуманный сценарий, при котором данные инициализируются в таком порядке или надуманное отрезание ветки с проверкой на null?
UFO just landed and posted this here
При всём при этом крайне желательно, чтобы браузер хотя бы сохранил список открытых закладок и настройки, если те были изменены.
UFO just landed and posted this here
Для таких вещей есть понятие «Системные требования». Но когда в разделе «Системные требования» будет стоять строка «Объём оперативной памяти: 128 Петабайт» (и в скобках — залобались выделять больше и убили процесс), то это немножко странно, не находите?
Программа может обработать это структурное исключение/сигнал. Но уже поздно. Где-то в памяти есть испорченные данные. Причем непонятно, какие данные испорчены и к каким последствиям это может привести!

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

Главное, не успеть записать эти испорченные данные в базу/файл. Но там, где это действительно важно, можно использовать журналирование.

Есть много случаев, когда UB связанное с нулевым указателем может привести к беде. И эти случаи надо знать. Но это сводится к «понимай как работает твой код, если случится nullptr», а не к «везде вставляй проверки».
Вообще вся статья относитсяисключительно к С. В С++ просто не надо использовать malloc и всё.
Спасибо за статью. Правильно я понимаю, что с оператором new совсем другая история? При ошибке, он кидает исключение. Поэтому можно спокойно разыменовывать указатель без проверки, и нету никакого неопределенного поведения. Я прав?
Причем nothrow может быть поведением по умолчанию
Ни в одном комментарии про Linux OOM killer.
Автор, если сможешь получить в Linux от malloc NULL, напиши пожалуйста статью, с интересом прочитаю. С этого наверное и следовало начать.
UFO just landed and posted this here
ок. я просто поиском по странице пытался
тем не менее. буду признателен за рабочий пример получения NULL от malloc
UFO just landed and posted this here
Слегка модифицированный вариант voices.canonical.com/jussi.pakkanen/2013/05/24/malloc-and-linux:

#include<stdio.h>
#include<malloc.h>

int main(int argc, char **argv) {
  long size=1;
  while(1) {
    char *x = malloc(size*1024);
    printf("Tried to alloc: %ldk.\n", size);
    if(!x) {
      printf("Malloc returned null.\n");
      return 0;
    }
    *x = 0;
    free(x);
    size *= 2;
  }
  return 1;
}


$ gcc q.c ; ./a.out
Tried to alloc: 1k.
Tried to alloc: 2k.
Tried to alloc: 4k.
Tried to alloc: 8k.
Tried to alloc: 16k.
Tried to alloc: 32k.
Tried to alloc: 64k.
Tried to alloc: 128k.
Tried to alloc: 256k.
Tried to alloc: 512k.
Tried to alloc: 1024k.
Tried to alloc: 2048k.
Tried to alloc: 4096k.
Tried to alloc: 8192k.
Tried to alloc: 16384k.
Tried to alloc: 32768k.
Tried to alloc: 65536k.
Tried to alloc: 131072k.
Tried to alloc: 262144k.
Tried to alloc: 524288k.
Tried to alloc: 1048576k.
Tried to alloc: 2097152k.
Tried to alloc: 4194304k.
Tried to alloc: 8388608k.
Tried to alloc: 16777216k.
Tried to alloc: 33554432k.
Tried to alloc: 67108864k.
Malloc returned null.

$ uname -a
Linux 4.10.0-42-generic #46~16.04.1-Ubuntu SMP Mon Dec 4 15:57:59 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

$ free
              total        used        free      shared  buff/cache   available
Mem:       82479492    39326248     1789876      854504    41363368    41316992
Swap:      16618364     1884660    14733704
64г одним куском. еще варианты? ради этого писать не демона (к примеру), а звездолет?
Много кусков по 100 мегабайт тоже прокатывают.

ради этого писать не демона (к примеру), а звездолет?

Один if превращает программу в звездолёт?

Много кусков по 100 мегабайт тоже прокатывают.

Нет, не прокатывают.

Для справки, тот пример работает только потому, что vm.overcommit_memory=0 и проверяет он только alloc_size > total_free_ram. Таким образом — убери оттуда free() — ничего не поменяется. Аллоцируй хоть милён раз по 100мегабайт — ничего не изменится. Отвалится потом по хардварным лимитам, но такое попросту невозможно.

Нет, не прокатывают.

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

Слив я могу уже засчитать. Ведь ответа я не получил, как и хоть каких-то оснований вашим словам.
Если это тот персонаж, о котором я думаю, то на linux.org.ru он давно известен. И те, кто его знают, воздерживаются от попыток конструктивного общения с ним. Если же попытаться с ним поговорить как с адекватным человеком, то получается, например, вот так. Можно обратить особое внимание на пассажи вида:
Неверно, никто этого не сделал, да и даже если сделал — из этого ничего не следует. Нигде обратное не утверждалось, да и вообще разговор был не об этом.
И это еще не самый худший образчик его мыслеизложения.
Вы написали «буду признателен за рабочий пример получения NULL от malloc»? Вот вам рабочий пример, разве нет?
Вы не допускаете ошибки в расчёте требуемого объёма памяти, которая может привести к тому, что допустим обычно пытаются выделить 40МБ, но если выбран флаг, который «никто не выбирает», то пытаются выделить 64ГБ? И из-за этого, при попытке записи по смещению 15МБ в выбранный массив будет перезаписываться heap например. Это _абсолютно_ нереалистичный сценарий? А если смещение не 15МБ?

Вероятно инженеры, проектировавшие процессоры тоже думали что временнАя атака на модуль предсказания перехода или спекулятивного исполнения, что это нереалистичный сценарий =)
Последствия такого мышления в виде ~20% увеличения времени исполнения при той же нагрузке я вот как раз наблюдаю у себя в продакшне после патчей против Meltdown =)
Сплошные манипуляции. Первое — почему не убран free()? Он там ничего не делает. С ним мы делаем вид, что всё работает так, как предполагалось — нет. Не работает.

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

И то, это специально закастыленный в ядре кастыль.
Что может быть лучше, чем код, который надеется не только на ОС, но еще на какой-то специфический ее компонент. А если в ядре ваш код, а если в UEFI? Во встраиваемой системе? Для библиотеки общего назначения это раз плюнуть.
Лехко.
Скрытый текст
Allocation 0: 0xb34ae008
Allocation 1: 0xaf4ad008
Allocation 2: 0xab4ac008
Allocation 3: 0xa74ab008
Allocation 4: 0xa34aa008
Allocation 5: 0x9f4a9008
Allocation 6: 0x9b4a8008
Allocation 7: 0x974a7008
Allocation 8: 0x934a6008
Allocation 9: 0x8f4a5008
Allocation 10: 0x8b4a4008
Allocation 11: 0x874a3008
Allocation 12: 0x834a2008
Allocation 13: 0x7f4a1008
Allocation 14: 0x7b4a0008
Allocation 15: 0x7749f008
Allocation 16: 0x7349e008
Allocation 17: 0x6f49d008
Allocation 18: 0x6b49c008
Allocation 19: 0x6749b008
Allocation 20: 0x6349a008
Allocation 21: 0x5f499008
Allocation 22: 0x5b498008
Allocation 23: 0x57497008
Allocation 24: 0x53496008
Allocation 25: 0x4f495008
Allocation 26: 0x4b494008
Allocation 27: 0x47493008
Allocation 28: 0x43492008
Allocation 29: 0x3f491008
Allocation 30: 0x3b490008
Allocation 31: 0x3748f008
Allocation 32: 0x3348e008
Allocation 33: 0x2f48d008
Allocation 34: 0x2b48c008
Allocation 35: 0x2748b008
Allocation 36: 0x2348a008
Allocation 37: 0x1f489008
Allocation 38: 0x1b488008
Allocation 39: 0x17487008
Allocation 40: 0x13486008
Allocation 41: 0xf485008
Allocation 42: 0xb484008
Allocation 43: 0x4047008
Allocation 44: 0x46008
Allocation 45: 0xb77ed008
Allocation 46: 0xbb7ee008
Allocation 47: 0
Cleanup

Скрытый текст
#include <memory.h>
#include <iostream>
#include <vector>
#include <unistd.h>

int main(int, char**)
{
        std::vector<void*> v;
        v.reserve(1024);
        const size_t bufSize = (1<<26);
        for (int i = 0;; ++i)
        {
                usleep(100000);
                void* ptr = malloc(bufSize);
                std::cout << "Allocation " << i << ": " << ptr << std::endl;
                if (ptr == nullptr) break;
                v.push_back(ptr);
        }
        std::cout << "Cleanup" << std::endl;
        for (auto ptr : v)
        {
                free(ptr);
        }
        return 0;
}

Кстати:
Linux ***** 3.16.0-4-586 #1 Debian 3.16.43-2+deb8u2 (2017-06-26) i686 GNU/Linux
Linux ***** 3.4.75-sun7i #35 SMP PREEMPT Sat Feb 8 02:10:31 CST 2014 armv7l GNU/Linux
Отказ в обслуживании это еще ерунда. Отсутствие проверки malloc на NULL вполне и к RCE может привести.
The Linux kernel's memset for the SuperH architecture has this property: link.
К сожалению, это код на незнакомой мне разновидности ассемблера, поэтому я не берусь рассуждать о нём.

Там всё просто. Память заливается вот таким циклом.
	dt	r0  ;декремент счётчика
	bf/s	1b  ;продолжаем пока r0 != 0
	 mov.b	r5,@-r4  ;запись r5 => [r4] с преддекрементом


SH — такой ранний RISC, имеющий delay slot как MIPS или Sparc. Команда mov выполняется параллельно с инструкций перехода.
Почему бы не поставить постинкремент вместо преддекремента, как на 68k или ARM?
Не получится — ведь такой инструкции нет (вернее есть, но не у всех процессоров SH)
www.shared-ptr.com/sh_insns.html

SH2A mov.b R0,@Rn+

Для остальных чипов хитрые японцы сделали лишь команды чтения с постинкрементом, и записи с преддекрементом (очевидно для организации стека)
Заполнять буфер с конца, также как и писать другие алгоритмы «трогающие» адреса памяти с конца на современных архитектурах не следует, потому что современное hardware в основном работает по принципу look-ahead, т.е. кэширует то, что впереди.
А можно распараллелить запись, начав двигаться с начала и с середины, но на половину длины.
Рекомендация загромождать код проверкой воспринимается мной как анти-паттерн, вредная рекомендация.
Обоснование. Одной только проверки недостаточно, если уж проверять, кроме проверки придется еще и в каждом случае решать что делать дальше, а это означает что придется писать дополнительный код, принимать решения, которые не всегда могут оказаться удачными или безошибочными. Это отвлекает, это время и деньги и дополнительные баги/проблемы. Кроме того, этот код будет кодом решающим системные задачи и по смыслу не имеющим отношения к непосредственному алгоритму/ решаемой задаче, вот почему его быть не должно.
Проблема тут в самих системных *nix функциях *alloc(). Они слишком старые, слишком «системные», и предназначались для системных реалий 70х-80х годов.

Как выглядит более корректное решение?
На мой взгляд лучше не использовать эти системные вызовы напрямую. Использовать или другие библиотечные функции, или написать свои обертки и использовать их. Основная задача — оставить в коде только семантику работы с памятью, не загромождая его ничем посторонним.
Это можно обосновать другими словами тем, что хорошо построенные системы разбиваются на слои. Системному слою абстракции, которому принадлежат функции *alloc() — системную логику и системную «ответственность». Пользовательскому слою абстракции — соответственно, свою упрощенную ответственность.
принимать решения, которые не всегда могут оказаться удачными или безошибочными


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

Отличаются ли эти две альтернативы скоростью реализации? Да, секунд на десять. Ну или сколько времени у вас займёт один if.

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

эти системные вызовы

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


Вы не поняли смысл моих слов, видимо воспринимая слова «системные» или «системный код» буквально, system call. А также, можно выдвинуть гипотезу, вы недопонимаете зачем архитектурно выполняется деление между высокоуровневым application-level слоем, и «системным», более низкоуровневым слоем.

Объясняю этот материал «на пальцах».
Высокоуровневый user-level слой ничего не знает ни о каких «платформах». Следовательно, ничего там не может «упасть» и пишется уже «не думая» об этом, не предусматривая «падения». За падения, за ошибки выделения памяти и «платформы» ответственнен именно system-level слой, который и должен по моей прямой рекомендации включать *alloc() функции. Это то, что я порекомендовал выполнить как «обертку».

Соответственно, ваш комментарий хоть и имеет собственную ценность, имеет слабое отношение к сказанному мной и/или не опровергает мой тезис.
Еще раз уточню, во избежание. Деление «application/user-level» и «system-level» не имеет отношения к уровням защиты и os-терминологии. Это лишь указания на распределение ответственности в нашем, доступном коде, это символическое название слоев абстракции, которые вводятся при проектировании.
Объясняю этот материал «на пальцах».
Высокоуровневый user-level слой ничего не знает ни о каких «платформах». Следовательно, ничего там не может «упасть» и пишется уже «не думая» об этом, не предусматривая «падения».


Разыменовывание нулевого указателя в надежде, что
1. Получится сегфолт
2. Компилятор раскроет UB именно так как вы планируете.

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

Я-то как раз и говорю, что у вас высокоуровневый код — и он на платформу и компилятор мог бы и не закладываться. Но закладывается. А зачем, если этого можно избежать одним if'ом?

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

Не делать проверку на null -> закладываться на особенности конкретной версии вашего конкретного компилятора и конкретные реализации библиотек.
Собственно, обратите внимание: автор поста говорит именно о языке, стандарте и явлении языка (UB).
Комментирующие оппоненты: рассказывают про то, что такого не может быть в malloc'е в linux'е (конкретные реализации под конкретные платформы).
Я думаю стоит начать с этого:
Разыменовывание нулевого указателя в надежде, что
1. Получится сегфолт
2. Компилятор раскроет UB именно так как вы планируете.
Уже является закладыванием и на платформу


В упомянутом выше «application-level» слое абстракции никогда не может возникнуть такой ситуации из-за того что нижележащий «system-level» слой не должен допускать ситуацию с возвратом не выделенной памяти. (Грубо говоря, null в application-level не может вернуться в принципе, на это можно рассчитывать).

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

Все остальное в вашем сообщении верно и имеет самостоятельную ценность. Вы совершенно верно сообщаете что рассчитывать на segfault нельзя и это в целом system dependent.
(Грубо говоря, null в application-level не может вернуться в принципе, на это можно рассчитывать).


… и именно поэтому нужно использовать new, а не malloc, если уж пишем на C++.
Кстати, автор об этом упоминал. Мол, в самом коде хромиума такой проблемы не видно, потому что они в основном используют new.

Соответственно, начать надо с того, как у вас вообще тогда может возникнуть разыменование нулевого указателя?


Если вы делаете вовне своего клиентского кода (в аллокатор памяти, например) запрос, который никогда не возвращает нулевой указатель (дёрнув new, или дёрнув malloc и сделав проверку, предлагаемую автором) — действительно, никак.

Если же вы дёргаете malloc — ты вы используете интерфейс malloc'а. В интерфейсе malloc'а написано — «может вернуть null».

Поэтому и имеет смысл избавляться от ситуации, когда в ваш бизнес код приходит UB. Например, сделав все проверки сразу же на месте выделения памяти. Или написав обёртки. Или аллоцируя не через malloc.
В упомянутом выше «application-level» слое абстракции никогда не может возникнуть такой ситуации из-за того что нижележащий «system-level» слой не должен допускать ситуацию с возвратом не выделенной памяти.

Но он допускает. Тут же обсуждается вполне конкретная функция malloc, которая описана в стандарте...

UFO just landed and posted this here
если мы пишем скажем калькулятор, которым пользователь будет пользоваться нажимая в нем кнопки и тот калькулятор грохнется при недостатке памяти (как и половина приложений системы), то смысл лепить те проверки какой? Тут на первый план начинает выходить стоимость разработки ПО: может быть тот калькулятор пока Вы его покроете всеми перфекциями станет не нужен уже никому?


Напоминаю — автор статьи рассказал про то, какое UB бывает, какие у него могут быть эффекты и как одним простым if'ом его избежать.
Пользоваться этим знанием вас никто не заставляет.

большинство программ не пишут

Большинство программ так же содержит ошибки и уязвимости.
Это нормально.

Я не вижу смысла столкнувшись с указанием на какой-то косяк в программе начинать разглагольствовать про «НУ И ЧТО, В БОЛЬШИНСТВЕ ПРОГРАММ ТАК ЖЕ». Иногда стоит исправить. Иногда разумно проигнорировать или отложить. Информация о наличии косяка, возможных последствиях и способах борьбы и предотвращения возникновения аналогичных ошибок (т.е. ровно то, чем является статья) — ценна для принятия обоснованного решения даже если это решение — забить.

UFO just landed and posted this here
Рекомендация загромождать код проверкой воспринимается мной как анти-паттерн, вредная рекомендация.
Это мощно.
Одной только проверки недостаточно, если уж проверять, кроме проверки придется еще и в каждом случае решать что делать дальше, а это означает что придется писать дополнительный код, принимать решения, которые не всегда могут оказаться удачными или безошибочными.
Так ведь это же программирование. Тут нужно думать, решать, обрабатывать различные варианты и т.д. Работа программиста в этом и состоит.

Или сейчас уже принято писать программы на C или C++ без всей этой отвлекающей настоящего мастера рутины?
UFO just landed and posted this here
Самое последнее в данной задаче что Вас будет волновать — это вопрос «как будет вести себя данная программа/утилита в условиях недостатка памяти?»
Было бы очень хорошо, если бы вы не апроксимировали свои заблуждения на весь остальной мир. Нехватка памяти может ничем не отличаться от других ошибок, с которыми сталкивается программа. Например, как невозможность открыть файл.

Или вы в своих C/C++ программах результаты open/read/write так же предпочитаете не проверять?
UFO just landed and posted this here
я говорю: перфекционируя — надо перфекционировать до конца: начали проверять malloc, проверяем и open (я выше писал об этом несколько раз).
Вообще-то это не перфекционизм, это нормальная, обычная работа программиста: выполнил действие, которое может завершиться неудачно, проверь результат. В зависимости от этого принимай решение о дальнейших действиях.

Другой подход к делу, т.е. программирование в расчете только на благоприятный сценарий, в мире C/С++ возможен только при написании quick-and-dirty прототипов «на выброс». Может быть где-то в мире Erlang-а, Ruby или JavaScript-а по-другому, а здесь вот так.
там где это не нужно предпочитаю не проверять. и не тольк в C/C++, но и на других языках.
Очень надеюсь, что мне не придется иметь дело с написанным вами софтом.
UFO just landed and posted this here
давайте еще раз с примером задачи с градиентным спуском поразбираемся?
Задачи, как таковой, не было. Есть что-то в вашей голове, о чем я не имею ни малейшего понятия. Было бы ТЗ, можно было бы о чем-то говорить. Но даже если вы и сможете выкатить подробное ТЗ, то у меня и без этого есть чем заняться.
то вопрос: нужно ли заморачиваться неблагоприятным сценарием?
Конечно. Я вам даже больше скажу, это вопрос дисциплины. При должной дисциплине стоимость минимальной обработки неудачных результатов совершенно невысока. Особенно, если не брать C, а ограничиваться C++ с возможностью использования исключений.
UFO just landed and posted this here
Простите мне мой резкий тон, но мне очень жаль времени, которое я на вас вынужден тратить. Есть сильное ощущение, что к программированию (особенно на C и С++) вы никаким боком не относитесь. Может быть учитесь. Посему вам кажется, что дать ссылку на описание задачи коммивояжера — это дать постановку задачи. Боюсь вас огорчить, но это вовсе не так.

А т.к. вы к программированию имеете отношение постольку-поскольку, то предметно с вами разговаривать вряд ли возможно. Т.к. вы просто не понимаете предмета разговора. Причем как на уровне собственно программирования (тупо if-ы в коде), так и на уровне того, что хочет заказчик и что его интересует.
UFO just landed and posted this here
UFO just landed and posted this here
давайте еще раз с примером задачи с градиентным спуском поразбираемся? (выше)


Давайте. Много весёлых моментов испытал с переписыванием кода всяких лингвистических числодробилок, написанными людьми на плюсах из соображений «ну пусть пишется, как пишется, ой, что значит, что у нас при определённых условиях nan'ы повылезали отовсюду?».

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

Энивер, действительно, сценариев, когда нужно быстро прототипировать — много.
Лучше в таких случаях использовать более устойчивые к выстрелам себе в ногу инструменты (т.е. аккуратнее с C++).
И даже если принимается решение говнокодить весело, отважно, бесрассудно — лучше понимать, в каком моменте ты оставил для себя грабли (в виде того же UB, про который рассказал автор), что делает знания, сообщённые автором, безусловно ценными.
UFO just landed and posted this here
тут проблема не перфекционизма а отсутствия автоматического тестирования :)


Ого! То есть, теперь покрыть код тесткейсами (да ещё и заранее придумать такие кейсы, которые, собственно, выстрелили только через годы непрерывной работы с кучей данных) — это не вопрос, который требует затраты времени. А вот один if поставить, чтобы UB убрать — это ужас-ужас, теряем конкуретное преимущество.

И кто ещё из нас максималист? ;)
UFO just landed and posted this here
то есть не надо смешивать способ достижения цели с определением цели и все будет хорошо :)


Избегание UB — такая же часть современной парадигмы разработки на С++, как и использование идиом вроде RAII.

автоматические тесты — способ написания ПО

Не тесты, сами тесты не обязательно связаны со способом разработки.
Вот TDD — это способ написания ПО. А вот просто написание каких-то автоматических тестов — это уже реализация.

Интегрировать TDD куда-либо (особенно в наколоченные числодробилки, которые зачастую пишутся не то что бы инженерами) — дороже, чем написание if'а после malloc'а. Я абсолютно серьёзен, хотя тесты искренне уважаю и люблю.
UFO just landed and posted this here
Это мощно.


Каков должен быть ответ на «мощно» — я не знаю.
Уместно ли делать такие комментарии в технической дискуссии?

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

Тут нужно думать, работа программиста в этом и состоит.


Вот и подумайте, что именно предлагается, если вы, конечно, разработчик.
То что я предложил находится на чуть более высоком уровне, архитектурном. Возможно просто «программист» этой проблемы не видит/не понимает, думая что выходом является поставить везде copy-paste проверки…

Или сейчас уже принято писать программы на C или C++ без всей этой отвлекающей настоящего мастера рутины?


Возможно вас это удивит, но — да. К этому всегда стремятся…
Я описал как эта проблема должна быть решена наиболее универсальным и наиболее разумным способом.
Могли бы ткнуть пальцем? А то кроме пространных и абстрактных рассуждений о старости системных вызовов в unix-ах вообще и *alloc-ов в частности ничего не было. Никакой конкретики. Ну или я не видел, комментариев здесь много, за всеми не уследить.
Вот и подумайте, что именно предлагается, если вы, конечно, разработчик.
Временами еще разработчик. И вот думаю-думаю, и глубину вашей мысли не постигаю. Могли бы вы объяснить ее на примерах кода, для тех, кто «от сохи» и в абстрактной архитектуре как-то не очень?
Возможно вас это удивит, но — да.
Очень сильно удивит. А вам ведь не составит труда на примерах кода показать, как это оно?
Очень сильно удивит.


Если вас удивляет тезис того что программисты постоянно стремятся снизить себе трудоемкость работы — тогда ответьте (самому себе, мне не надо) на вопрос — зачем создаются библиотеки? Зачем добавляется syntactic shugar?

А вам ведь не составит труда на примерах кода показать, как это оно?


А ведь составит. Мне это затратно, тратить время на вас лично.
Изучайте существующие кодовые базы, а не пользуйтесь уловкой «а вот покажите мне пальцем». В простейшем случае изучите поведение/реализацию operator new в языке c++ (на предмет проверки и exeption) или что происходит в managed-языках.
Там все происходит именно так, как это вам описано — пользователь языка генерально доверяет распределению памяти, нет проверок по месту на то что аллокация произошла/не произошла.

Объясняю предельно примитивно.
Предлагается не втыкать везде проверки после malloc(). Это грязь. Это нарушает ряд высокоуровневых принципов программирования/проектирования, когда у вас код, выполняющий некую функцию должен быть определен в одном месте, и не должен повторяться. Кроме того, это чревато другим, тем что вам придется еще и писать код обрабатывающий проверки, (возможно освобождающий уже распределенную память), и отсюда потенциально — возникновение новых ошибок.

Предлагается выполнить эту проверку в одном месте, сразу после вызова malloc(), оформить это как функцию/класс/макро, назначив этому функционалу обязанности «системного слоя» и далее уже писать свой application код в надежде на то что в application-level уровне абстракции память валидна всегда, не может быть ситуации когда память не возвращена, и, соответственно, UB не может возникнуть в принципе.

Вопрос который остался за кадром — это что делать на системном слое сразу после вызова *alloc, если память не вернулась? Все поведение malloc() в оригинале предназначалось для GE-645 с которым работали в Bell Labs и который являлся мультизадачным mainframe с виртуальной памятью. Не исключено что в оригинале можно было подождать :) пока память появится. В данном же случае можно либо завершать процесс, выводить сообщение пользователю и ждать, либо предусмотреть какое-либо другое поведение, но это уже детали.
UFO just landed and posted this here
Там все происходит именно так, как это вам описано — пользователь языка генерально доверяет распределению памяти, нет проверок по месту на то что аллокация произошла/не произошла.
Хочу вас разочаровать. Когда разработчик имеет дело с исключениями вместо кодов возврата, необходимость думать и принимать решение никуда не исчезает. Там есть свои проблемы, например, позаботиться об exception safety.
Предлагается выполнить эту проверку в одном месте, сразу после вызова malloc(), оформить это как функцию/класс/макро, назначив этому функционалу обязанности «системного слоя» и далее уже писать свой application код в надежде на то что в application-level уровне абстракции память валидна всегда, не может быть ситуации когда память не возвращена, и, соответственно, UB не может возникнуть в принципе.
Уж простите мне мой французский, но какая глубокая мысль. Видимо, мосье теоретик и архитектор-астронавт?

Иначе как объяснить вот эту вот «досадную мелочь»:
В данном же случае можно либо завершать процесс, выводить сообщение пользователю и ждать, либо предусмотреть какое-либо другое поведение, но это уже детали.
Получается в точности как в народной мудрости: гладко было на бумаге, но забыли про овраги. На словах все красиво про уровни абстракции, а как доходит до практики, то «это уже детали».
UFO just landed and posted this here
эта необходимость уменьшается. раньше ему надо было думать на каждом вызове.
теперь можно весь стек вызовов охватить одним try/catch и не париться особо.
Попробуйте сделать тот же std::vector::push_back/emplace_back с обеспечением exception safety. Посмотрим, придется вам париться или нет.
Хочу вас разочаровать. Когда разработчик имеет дело с исключениями вместо кодов возврата, необходимость думать и принимать решение никуда не исчезает. Там есть свои проблемы, например, позаботиться об exception safety.


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

Уж простите мне мой французский, но какая глубокая мысль. Видимо, мосье теоретик и архитектор-астронавт?


Зачем вы хамите, переходя на личности? Если у вас отсутствуют аргументы — это не повод к Ad hominem.

Получается в точности как в народной мудрости: гладко было на бумаге, но забыли про овраги. На словах все красиво про уровни абстракции, а как доходит до практики, то «это уже детали».


Вы не правы.
Построить обертку и не использовать malloc() повсеместно (т.е. на application-level уровне) — это именно то, что следует сделать. В чем тут проблема и какие тут «овраги»?

Итого, никаких аргументов против простейшего решения вы не привели.
Более того, вы отчаянно сваливаете техническую дискуссию в фанбойский балаган.
В легко достижимом идеале пишется код реализующий только свою задачу, не отвлекаясь на посторонние детали, такие как проверки выделения памяти, реализация exception safety, мультипоточности и проч.
Простите, а вы точно к разработке на C или C++ имеете отношение?
Зачем вы хамите, переходя на личности? Если у вас отсутствуют аргументы — это не повод к Ad hominem.
А вы начните предметно разговаривать, без воспарений в высокие абстракции на счет каких-то слоев, которые волшебным образом решают проблемы неудачных операций (хоть вызовов malloc, хоть чего другого). Тогда можно будет поговорить о решениях. Пока же есть ощущение, что вы живете не в мире реального программирования, а в какой-то параллельной вселенной, в которой легко достигается абстрагирование от многопоточности, exception safety и пр.

Другими словами: покажите, что с вами есть о чем говорить. Что вы не теоретик и архитектор-астронавт.
Построить обертку и не использовать malloc() повсеместно (т.е. на application-level уровне) — это именно то, что следует сделать. В чем тут проблема и какие тут «овраги»?
Ну так покажите, какую обертку вы предлагаете делать. Тогда можно будет сказать, где эта обертка будет работать, а где нет. Но вместо этого вы говорите про какие-то system-level и application-level. А действительно важные вещи, которые влияют и на то, и на другое, это у вас: «уже детали».
Простите, а вы точно к разработке на C или C++ имеете отношение?

Какая вам разница? Почему вы в сугубо технической дискуссии неоднократно переходите на личность человека?

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


Волшебным?
Сошлитесь на мой фрагмент текста, который для вас непонятен и объясните что там для вас «не предметно» и «волшебно».

Тогда можно будет поговорить о решениях. Пока же есть ощущение


С «ощущениями» и «чувствами» это не ко мне. Я только по технической и архитектурной части.

Ну так покажите, какую обертку вы предлагаете делать. Тогда можно будет сказать, где эта обертка будет работать, а где нет.


Вы троллите? Что тут может быть «где эта обертка будет работать, а где нет»?

//  Call it "System level"
void* checked_malloc(size_t sz) {
    void* ptr = malloc(sz);
    if (!ptr) {
        exit(YOUR_ERROR_CODE);
    }
    return ptr;
}

// Call it "application level"
int main() {
    void* mymem1 = checked_malloc(YOUR_SIZE1); 
    // Here I fully trust mymem1, no need for checking
    
    void* mymem2 = checked_malloc(YOUR_SIZE2); 
    // Here I fully trust mymem2, no need for checking

    free(mymem2);
    free(mymem1);
    return 0;
}


— Концепция ясна? Это же сверх-тривиально, как тут что-то может быть неясно, что тут требуется иллюстрировать?

Но вместо этого вы говорите про какие-то system-level и application-level.


Это простейшее разбиение на логические слои. Почему вы не можете мне просто написать: «Простите, я не совсем понимаю что вы имеете в виду. Можете мне объяснить?»

А действительно важные вещи, которые влияют и на то, и на другое, это у вас: «уже детали».


Вы не правы.
Решение принимаемое в каждой конкретной программе в случае нехватки памяти не имеет значения (выше это тривиальный вызов exit() ). Мы обсуждаем только узкий контекст необходимости проверки аллокаций памяти. Всё что вне этого контекста — не информативно.
Какая вам разница?
Огромная. Когда в тему о проблемах использования malloc в C и C++ приходят теоретики, ссылающиеся на опыт управляемых языков, то разговаривать, как правило, можно только о личностях, которые до такого додумываются.
Сошлитесь на мой фрагмент текста, который для вас непонятен
Это вы троллите? Я вам уже сказал, что до сих пор вы вообще не сказали ничего конкретного, что можно было бы предметно обсуждать. Вот только сейчас вы привели пример кода. Из которого уже видна степень вашей экспертизы.
Я только по технической и архитектурной части.
Пока что это ничем не подтверждено.
— Концепция ясна?
Более чем.
Это же сверх-тривиально, как тут что-то может быть неясно, что тут требуется иллюстрировать?
Отличная иллюстрация того, что вы не понимаете сложности и широты темы, о которой идет речь. Если такой checked_malloc будет в какой-нибудь библиотеке для парсинга хитрого формата файлов, то использовать библиотеку с подобным checked_malloc будет ой как стремно, скажем, в многооконном офисном приложении.
то разговаривать, как правило, можно только о личностях, которые до такого додумываются.


Я считаю что о вашей деятельности нужно уведомить модераторов. Когда человек переходит на личности нечаянно, это одно дело. Но когда он вбил себе в голову и раздает всем вокруг ярлыки, еще и ОБОСНОВЫВАЯ свое хамство — это совершенно другой случай.

Евгений, вы не правы во всем вашем выступлении.

Моя тривиальнейшая мысль заключалась в том что в коде желательно писать не напрямую библиотечный malloc() с повсеместной проверкой возвращаемого значения, но его собственную замену/обертку — совершенно верна. Это азы. Это тривиальщина.

У этого, кроме озвученных выше есть дополнительные преимущества, например такие что не придется выполняя рефакторинг, бегать по коду и менять все места при желании что-либо сделать с аллокацией памяти, будь то ввод дополнительного memory guard'а, логгинг аллокаций или даже простейший breakpoint, который удобно поставить внутри обертки.

Вы никак мои слова не опровергли. Вот именно от вас ПО СУЩЕСТВУ никто тут ничего интересного не услышал, впустую потеряв время на вычитку ваших бессмысленных текстов.

И вместо этого, будучи изначально неправым вы:
а) Совершаете атаку на мою личность, занимаясь на Habrahabr выяснением кто якобы © «лучше знает XYZ а кто не знает».
— это очень нехорошо.

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

ORLLY? Тривиальный рефакторинг будет иметь проблемы? Чего? Обертка malloc()'а доставит какие-то практические проблемы? Конечно же это не так, и это ложь, бред, потеря реальности.

Я свою задачу выполнил и умываю руки, так как дальнейшее не обогащает меня знанием но является пустой растратой времени. А вот вы можете взять на себя обязательства доказать якобы практическую несостоятельность решения. Дерзайте, занимайтесь опровержением 2+2. Докажите, раз вы в этом так заинтересованы. Но с моей точки зрения это заведомо полнейшая глупость.

Вынужден разорвать бессмысленную дискуссию.
Давайте я вам напомню, как развивались события. Вы написали нечто, с чем сложно согласиться. Это заставило задать вам уточняющий вопрос. Но вместо того, чтобы на него ответить, вы завели шарманку в стиле:
Вот и подумайте, что именно предлагается, если вы, конечно, разработчик.
Далее я вас попросил говорить более предметно (т.к. и в ответах другим участникам дискуссии вы не утруждали себя конкретными примерами). На что вы продолжили играть на той же шарманке:
Мне это затратно, тратить время на вас лично.
Изучайте существующие кодовые базы, а не пользуйтесь уловкой «а вот покажите мне пальцем». В простейшем случае изучите поведение/реализацию operator new в языке c++ (на предмет проверки и exeption) или что происходит в managed-языках.
И продолжали играть до тех пор, пока из вас таки не удалось выжать хоть какой-то пример кода.

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

Нежелание человека разговаривать о значимых деталях наводит на подозрение о том, что человек не является действующим разработчиком (по крайней мере действующим C или C++ разработчиком), об этих-то подозрениях и приходилось вам мягко намекать.
А давайте я взамен тонны вот этого запредельного школьного хамства:
eao197 Это мощно.

eao197 Уж простите мне мой французский, но какая глубокая мысль. Видимо, мосье теоретик и архитектор-астронавт?

eao197 вы не понимаете сложности и широты темы, о которой идет речь

eao197 подозрение о том, что человек не является действующим разработчиком

eao197 покажите, что с вами есть о чем говорить. Что вы не теоретик и архитектор-астронавт.


Отпишу доступным вам языком? Ну-ка брысь курить xmalloc!
Потому что весь этот школьный бред просто не мог бы возникнуть, если бы eao197 хотя бы подозревал о его существовании.

eao197 Ну не подходит предложенный вами вариант checked_malloc для ряда случаев.

Жгите ещще красавчик, я польщен глубиной беседы и вашими «выводами» о иллюстративном коде, набитом за пару минут.
© Ге-ни-аль-но!
вашими «выводами» о иллюстративном коде, набитом за пару минут.
Так как прикажете воспринимать приведенный вами здесь код?

Как демонстрацию ваших рассуждений о слоях абстракции? Или как не имеющую отношения к теме иллюстрацию, на которую не стоит обращать внимания?

Если это всего лишь иллюстрация, то когда будет хоть какая-то конкретика, которую можно обсуждать предметно? Или обсуждать останется только вашу тонкую душевную организацию?

Я вас уже который комментарий прошу: давайте ближе к теме.
Евгений, вы присмирели, благодарю вас. Таким вы мне нравитесь. :)

Вы мне только что написали:
Ну так покажите, какую обертку вы предлагаете делать. Тогда можно будет сказать, где эта обертка будет работать, а где нет.


Я вам объяснил идею словами и привел иллюстративный пример кода. Кто знает? Может у вас реально проблемы с пониманием русского языка, действительно, бывают такие люди. Не проблема, я пошел вам навстречу набросав простейший пример в качестве иллюстрации… эээ… «архитектурным решением» называть это слишком громко, пусть будет «рефакторинга».

На это вы выдали в эфир совершенно пустое рассуждение (по смыслу никак не опровергающее и не подтверждающее мой исходный тезис):
Ну не подходит предложенный вами вариант checked_malloc для ряда случаев.
Ну очевидно не подходит, а что должен? Это как-то отменяет саму идею, сказанное в самом первом сообщении, как-то ПРИНЦИПИАЛЬНО запрещает заменять malloc на собственную обертку, или что? В чем проблема-то? Вы это никак не прояснили. И вот вы пишете:
Так как прикажете воспринимать приведенный вами здесь код? Как демонстрацию ваших рассуждений о слоях абстракции? Или как не имеющую отношения к теме иллюстрацию, на которую не стоит обращать внимания?

Хахахаха. Я после этой фразы пытаюсь понять, чего вы добиваетесь. Реально, уже думаю о нехорошем. :)
Если это всего лишь иллюстрация, то когда будет хоть какая-то конкретика, которую можно обсуждать предметно? Или обсуждать останется только вашу тонкую душевную организацию? Я вас уже который комментарий прошу: давайте ближе к теме.

Конкретика?
А какая вам «конкретика» тут нужна?
Ну вот вам написали, не используйте malloc, заменяйте враппером, не замусоривайте код проверками, это решение несколько получше, вон даже на *nix ах без проблем существует аналогичный xmalloc. Даже продемонстрировали примерный код. Куда уж конкретней, Евгений? Приведите пример своей «конкретики»?

Ну и давайте все-таки вернемся к исходной посылке, ибо вы не продемонстрировали какого-либо суждения по существу вопроса.
Исходное предложение до абсурдного простое, я и другие люди не видим что там ВООБЩЕ можно обсуждать. Но вы, видимо, усматриваете какие-то концептуальные проблемы. Вот только рассказать не успели, всё больше требуя от меня. Я ваши требования выполнил.

Жду теперь от вас наконец какого-то разумного опровержения или возражения по существу сказанного в исходном сообщении Подчеркиваю, жалобы на якобы «не конкретику» я уже слышал. Код привел, объяснения дал. На xmalloc сослался. Давайте теперь Евгений вашу здравую аргументацию в ответ на исходный пост.

И у меня к Вам будет предложение. А давайте с вашей помощью поставим на хабре рекорд абсурда и войдем в историю? Ну например, я напишу слова «оператор сравнения», а вы возьмете на себя функцию раз десять написать «давайте конкретику»? Я вам уже и код
if (x) {}
приведу, а вы всё знай будете требовать «конкретики», да «конкретики»? Давайте, а? Чего уж там, гулять так гулять абсурд так абсурд! :)
Опять графоманские потоки вместо обсуждения технических деталей. Сильно не вяжется с приснопамятным:
Я только по технической и архитектурной части.
Скорее я тут вынужден разговаривать не с архитектором-астронавтом, а с теоретиком-графоманом.

Поскольку вы ссылаетесь еще на кого-то («я и другие люди не видим что там ВООБЩЕ можно обсуждать») то придется потратить время дабы объяснить вам и мифическому еще кому-то очевидные вещи. Очевидные для тех, кто не отмахивается посредством «это уже детали», как вы.

Итак, предложенная вами идея оберток вокруг malloc-а, во-первых, работает только тогда, когда обертка имеет возможность прервать выполнение кода после возникновения ошибки тем или иным способом. Вызовом abort/exit/terminate, или выбросом исключения, или эмуляцией исключения на подручных средствах. Или даже goto error, если «предлагаемая» вами обертка реализована в виде макроса.

Проблема здесь в том, что это не всегда работает. Безусловное прерывание программы (abort/exit/terminate) может быть приемлемым для утилит типа grep, wc или xargs, но совершенно не приемлем для библиотек вроде libxml2, zlib или libcurl. Так же это не приемлемо для больного класса приложений, скажем офисных пакетов или СУБД.

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

Как должна вести себя обертка вокруг malloc в условиях, когда прервать исполнение кода нельзя (например, внутри libxml2 или libcurl)?

Эта обертка будет вынуждена возвращать результат операции, в том или ином виде. И не суть важно, будет ли этот результат возвращаться в виде голого указателя (что нам придется делать, если мы находимся в чистом C) или в виде какой-то обертки, вроде std::optional или folly::Expected. Все равно мы будем вынуждены этот результат проверять. А раз так, то вопрос «нужно ли проверять коды возврата malloc?» для большого количества прикладных ниш, где применяются C/C++ превратиться в «нужно ли проверять коды возврата обертки над malloc?». Получаем то же самое, вид в профиль.

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

Я же вам написал — давайте теперь по существу поставленного вопроса. А вопрос у нас по существу такой: Автор статьи, Andrey2008 объяснил почему результат вызова malloc() проверять надо, и аргументировал это в общем случае UB в случае отказа функции вернуть память и возврата null pointer. Я на это (пишу раз в десятый) аргументировал тем, что не совсем красиво будет ставить везде проверки, и что это функцию лучше обернуть проверкой, создав условие что null pointer никогда не вернется, также как это делает xmalloc.

Вы начинаете уводить в сторону, вместо обсуждения вопросов Андрея и моего, рассказывать постороннее, что в общем случае нельзя реализовать универсальную обработку случая отказа выделения памяти, о чем вам было неоднократно уже сказано:
Вопрос который остался за кадром — это что делать на системном слое сразу после вызова *alloc, если память не вернулась? Все поведение malloc() в оригинале предназначалось для GE-645 с которым работали в Bell Labs и который являлся мультизадачным mainframe с виртуальной памятью. Не исключено что в оригинале можно было подождать :) пока память появится. В данном же случае можно либо завершать процесс, выводить сообщение пользователю и ждать, либо предусмотреть какое-либо другое поведение, но это уже детали.
Конечно же нельзя предусмотреть абсолютно универсальную, общую функцию для абсолютно любых случаев в жизни. Если уж на то пошло, следует реализовать callback на желаемую функцию обработки ошибки и дело в шляпе. Но это не является поставленным вопросом, это офтопик, совершенно посторонняя деталь. Вопрос тут:
а) Тема начатая автором статьи
б) Мое дополнение (см. курсив выше)
Вот их и следует обсуждать, не уводя в сторону на несущественные по смыслу детали.

В связи с чем ваша попытка ответа не засчитывается, Евгений, пытайтесь еще. Ждем ответа от Вас на поставленный вопрос по-существу (выделено выше курсивом), ответ вы пока не дали, отделавшись отпиской.
1. Вы не понимаете, о чем мы с вами разговариваем и почему-то ждете ответов от меня, хотя это вам нужно отвечать за свои убеждения и рекомендации.

2. Вы настолько тупы, что не можете скопипастить мое имя и фамилию правильно из профиля. Это полностью объясняет проблемы с п.1.

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

Эта обертка будет вынуждена возвращать результат операции, в том или ином виде. И не суть важно, будет ли этот результат возвращаться в виде голого указателя (что нам придется делать, если мы находимся в чистом C) или в виде какой-то обертки, вроде std::optional или folly::Expected. Все равно мы будем вынуждены этот результат проверять.


— Обратим внимание на ваши слова, Евгений, «вынуждена возвращать результат», «будем вынуждены этот результат проверять».

Это, наверное, у Вас как раз тот случай, когда человек понимает мелочи из C++ (std::optional, итд) но не понимает самую важную суть предлагаемого в разговоре, простейшее архитектурное и алгоритмическое решение. Но при этом как надуты щеки! :)

Еще раз поясняю специально для вас Евгений, суть предложения, что и зачем делается. Читайте внимательно!

malloc() заменяется на свою функцию-обертку.
Зачем это делается? Да затем, чтобы результату её вызова доверять в своем коде ВСЕГДА. Почему это можно делать?

Обертка внутри себя проверяет результат вызова malloc(), и в случае возврата malloc()'ом значения null pointer, обертка НИКОГДА не вернет в вызвавший её код null pointer, сразу предприняв какую-то обработку, которую можно задать универсальным callback'ом или прямиком прописав в данном конкретном случае данной конкретной обертки. Она просто не вернется назад. Всё, краш, fail. Памяти нет. Отказ.

А следовательно, вам не надо каждый раз писать в коде проверку результата вызова обертки.

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

ПОВТОРЯЮ :)
В чем плюс? В чем смысл всего этого?
В основном в том, что null pointer в вызываемый код не вернется НИКОГДА, и соответственно, результату вызова можно всегда полностью доверять. А это значит, что его НЕ НАДО ПРОВЕРЯТЬ. Это ровно противоположно тому что вы думаете и пишете.

Тем самым проблема постоянной проверки возвращаемого значения (и каких-либо дополнительных действий) уходит. В «клиентский» код (вызывающий обертку) никогда не может вернуться null pointer, и поэтому и проверять не надо и undefined behaviour не возникнет. Всё, проблема решена. Это понятно?

Надеюсь Вы всё поняли, Евгений eao197? Потому что если это не так, это фиаско, братан! Это фиаско! :)

Как и прежде, жду от вас дельных комментариев по-существу поставленного вопроса.
Обертка внутри себя проверяет результат вызова malloc(), и в случае возврата malloc()'ом значения null pointer, обертка НИКОГДА не вернет в вызвавший её код null pointer, сразу предприняв какую-то обработку, которую можно задать универсальным callback'ом или прямиком прописав в данном конкретном случае данной конкретной обертки. Она просто не вернется назад. Всё, краш, fail. Памяти нет. Отказ.
Опять 25. Вы хоть понимаете, что библиотека, позволяющая себе подобные вольности будет просто выкинула и замена чем-то другим? Потому что ей просто нельзя пользоваться в условиях, когда памяти не хватает (а когда её заведомо хватает и атак на вашу программу не ожидается, то никакие обёртки, собственно, не нужны — можно просто вызывать malloc и «надається на лучшее»)

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

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

Надеюсь Вы всё поняли, Евгений eao197? Потому что если это не так, это фиаско, братан! Это фиаско! :)
Да что ж вы опять куда-то в стратосферу устремились-то? Какой выигрыш может быть от подобного подхода — понятно всем. А вот какие могут быть побочные эффекты — нет. А они ведь разные, в зависимости от реализации вашей обёртки. В каких-то случаях они приемлемы, в каких-то — нет. Вот о чём разговор, вот почему от вас требуют конкретики. И не в виде «псевдокода в комментариях» (любимое занятие астронавтов), а виде работающего кода!
Опять 25. Вы хоть понимаете, что библиотека, позволяющая себе подобные вольности будет просто выкинула и замена чем-то другим? Потому что ей просто нельзя пользоваться в условиях, когда памяти не хватает (а когда её заведомо хватает и атак на вашу программу не ожидается, то никакие обёртки, собственно, не нужны — можно просто вызывать malloc и «надається на лучшее»)


Я понимаю, полнолуние, но все же, с чего вы взяли что всенепременно следует говорить о библиотеке, и что вам мешает реализовать callback?

В основном, понятно, нужно обсуждать «цену» такой обёртки (дополнительную память, влияние на код и прочее) — но пока примера реализации нет


И не в виде «псевдокода в комментариях» (любимое занятие астронавтов), а виде работающего кода!


Час моей работы стоит $50 и меньше чем на 20 часов я не размениваюсь. Предоплата в столь мелких случаях 100%. Как будете оплачивать?

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

Чукча не читатель, чукча — писатель?

Вы статью-то вообще читали, которую мы тут обсуждаем? И именно в этом контексте идёт всё обсуждение.

Всё что здесь обсуждается — это проблемы только и исключительно библиотек.

Цитата:
Но прежде надо ответить на вопрос: «а причём здесь Chromium?».

Chromium здесь при том, что в используемых в нём библиотеках имеется не менее 70 ошибок, связанных с отсутствием проверки после вызова таких функций, как malloc, calloc, realloc. Да, в самом Chromium эти функции почти нигде не используются. В Chromium применяются только контейнеры или operator new. Однако, раз ошибки есть в используемых библиотеках, то значит, можно сказать, что они есть и в Chromium. Конечно, какие-то части библиотек могут не использоваться при работе Chromium, но определять это сложно и ненужно. Всё равно надо править все ошибки.


и что вам мешает реализовать callback?
А вот с этого момента обсуждение, собственно, и стоит начать. Покажите пример кода, который предлагает использовать — и давайте его сравним с банальной проверкой ошибок. Не какие-то «сферические принципы в вакууме», а реальный код, который реально может захотеться засунуть в биболиотеку. Не обязательно приводить его здесь — ссылка на реализацию где-нибудь на GitHub'е тоже пойдёт.

Час моей работы стоит $50 и меньше чем на 20 часов я не размениваюсь. Предоплата в столь мелких случаях 100%. Как будете оплачивать?
Вас понял. Диагноз — «архитектурный астронавт, на работу не брать ни под каким соусом» принят и зафиксирован.

Потому что отговорки подобного плана — это как раз типичный признак: говорить умные слова мы умеем, писать код — нет. И неважно даже: вы считаете, что вы «переросли» этот этап и теперь можете работать «учёной совой» и «разрабатывать стратегию» или никогда не умели… важно что сейчас вы код не пишите — иначе пример того, во что вы так свято верите не требовал бы 20 часов работы, а занял бы 5 минут копи-паста.
Чукча не читатель, чукча — писатель?

Вы статью-то вообще читали, которую мы тут обсуждаем? И именно в этом контексте идёт всё обсуждение.

Всё что здесь обсуждается — это проблемы только и исключительно библиотек.


Обсуждаются здесь не «проблемы библиотек», как это может понять только странный человек, а проблема обязательности проверки malloc-подобных функция на результат вызова, и обоснование почему не проверять нельзя. Во-первых, автором указано что проверялся сам Chromium, но в нем не оказалось проблем, поскольку он, как это и предполагалось, имеет чисто C++ код. Всё, точка, ваша карта бита. А далее, анализируя Chromium, чтобы поиметь хоть какой-то улов, автор переходит к массе библиотек (а также вступал в диалог с автором библиотеки, да и советы дает автором библиотек), однако же делать отсюда алогичный вывод что это проблема якобы «ТОЛЬКО библиотек» — нельзя, это вывод по типу «посчитал количество слов, сделал вывод», смотрю в книгу — вижу фигу.
Ну я лично, будучи в здравом уме и твердой памяти такого вывода о ТОЛЬКО библиотеках не сделал. Речь идет о любом коде вообще, поскольку функции аллокации памяти встречаются везде где угодно.

А вот с этого момента обсуждение, собственно, и стоит начать.


То есть до этого это был с вашей стороны трёп?

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


Я-то покажу, а вот мне хотелось бы знать, с каких это щщей вы в разговоре общаетесь со мной личными повелениями — А ПОКАЖИТЕ-КА МНЕ? Вам не кажется это нахальством или троллингом?

Вас понял. Диагноз — «архитектурный астронавт, на работу не брать ни под каким соусом» принят и зафиксирован.


Нет это я вас понял, вы хам. Вы обзываетесь и наклеиваете ярлыки. Это хамство запредельных масштабов, вы с чего взяли что можете общаться в таком ключе?

Потому что отговорки подобного плана — это как раз типичный признак: говорить умные слова мы умеем, писать код — нет. И неважно даже: вы считаете, что вы «переросли» этот этап и теперь можете работать «учёной совой» и «разрабатывать стратегию» или никогда не умели… важно что сейчас вы код не пишите — иначе пример того, во что вы так свято верите не требовал бы 20 часов работы, а занял бы 5 минут копи-паста.


Её-мое. Да что же это такое тут творится?
Да какая вам вообще разница, кто я? Обсуждайте идеи, обсуждайте события, но не людей! См. высказывание Элеоноры Рузвельт: «Великие умы обсуждают идеи. Средние умы обсуждают события. Мелкие умы обсуждают людей.»
Постыдились бы!

Далее я уже второй раз привожу свой код, а вы только трепете языком и наезжаете на меня с хамством.

// main.c
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include "testmemlib.h"

static int test_function() {
    const int TEST_MEM_SZ = 16;

    unsigned char *mem1 = NULL;

    MALLOC_TRY 
		mem1 = (unsigned char *) test_malloc(TEST_MEM_SZ);
    MALLOC_EXCEPT {
        printf("memory allocation error!\n");
        return 0;
    };

    // some memory usage, dump contents
    {
        int i;
        printf("memory dump: ");
        for (i = 0; i < TEST_MEM_SZ; i++)
            printf("%02X,", mem1[i]);
        printf("\n");
    }

    test_free(mem1);
    return 1;
}

int main() {
    printf("begin test\n");
    if (test_function())
        printf("test completed.  well done!\n");
    else
        printf("test failed.\n");
    return 0;
}

// testmemlib.c
#include <stdlib.h>
#include <setjmp.h>
#include "testmemlib.h"

jmp_buf malloc_buf123;

void *test_malloc(size_t sz) {
    void *ptr = malloc(sz);
    if (!ptr) {
        longjmp(malloc_buf123,1);
    }
    return ptr;
}

void test_free(void *ptr) {
    free(ptr);
}

// testmemlib.h
#ifndef TESTMEMLIB
#define TESTMEMLIB

extern jmp_buf malloc_buf123;
extern void *test_malloc(size_t sz);
extern void test_free(void *ptr);

#define MALLOC_TRY    if (!setjmp(malloc_buf123))
#define MALLOC_EXCEPT else

#endif


— Это либка из двух файлов. Родил за полчаса.
Не надо придираться, ныть о частностях и о возможных ошибках, это рождено реально, только что, за полчаса, в основном под воздействием вашего зашкаливающего троллинга. Это не является панацеей, commercial quality grade code и тестировалось методом двух запусков. На большее я пойтить не могу.

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

Интересно, будет ли что-нибудь практическое по существу от вас, нетривиальный вы наш?
Знакома ли вам формальная логика?

Рассмотрим вашу программу. Верно ли в ней утверждение «результат каждого вызова malloc проверяется на 0»? Верно, потому что вызов malloc всего один, и его результат проверяется.

Так с чем же вы спорите-то, ёпрст?
Мне-то это очевидно, почему я свободно и высказался.

Но, видимо, это ясно далеко не всем моим оппонентам.

Я тоже искренне недоумеваю, что тут можно не понимать, и о чем тут можно спорить. Но, вот поди ж ты… Можно :-)
Так спорите-то с этим именно вы…
Приведите фрагмент моего текста, в котором я «спорю» со своим же предложением вынести все malloc'и, заменив их оберткой, и заявляя что это — неравноценное решение.

Самое первое же ваше сообщение:


Рекомендация загромождать код проверкой воспринимается мной как анти-паттерн, вредная рекомендация.

В нем вы, по форме, спорите с автором статьи, а автор статьи писал именно это:


Всегда сразу проверяйте указатель, который вернула функция malloc или аналогичная ей.

Вместо того чтобы оспаривать тот совет которому вы, в итоге, сами же следуете, вам следовало бы написать уточнение. Что-то вроде вот такого:


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

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

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

Прочтите мой исходный комментарий до конца.
В его конце содержится ровно то, чем вы мне пеняете. Я предлагаю сделать (цитирую конец своего комментария):
Как выглядит более корректное решение?
На мой взгляд лучше не использовать эти системные вызовы напрямую. Использовать или другие библиотечные функции, или написать свои обертки и использовать их. Основная задача — оставить в коде только семантику работы с памятью, не загромождая его ничем посторонним.

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

Как и почему это «работает», какие у этого есть небольшие дополнительные преимущества, а также краткую иллюстрацию си-кодом вы найдете в этой ветке комментариев, там же я кратко выделил всю суть дела (повторно цитирую): Andrey2008 объяснил почему результат вызова malloc() проверять надо, и аргументировал это в общем случае UB в случае отказа функции вернуть память и возврата null pointer. Я на это дополнил тем, что не совсем красиво будет ставить везде проверки, и что это функцию лучше обернуть проверкой, создав условие что null pointer никогда не вернется, также как это делает xmalloc.

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

Помимо этого иметь под «рукой» свою замену malloc (а-ля «перехваченный») очень удобно исходя из массы других соображений. У меня это must have.
Ключевая мысль: оставить в коде только семантику работы с памятью, не загромождая его ничем посторонним.
Прекрасно, великолепно, супер! Мыши, станьте ёжиками!

А теперь вспомним, наконец, о том, что мы говорим о библиотеках (в коде Хромиума, где мы можем контролировать всё и вся, разумеется в основном обёртки и используются), то есть у нас есть два дополнительных ограничения:
1. Библиотеки не должна обрушивать всю программу целиком — во многих программах подобное поведение просто недопустимо.
2. Использовать исключения также нельзя, потому что на многих платформах и во многих организациях их использование запрещено.

Что и переводит нашу проблему в практическую плоскость: как, собственно, вы собираетесь писать вашу обёртку, чтобы удовлетворить этим самоочевидным требованиям.

Пока ничего, кроме словоблудия и «махания руками» вы не предложили — а ведь это ключевой момент! Все виденные мною техники очень серьёзно влияют на программу и во многих случаях их использование было менее удобно, чем навешивание атрибута warn_unused_result на malloc и компиляция с -Werror.

Заметьте: в отличие от вас я, как раз, знаю несколько способов это сделать — но также вижу, что это не панацея. Вы, в отличие от меня, категорично заявляете, что это — единственный возможный подход, что немедленно вызывает реакцию Architecture astronautus detected, fire protocol enabled
Что и переводит нашу проблему в практическую плоскость: как, собственно, вы собираетесь писать вашу обёртку, чтобы удовлетворить этим самоочевидным требованиям.

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

Кто вам сказал, что в комментариях под чужой тематической статьёй я вообще, должен предлагать какие-то замечательные универсальные решения? Это вы с чего взяли? От большого ума?

Врать что я ничего не предлагал не надо.
Я предложил то, что посильно сделать в комментарии — самое первейшее решение a-must-have (задел на будущее) — это не обращаться НАПРЯМУЮ к malloc-подобным функциям, предусматривая в качестве задела нечто своё. Это первое что я написал в первом же сообщении! Первый и весьма разумный шаг.

В чем проблема? С чем спорим? Это что, как-то хуже? Нет, в простейшем случае это полностью равноценное решение прямому вызову malloc'а. Но зато чуть позже (вместе с аналогичным использованием своей free) — это даст ряд «вкусностей».

Это простейшее первичное решение «обёртка», вообще говоря, ничто иное, как собственный менеджер памяти. Со всеми вытекающими. В простейшем случае, как в комментарии — он заглушка, чекающая результат malloc. Далее у него можно спросить хэндл закладки для серии аллокаций с тем чтобы выйти из ряда вызовов, освобождая цепочки аллоцированных блоков. Можно попросить free(ptr1, ptr2); — т.е. «освободи-ка разом всю память что была между аллокациями такими-то и такими-то».

Заметьте: в отличие от вас я, как раз, знаю несколько способов это сделать Вы, в отличие от меня,


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

Но раз вы обозначились, охотно верю, поведайте несколько способов это сделать. Буду ждать.

категорично заявляете, что это — единственный возможный подход, что немедленно вызывает реакцию Architecture astronautus detected, fire protocol enabled


Зачем вы врёте?
Приведите ссылку на мое сообщение, где бы я © «категорично заявлял» что это единственный подход.

Исходная мысль была очень проста — вообще-то, размножать повсеместно один и тот же код не следует.
https://en.wikipedia.org/wiki/Don't repeat yourself
И следует подумать как от этого избавиться наиболее элегантным способом.

Чего вы хотите от комментариев?
Я привел самое простейшее и самое прямолинейное, очевидное решение. Не лучшее, просто пример. ЭТО НЕ ЗНАЧИТ ЧТО ЭТО ПАНАЦЕЯ НА ВЕКИ ВЕКОВ, ДЛЯ ВСЕХ СЛУЧАЕВ И ВОЛШЕБНОЕ РЕШЕНИЕ — кто так думает и кидается с пеной это обсуждать… ну… у меня плохие новости…

Не надо никого хватать за горло и тут же требовать волшебной панацеи, применимой всегда и везде, и причем написать это тут же, в комментах. И объяснять что это невозможно тоже не надо, надо уметь общаться на определенном уровне.
Я предложил то, что посильно сделать в комментарии — самое первейшее решение a-must-have (задел на будущее) — это не обращаться НАПРЯМУЮ к malloc-подобным функциям, предусматривая в качестве задела нечто своё. Это первое что я написал в первом же сообщении! Первый и весьма разумный шаг.
Вообще-то подобный шаг должен быть последним — после того, как вы посмотрели на альтернативы и убедились, что вариант с проверками в вашем конкретном случае действительно неприемлем.

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

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

Второй вариант: setjmp/longjmp. В том месте, где ваша библиотека получает управление они вызывает setjmp, в обёрдке для malloc'а — соотвественно происходит выход из библиотеки.
Преимущества: относительно невысокие накладные расходы если ваша библиотека преимущественно «счётная» и имеет не слишком «широкий» API.
Недостатки: при бездумном использовании setjmp/longjmp исключения могут быть потеряны, потому в случае, когда вы всё-таки собираетесь на платформе с их поддержкой вам нужно будет их ловить, например, при вызове callback'ов и передавать дальше. Или, как альтернатива, потребовать, чтобы пользователи библиотеки исключениями не пользовался.

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

Приведите ссылку на мое сообщение, где бы я © «категорично заявлял» что это единственный подход.
Вот, например: Построить обертку и не использовать malloc() повсеместно (т.е. на application-level уровне) — это именно то, что следует сделать. В чем тут проблема и какие тут «овраги»?

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

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

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

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

То что я имел в виду (и только это, без вашего произвольного расширения) — я написал выше другим людям НЕОДНОКРАТНО. Повторять это и цитировать вам уже по третьему разу — считаю будет бредово, давайте читайте сам, сам, сам. Но не расширяя тезисы как вам это угодно!

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


Я уверен что и «обертку» вы как-то очень специфично по-своему понимаете. А что по-вашему «обертка», как не замена malloc'а на свою собственную реализацию? Я об этом в явном виде писал.

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


Общение через shared memory достаточно быстрое. Процесс будет срублен только в случае фатальных происшествий, поэтому вполне себе рабочее решение — кстати приснопамятный Chrome представляет собой именно такой набор процессов.

Однако же должен упомянуть, (сам я, а не то, как вы додумали) ни в коем случае я не предлагал исходно рубить ВООБЩЕ ЛЮБОЙ ПРОИЗВОЛЬНЫЙ процесс, с помощью exit(), это вы уже произвольно додумали наблюдая просто образец кода, в котором exit() только для иллюстрации.

Который, впрочем, на практике-то как раз, вопреки всем исходно «навешанным на меня собакам» и всем крикам — имеет право на существование и имеет место быть! В особенности с развитием парадигмы Microservices. Причем на *NIX это вполне себе устоявшаяся парадигма программирования — порождение процессов вместо потоков и рубка их в случае чего.

А вот остальное, все что вы написали КРОМЕ отсылки к замене malloc и упомянутого мной выше коллбэка — это всё очень генеральные вещи, не имеющие НЕПОСРЕДСТВЕННОГО отношения к теме автора:
А именно, к функциям аллокации памяти + проверке возвращаемого значения. Это у вас произвольный текст «на тему». Он вообще никак не противоречит и пересекается с поднятой автором и мной темой постольку-поскольку, соприкасаясь с ней некоторыми фрагментами. То есть опять произвольное «расширение». Смысл? Ну смысл есть на поболтать. Но мои тезисы это никак не опровергло.

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

Если обсуждать около-темы выделения памяти, вы не упомянули самые интересные и очень действенные методы. Что ж, «распальсую пальсы»:
Это:
а) Компрессия оперативной памяти.
б) Уничтожение содержащихся в памяти данных в случае нехватки памяти и воссоздание данных, там где можно (загрузка с диска).
в) Memory-mapped files.
г) Дефрагментация памяти.
д) Вызов garbage collector (шутка) :) если используется GC-архитектура.

Но это повторяю, к тому что мы обсуждаем постольку-поскольку. Не более чем «текст на тему».

P.S.
У меня тут отпуск и весь этот «обмен распальсовками» местных уникумов у меня просто отнимает время. Особо нового я ничего не узнал. Никто мне ничего уникального не поведал. Поэтому не обессудьте если я исчезну, смысл всех этих «распальсовок» исходно стремился к нулю.
Я уверен что и «обертку» вы как-то очень специфично по-своему понимаете. А что по-вашему «обертка», как не замена malloc'а на свою собственную реализацию? Я об этом в явном виде писал.
«Обёртка» — она, как бы, «оборачивает». То есть вызывает стандартную функцию malloc, но дополнительно что-то как-то обрабатывает.

Chromium же заменяет malloc целиком и полностью: его malloc является полной заменой: функции имеют названия не chromium_malloc и chromium_free, а просто malloc и free, все библиотеки, влинкованные в Chromium используют именно их, а стандартные функции — не используются вообще.

Есть разница между «обёрткой» и вот этим, а? Или таки нет? Или вам что в лоб, что по лбу?

Общение через shared memory достаточно быстрое. Процесс будет срублен только в случае фатальных происшествий, поэтому вполне себе рабочее решение — кстати приснопамятный Chrome представляет собой именно такой набор процессов.
Ага — только вот беда: shared memory там не используется. Потому как кроме скорости — есть ещё такая вещь, как безопасность и надёжность. И вот с ними в shared memory всё… непросто. Даже очень.

И да — Chromium использует подобную модель, но, опять-таки, из соображений безопасности и надёжности его «дети» этого делать не могут. То есть снова: то, что позволено приложению — не дадут делать библиотеке.

Который, впрочем, на практике-то как раз, вопреки всем исходно «навешанным на меня собакам» и всем крикам — имеет право на существование и имеет место быть! В особенности с развитием парадигмы Microservices. Причем на *NIX это вполне себе устоявшаяся парадигма программирования — порождение процессов вместо потоков и рубка их в случае чего.
Вот только далеко не всегда это хорошо и правильно. Как я уже писал выше: библиотеки, используемые Chromium'ом так делать не могут.

Я также могу сейчас как это выше устроили, устроить балаган, закуситься с криком «где это вы возьмете доп. буфера в микроконтроллере» — и из этого устроить распальцовку с криками «аааа, не понимаете! А покажите мне доп. буферов на микроконтроллере с 2кб памяти». Слава богу, у меня все в порядке с рассудком. Но вы-то сделали по сути именно так!
Нет. Я с самого начала сказал, что я не знаю одного универсального способа избавления от проверок в коде и, более того, считаю, что во многих случаях их использование (плюс warn_unused_result на malloc и компиляция с -Werror, разумеется) — меньшее зло.

Если обсуждать около-темы выделения памяти, вы не упомянули самые интересные и очень действенные методы.
Потому что все ваши «интересные и очень действенные методы» слабо применимы в контексте статье — то есть, ещё раз повторяю, при написании переносимых библиотек, а не приложений.
В смысле в хромиуме свой malloc? там же jemalloc обычный вроде был
Зависит от платформы. Вот тут есть README.

Но я о другом — перекрыть malloc и free может программа. Или какой-нибудь «большой» компонент (Blink, V8) — настолько большой и специфический, что, фактически, является частью программы. «Обычная» же библиотечка, предназначенная для использования самыми разными программами с подобными запросами вряд ли приживётся.
Понятно.
Приятно с вами поговорить. Ну мы просто выше не поняли друг друга. Универсальное решение я не предлагал. Наоборот, вбросил простые две копейки.

Мне вот что интересно. Вы не могли бы припомнить ситуацию с GetMem в паскале, подробнее расписать что там было.

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

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


Что за резервный буфер? Это самопал какой-то?
Мне вот что интересно. Вы не могли бы припомнить ситуацию с GetMem в паскале, подробнее расписать что там было.
С GetMem ничего интересного не было. Ну за исключением небольшой фишки: HeapError, который позволял её «зациклить» — такой callback, который в случае нехватки памяти возвращал управление обратно в программу и позволял ей попытаться что-нибудь с этим сделать.

В Turbo Vision же было «два слоя обороны»: в начале освобождались кеши (память выделенная с помощью функций NewCache), потом — уже SafetyPool на 4K.

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

Что за резервный буфер? Это самопал какой-то?
Нет. Safety Pool. В Turbo Vision (исходники тут) всё было сделано просто: они просто при старте программы захватывали некий кусок памяти, а HeapError этот кусок памяти освобождал (после чего аллокатор заново пытался выделить память, если возвращалось значение 2). Но это хорошо работает в однопоточной среде. В многопоточной вам будет нужен отдельный буфер для вашей библиотеки и соответствующий аллокатор поверх этого буфера.
А, ну все понятно, отбой. Turbo Vision, а не Pascal. А то я уже весьма заинтересовался таким «новостям».

Так там всё и есть. У OWL SafetyPool они в два раза больше взяли, 8К.

У Борландов сразу был предусмотрен callback с глобальной обработкой out of memory.

Ценю когда человек сразу url даёт. Сам такой. Поставил бы +1, но не могу.
Сейчас посмотрел на оригинальное сообщение — пропущены три буквы: имелась в виду Turbo Pascal 6.0 IDE, конечно. Которая унаследовала этот механизм от Turbo Vision.

Ясно, что не они это придумали (думаю если «копнуть», то выяснится, что, как обычно, кто-нибудь это для какого-нибудь Multics'а в 60е придумал), но работает достаточно эффективно: количество проверок снижается на порядок, а то и на два, что решает главную проблемы: практическую невозможность всё это тщательно покрыть тестами.

Эту проблему, кстати, исключения (хоть «настоящие», хоть «эмалированные» на setjmp/longjmp) не решают, кстати. До такой степени не решают, что в Java нехватку памяти никто даже не пытается обработать! Как и во многих других современных программах!

Однако, опять-таки, всё зависит от библиотеки: SafetyPoll ведь тоже место занимает и если каждая библиотеке возьмёт себе по 8К в каждом потоке, а тех библиотеке будет с полсотни… В общем — подход красивый и интересный, но не панацея ни разу…
«Обёртка» — она, как бы, «оборачивает». То есть вызывает стандартную функцию malloc, но дополнительно что-то как-то обрабатывает.

Chromium же заменяет malloc целиком и полностью: его malloc является полной заменой: функции имеют названия не chromium_malloc и chromium_free, а просто malloc и free, все библиотеки, влинкованные в Chromium используют именно их, а стандартные функции — не используются вообще.

Есть разница между «обёрткой» и вот этим, а? Или таки нет? Или вам что в лоб, что по лбу?


1. Chromium меня не интересует. Если я его и упомянул, то только чтобы сказать что он «набор процессов», какой в нем malloc мне в текущем контексте вообще без разницы.
Поэтому смысла мне писать об этом ноль. В моей основной теме Chromium никак не звучал.

2. Про механизм malloc hooks в GLBC я в курсе, как и POSIX dlsym.

3. Теперь насчет «обертки», как это я понимал, когда произносил вам определенные слова выше и связывал их с memory manager.

  • «Обертка» оборачивает вызов к malloc'у().
  • Вы называете ее как-нибудь my_malloc().
  • Ваш код зовет везде my_malloc()
  • Эта обертка к malloc'у под названием my_malloc вызывает внутри себя malloc() и сразу «откусывает» блок памяти.
  • В самом примитиве — она вообще ничего не делает, это именно только функция обертка malloc(), все вызовы форвардятся malloc'у(), почему она так и называется, «обертка».
  • В самом сложном случае — внутри пишутся функции эдакого наколенного менеджера памяти для сишечки, то есть отожрала блок у malloc'а — раздает под видом my_malloc.


Нет. Я с самого начала сказал, что я не знаю одного универсального способа избавления от проверок в коде и, более того, считаю, что во многих случаях их использование меньшее зло


Я тоже не знаю одного универсального способа избавления от проверок в коде и тоже считаю что ситуация что и где использовать это гибкий вопрос.

Вот например знакомый вам язык C++, кроме старых версий MSVC библиотек (хотя это фиксится), архитектурно решает вопрос контролем не проверками по месту в коде, а глобальной проверкой выполненной в operator new и выкидывающей std::bad_alloc.

То есть, он реализует глобальную парадигму проверки аллокации памяти (а не по месту).
Также, как звучавшее предложение.
Вот например знакомый вам язык C++, кроме старых версий MSVC библиотек (хотя это фиксится), архитектурно решает вопрос контролем не проверками по месту в коде, а глобальной проверкой выполненной в operator new и выкидывающей std::bad_alloc.
Что и приводит к тому, что внушительную часть библиотек, разработанных в таком стиле во многих огранизациях использовать просто запрещено.

Для того, чтобы не использовать исключения в C++ есть std::nothrow и, сюрприз-сюрприз, он просто возращает указатель, который может оказаться равным nullptr.
Всё так и есть, почему я и говорю,
я тоже не знаю одного универсального способа избавления от проверок в коде и тоже считаю что ситуация что и где использовать это гибкий вопрос.

p.s. коммент не просмотрите про буфер в паскале:
ссылка
Логика проверки может зависеть от ситуации.
Не очень понимаю споров в комментариях выше. Как можно защищать UB, конкретные примеры которого привели в статье? Это же абсолютное зло, Undefined может завтра привести к чему угодно. С каких пор в проге появился такой критерий «сейчас в подавляющем большинстве случаев сработает»? Код программы должен стремиться к 100% гарантии правильного поведения. Да, случайные ошибки в коде на то и случайные ошибки, что появляться будут все равно. Но сознательно ставить в код UB, потому что одна «лишняя» проверка режет глаз — это что-то крайне мне непонятное.
Как можно защищать UB, конкретные примеры которого привели в статье?


Психологическая защита же.
UFO just landed and posted this here
UFO just landed and posted this here
Вы совершенно правы в способе оценки, но на мой взгляд сильно ошибаетесь в самой оценке этой разницы для ПО, которое разрабатывается не в качестве одноразовой «поделки».
UFO just landed and posted this here
Отсутствие проверки после malloc — это не отсутствие перфекционизма. Это отсутствие профессионализма. И печально, что многие это считают нормой. Мельчает C++ программист :).

P.S. А в прочем, это даже к лучшему. Будет больше возможностей продавать PVS-Studio, когда вдруг припрёт, и поделие таких «программистов» вдруг станет большим, востребованным, и окажется, что весь этот код надо как-то поддерживать и развивать. Благословляю на говнокодинг :).
UFO just landed and posted this here
а вот то что например этот проект нельзя использовать на современном HiDPI мониторе/ноутбуке
А мужики-то и не знают. Я уже четвёртый год использую Chrome на X1 Carbon в разрешении 2560x1440 (15", если что). Это — недостаточно Hi?
UFO just landed and posted this here
Включите честные Hi на уровне ВСЕХ приложений (ЕМНИП это скачиванием программки делается) и посмотрите как станет выглядеть Хром.
Также, как и всегда, я думаю. У меня --force-device-scale-factor=1.5 стоит.

У хрома, увы нет даже возможности пользоваться им на HiDPI мониторе (Ваш кейз — когда из Hi сделать недо-Hi на уровне ВСЕЙ операционки, то есть де-факто свести разрешение к половинному — я не рассматриваю как Hi).
Тут Hi, тут не-Hi. Хром не виноват в том, что в GNU/Linux имеется 100500 способов задать разрешение и зачастую они выдают противоречивую информацию.

На системах, где этой проблемы нет (Windows там… Android...) всё работает «из коробки», для Linux — есть костылик, а для тех, кто почему-то использует Linux, да ещё и HiDPI, но не хочет возиться с настройками… ну извините — для этих «трёх с половиной землекопов» тратить кучу времени не хочется…
UFO just landed and posted this here
Ибо является чистым перфекционизмом.
Когда из-за дыр в браузере взломают ваш интернет-банкинг, то, может быть, вы перестанете считать это перфекционизмом.
UFO just landed and posted this here
когда взломают, тогда поговорим
Блин, ну это уже просто клиника какая-то.
UFO just landed and posted this here
UFO just landed and posted this here
Как много болтологии, чтобы оправдать нежелание и неумение обрабатывать ошибки. Мы сейчас занимаемся полноценной поддержкой компилятора Keil. В целях проверки (ложных срабатываний и т.п.) я сейчас разбираю отчёт об анализе проекта RT-Thread. И вот что интересно, почему-то там разработчики умеют обращаться с malloc. Проверяют и обрабатывают нехватку памяти (первый попавшийся пример):

    /* allocate memory */
    mp->start_address = rt_malloc((block_size + sizeof(rt_uint8_t *)) *
                                  block_count);
    if (mp->start_address == RT_NULL)
    {
        /* no memory, delete memory pool object */
        rt_object_delete(&(mp->parent));

        return RT_NULL;
    }
Им это не кажется чем-то нереально сложным или ненужным. Вот они молодцы. Просто взяли и сделали обработку ошибок, а не занялись отмазками.

P.S. Есть ли там ошибки? Конечно есть. Например, вот эта понравилась.
void LCD_PutPixel (LCD_PANEL panel, uint32_t X_Left, uint32_t Y_Up, LcdPixel_t color)
{
    uint32_t k;
    uint32_t * pWordData = NULL;
    uint8_t*   pByteData = NULL;
    uint32_t  bitOffset;
    uint8_t*   pByteSrc = (uint8_t*)&color;
    uint8_t  bpp = bits_per_pixel[lcd_config.lcd_bpp];
    uint8_t  bytes_per_pixel = bpp/8;
    uint32_t start_bit;
  
    if((X_Left >= lcd_hsize)||(Y_Up >= lcd_vsize))
        return;

    if(panel == LCD_PANEL_UPPER)
    pWordData = (uint32_t*) LPC_LCD->UPBASE + LCD_GetWordOffset(X_Left,Y_Up);
    else
    pWordData = (uint32_t*) LPC_LCD->LPBASE + LCD_GetWordOffset(X_Left,Y_Up);
    
    bitOffset = LCD_GetBitOffset(X_Left,Y_Up);
    pByteData = (uint8_t*) pWordData;
    pByteData += bitOffset/8;
    
    start_bit =  bitOffset%8;

    if(bpp < 8)
    {
      uint8_t bit_pos = start_bit;
      uint8_t bit_ofs = 0;
      for(bit_ofs = 0;bit_ofs <bpp; bit_ofs++,bit_pos++)
      {
          *pByteData &= ~ (0x01 << bit_pos);
          *pByteData |= ((*pByteSrc >> (k+bit_ofs)) & 0x01) << bit_pos; // переменная k неинициализированная
      }
    }
    else
    {
         for(k = 0; k < bytes_per_pixel; k++)
        {
           *(pByteData+ k) = *pByteSrc++;
        }
    }
}
PVS-Studio: V614 CWE-457 Uninitialized variable 'k' used. lpc_lcd.c 510
1. Я вообще за то чтобы по-возможности не одобрять в pure C++ прямые выделения/деаллокации памяти в сишном стиле и работу с памятью.

2. Можно двигаться в сторону GC — архитектур + «активных» указателей/ссылок, т.е. смартпоинтеров. У народа есть опыт написания всей 3D engine полностью на смартпоинтерах, падение производительности незначительное.

3. Можно двигаться через создание domain-specific language's. Втч возможно динамически комплируемых, (с автоматическим управлением памятью) возможно, но не обязательно с байткодом, и возможно, но не обязательно с отложенной/статической компиляцией. Сейчас пока с этим заминка, основные VM пока громоздкие и малоупотребимые, а Framework'и для создания собственных DSL слишком сложны. Но это пока. В ближайшем будущем очень вероятен взрыв Intra-CPP решений, когда внутри/параллельно C++ решения находится несколько самопальных язычков под свои задачи.

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

Написать в комментариях к чужой статье «Я вообще за то чтобы по-возможности не одобрять в pure C++ прямые выделения/деаллокации памяти в сишном стиле и работу с памятью.» Не нужно ни большого ума, ни большого труда. Тем более, что в C++ работа с памятью в С-шном стиле не одобряется уже лет тридцать как.

А вот сделать статью и затем получить сотню комментариев с оценками степени вашей оригинальности и оторванности от жизни… Вот это совсем другое. Ну не обсуждать же "Парадигма вида «попросить память» — что-то сделать — «отдать память» — древняя, архаичная как г. мамонта." в комментариях к статье, в которой говорится, как правильно работать именно в этой парадигме.
Так © «в C++ работа с памятью в С-шном стиле не одобряется уже лет тридцать как» или «вдоволь по вашим заблуждениям оттоптаться». Должно быть что-то одно.

Это я ответил Andrey2008, но что-то пошло не так. Смысл риторического комментария был в том, что от malloc-free или new-delete по-хорошему надо бы вообще по возможности отдаляться. Ясно что он это понимает, и вы это понимаете, и мой последний оппонент понимает, да выше пример был — в Chromium функции не используются, но как риторическое замечание — почему бы и нет? Почему это надо воспринимать сразу в штыки? :)

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

Я подумаю написать статью, спасибо. Но мне кажется надо начинать с чего-то попроще.
С комментариев начал, а оно что-то в штыки вышло :) ...?
Должно быть что-то одно.
С чего бы? Вы тут выступаете с очень странными заявлениями, обосновать которые в предметном разговоре просто не можете. Так что оттоптаться по вашим убеждениям можно вне зависимости от того, что работать в C++ с памятью в C-шном стиле не принято уже очень и очень давно.

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

Во-первых, код на чистом C продолжают писать. Не говоря уже про сопровождение кода на чистом C. Поэтому для C-шников malloc — это такой же привычный инструмент, как для C++ников new или std::vector.

Во-вторых, зачастую C-шный и C++ный код смешивают в одном проекте. И если в C++ной части нужно выделить память, которая уйдет затем в C-шную часть, то не через new же ее выделять. Поэтому и в C++ном коде можно встретить malloc вместо new и это будет иметь смысл.

В-третьих, C++ сложный язык. Его и раньше знали далеко не многие. А уж теперь, когда он потерял изрядную долю своей популярности, дела с изучением C++ обстоят не очень хорошо (насколько я могу об этом судить). Поэтому на C++ многие программируют вообще не следуя рекомендациям, в том числе и очень старым. Поэтому некоторые «типа C++ники» запросто используют в своем C++ном коде malloc, не очень понимая, что для этих же целей может быть использован new.

Поэтому статья господина Andrey2008 вполне себе осмыслена и актуальна. В отличии от ваших комментариев в ней. И когда вы сюда приходите с идеями полностью отказаться от malloc-а (и других *nix-овых вызовов) или отказом от парадигмы «выделил память — освободил память», то обсуждение ваших странных, мягко говоря, идей и предложений здесь оказывается злостным офтопиком. А обсудить их могло бы иметь смысл, т.к. Хабр читает молодежь и мало ли, кто-то воспримет ваши потоки создание за результат разумных размышлений.
Ответ на комментарий eao197.

С чего бы?

Потому что вы сами себе логически противоречите. Я написал о том, что архитектурно в CPP проектах следует избегать менеджмента памяти в C стиле, да и вообще по возможности избегать менеджемента памяти, как такового. Мой тезис вы подтверждаете своими словами:
Тем более, что в C++ работа с памятью в С-шном стиле не одобряется уже лет тридцать как.
.

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

Однако, поскольку представления о реальности у вас какие-то очень своеобразные


Когда 1. мои слова соответствуют реальности, и когда 2. человек сам подтверждает мои слова, но при этом считает что я чему-то © «противоречу», это называется шизофрения.
Попытка видеть то, чего актуально нет. Попытка совместить несовместимые сущности.

Вот и сейчас. У вас в голове что-то переклинило, ввиду чего вы начинаете зачем-то (зачем?) с апломбом рассказывать мне почему в C++ проектах все еще используется аллокация памяти.

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

Все свои слова касаемо замечания Евгения я обосновал и логикой и делом — показав код.
Вы же в ответ кроме хамства и требований «Покажите мне» ничего не сумели сделать.

Всё. Лечитесь. Принимайте таблетки.
Конец связи.

Обсуждение выше уже показало, что вы a) не способны внимательно читать то, что пишут вам и b) не в состоянии понять то, что вам говорят. Уж не знаю, что для чего является первопричиной.

Вот и сейчас вы видите противоречия, которых нет. Показываю на пальцах:

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

Т.е. вы написали, мягко говоря, бред. Который можно было бы весело обсудить, изложи вы его подробнее в отдельной статье.

Вы же цепляетесь всего за один тезис и пытаетесь проехаться по личности комментатора. Хотя сами до этого демонстрировали ну очень тонкую душевную организацию и повышенную чувствительность к хамству.

По поводу же:
Лечитесь. Принимайте таблетки.
Вы уж определитесь, хотите вы участвовать в технической дискуссии или нет. Если хотите, то будьте готовы к тому, что ваши аргументы/убеждения/заблуждения будут подвергаться сомнению. Если не хотите, то зачем вы комментарии к техническим статьям пишете?
Я обычный, приземленный человек. Который посоветовал (в отличие от вас) дельную хорошую мысль, которой я очень советую воспользоваться тем, кто реально разрабатывает новые проекты — эта мысль достаточно проста, тривиальна но в то же время хороша. В самом генеральном смысле это перехватить самым простым образом управление памятью на себя, в самом первом базовом случае обернув вызов malloc, заменив своей функцией, своим *alloc'ом. Эта мысль, если ее выслушивает адекватный, разумный человек — безусловно заслуживает одобрения.

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

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

Что уникально, если вычитать первый комментарий к статье — оказывается там Александр Патраков AEP написал ровно то же самое.

Но! Вместо всего этого меня обосрали как только могли. Поэтому я говорю — это тяжелый синдром непризнанного гения. Гадить на людей, совершенно отвращая их от того чтобы общаться. Пребывая в своем высокомерии «гении» полагают что люди чего-то там «не знают», что они убоги, неразумны, не логичны, противоречат и «несут бред». Но это не так, это у «гения» глаза застило.
Гуглите и почитайте: https://www.google.com/search?q=комплекс+непризнанного+гения там прямо о том как здесь общаются.

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


Верно, да не всё.

Если я с чем-то не согласен, я максимально обезличенно пишу о сути дела, не затрагивая человека. Расстроившись, я намеренно стал писать слова «Вы не правы». Когда все нормально, я даже не стремлюсь сказать что оппонент не прав, я говорю что это неправильная мысль, мнение, или взгляд. Или даже максимально обезличенное — «это не так», а это вот так.
Это — уважение.

Вы посмотрите пожалуйста что тут мне писали, ладно? И в какие-то астронавты меня записали, и оскорбляли то так, то сяк.
Вы посмотрите какое высокомерие. Стремление подчеркнуть якобы мое недопонимание, «странность», «заблуждения», «оторванность от жизни». Это стремление принизить, унизить человека.

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

По всему остальному вам уже все сказали, как минимум несколько раз: один, два, три и четыре.

PS. Ваш страх и ужас с MALLOC_TRY и MALLOC_EXCEPT лучше никому не показывать.
PPS. По поводу архитекторов-астронавтов вам даже ссылку дали с объяснением, что это такое.
По всему остальному вам уже все сказали, как минимум несколько раз: один, два, три и четыре.


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

Я вас спрашиваю да, или нет? Вот только не надо ожидать что это должно работать всегда и везде и являться универсальной паацеей. Человек сам предположивший такое (я этого не предполагал) и кинувшийся опровергать — реальный шизик.

c xmalloc давайте спорьте, со мной не надо.

PS. Ваш страх и ужас с MALLOC_TRY и MALLOC_EXCEPT лучше никому не показывать.


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

По поводу архитекторов-астронавтов вам даже ссылку дали с объяснением, что это такое.


Я прочел. Человек серьезно воспринимающий это поток сознания — вероятно полный дурак. Просто недоразвитый. Быдлокодер с манией величия, считающий этот бред откровением. Такое бывает у дураков. И в этом, видимо вся причина происходящего, я внезапно всё понял о вс.
Вы еще дом2 почитайте, там полно таких же потоков сознания.
Вы логикой-то владеете?
Пока ни коллеги, ни заказчики не жаловались.
Соображаете что такое доказательство или опровержение?
Что-то вы совсем того, давайте я вам еще раз объясню, как обстоит дело. Может хоть в этот раз вы поймете что к чему.

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

Все. Вы не выяснили, что именно болит, ни симптомов, ни анализов, ни общего состояния пациента, ни истории его болезней. Ничего. Вы даже не рассказали, что за примочки и как их применять (количество, длительность, состав и пр.).

Вы просто сказали «Я рекомендую делать примочки» и все.

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

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

И тут нечего опровергать. Есть просто конкретный рецепт и список конкретных противопоказаний к нему.
Так что умойтесь.
Вы это, когда разговариваете с оппонентом в подобном тоне, перестаньте жаловаться на оскорбления в свой адрес. Тут уже «либо крестик сними, либо трусы надень».

А по поводу MALLOC_TRY/MALLOC_EXCEPT — вы серьезно хотите обсудить качество этого говноподелия?
Человек серьезно воспринимающий это поток сознания — вероятно полный дурак
Вы понимаете, почему термин «архитектор-астронавт» пошел в массы? Потому что это узнаваемый образ. Спольски только сумел мастерски его описать, но он его не выдумывал, а подсмотрел в жизни. Так что вы можете не верить в существование архитекторов-астронавтов, но ваше неверие ни на что, к счастью, не влияет. Вы, как раз, подтверждаете их существование.
Да ладно.
Утомился я.

Обсуждать несколько строк рожденных на коленке за 30 минут глупо, они того не стоят. Но я понимаю что для вас как C++ кодера это желаемо. Вы можете факультативом, разве я могу вам запретить.

Но вы, Евгений Охотников eao197, лично тоже вызвали во мне огромный интерес!
Если вы такой крутой кодер (а это представляется мне несомненным),
нельзя ли глянуть на ваш код?

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

Хочется прикоснуться к прекрасному.
Я понимаю что можно «гугл в зубы»
Если бы понимали, то уже бы сделали.
но вы сами выделите мне свою гордость?
Мне уже слишком много лет, чтобы гордиться своим кодом.
Хочется прикоснуться к прекрасному.
Не там вы собрались прекрасное искать.
Буду проще.
Дайте ссылку хоть на какой-нибудь код, который принадлежит вашей руке.
Можно в ЛС.

Вот например khim дал в личной ссылке, насладиться я пока не успел, но все же почему он так много говорит о некоторых вещах понятно :)
Вот например khim дал в личной ссылке, насладиться я пока не успел, но все же почему он так много говорит о некоторых вещах понятно :)
Наслаждаться продакшн-кодом — это что-то новенькое. Реальный код в реальных приложениях редко когда бывает красив. Это — всегда компромисс между тысячами разных требований. Не там вы собрались прекрасное искать — это точно.
Не стоит выражения сказанные явно в шутку принимать всерьез. Также как «прикоснуться к прекрасному» — ходовое устойчивое выражение.
UFO just landed and posted this here
Он просто стебется.
Malloc_try/Malloc_except — libunwind спасет юного падавана в принципе через нее можно реализовать подобие __try/__except из msvc. А обработка malloc а SEH исключениями конечно из пушки по воробьям но работать будет(главное за ресурсами следить)
Malloc_try/Malloc_except — libunwind спасет юного падавана в принципе через нее можно реализовать подобие __try/__except из msvc.
Далеко не на всех платформах есть libunwind. Далеко не на всех платформах, где он есть — он работает. Далеко не всегда можно его прикрутить к библиотеке «малой кровью».

А так — да, рабочий вариант. Для некоторых случаев.
Жаль не могу поставить вам +1, у меня нет такой возможности.
Я libunwind вчера мельком смотрел, когда пытался родить кодец менее убогий чем я запостил.

Но безусловно, вы правы, unwinding фреймов тут необходим.

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

Здесь у меня топик был — это «гольный код», конструкция из двух строк — аллокация (и в оригинале проверка), а также тупенькая-тупенькая функция-обертка. По всей строгости ожидается обсуждение вот этого примитивного кода «на мысленном экране», и ни в коем случае не отход в сторону.

Ситуация равноценная следующему.
Представьте мысленно что на доске написана формула. В дискуссии спрашивают — ну а вы видите что в числителе квадрат скорости? И тут человека вместо ответа на вопрос и вместо обсуждения формулки начинает нести, и он заявляет оппоненту: «А вы покажите-ка мне свои работы! А ну-ка, напишите-ка мне формулу за пять минут. А, да вы господин теохретик у нас!»
— Вы знаете что за такое будет? На него люди посмотрят как на демагога-софиста и тут же отберут микрофон. Я к такому не привык.

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

У меня всё.
Надеюсь я сумел дать вам понять что происходило (предложение, «формулка» на доске), что я ожидал от присутствующих, и какую грязь тут устроили. Ну да это всё уже не важно.
Я занимаюсь наукой и участвую в научных дискуссиях.
Т.е. с тем, что вы не имеете отношения к разработке на C и C++, я оказался прав?
Нет, крестами я занимался с прошлого века. Электроника, машкод, ассемблер, RTOS, микроконтроллеры — это всё моё. Как и CAD-like приложения. Но давно вырос, занимаюсь другим и сейчас просто испытываю интерес. Сейчас уже только хобби.
Профессионально я стою на несколько ступенек выше C++ кодеров.

Профессионально я стою на несколько ступенек выше C++ кодеров.
С вашей скромностью и адекватностью все уже понятно, можно лишний раз не демонстрировать.
UFO just landed and posted this here
Профессионально я стою на несколько ступенек выше C++ кодеров.
Ух ты, сколько пафоса. Но нет.

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

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

И я отлично вижу что ценится в науке.

Умение расписывать элементарные вещи в 100500 страницах — да, очень важно. Умение замаскировать то, чего вы не знаете — тоже ценно.

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

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

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

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

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

Столь же глупо и нелепо будет выглядеть моя попытка обьяснить вам, как писать научные статьи, извините: рассказать как сделать так, чтобы «кодеры» могли статью воспринять — я, наверное, смогу. А вот сделать так, чтобы она прошла через рецензентов — вряд ли.
Ну теперь, наконец, стало понятно — почему вы ведёте так, как себя ведёте.


Наверное да.
Но и мне понятно происходящее — кто же еще может крутиться под статьями о инструменте проверяющим C++ код?
Кодеры конечно же! А им это всё… У них другие сферы понимания. И профессиональная деформация.

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


Насчет науки. У меня несколько своеобразное отношение к современному положению дел, когда гонится индекс цитирования, индекс Хирша. На мой взгляд многое сейчас пустая макулатура.

Вы немного не так восприняли мою роль в науке. Моя деятельность это параллельная «вилка» в жизни — из прикладной науки и ПО, если вам интересно. Наука отдельно. ПО отдельно, как это часто и бывает.

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

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

Но мне показалось вы упомянули Borland Pascal 6.0 — а это же довольно старый инструмент. Так что по подсчетам лет вам должно быть немало, не менее 50-55, а то и бери больше. Можно спросить, сколько вам? Вы до сих пор, в свои годы работаете программистом?

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


Да ну что вы, я не отнимаю этого у вас. Повторяю, я не мастер и не пытаюсь как-то претендовать на вашу мастеровую деятельность и отнять вашу прерогативу. Так сказать, охотно признаю в этом ваше преимущество. Заранее. Априори.

Вы родили во мне интерес.
Теперь мне стало очень интересно и хочется глянуть на ваши труды. Можно получить «ссылки гордости» на разработанные вами архитектуры, системы, фреймворки?
Просто захотелось прикоснуться к прекрасному.
Но мне показалось вы упомянули Borland Pascal 6.0 — а это же довольно старый инструмент. Так что по подсчетам лет вам должно быть немало, не менее 50-55, а то и бери больше.
Это почему это, вдруг? Один мой хороший друг, только начинающий заниматься программированием не так давно задавал вопросы по поводу вот этого вот сайта.

Классика не умирает, и как Turbo Pascal 7.0, так и Turbo C/C++ 3.1 до сих пор в ходу в ВУЗах. А упомянул я не Turbo Pascal 7.0, а Turbo Pascal 6.0 — так это потому, что Turbo Vision именно в версии 6.0 появился, а не в 7.0

Его, если вам интересно, можно тут взять — и убедиться, что Turbo Vision и всё с ним связанное в нём уже есть…

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

Классика не умирает, и как Turbo Pascal 7.0, так и Turbo C/C++ 3.1 до сих пор в ходу в ВУЗах. А упомянул я не Turbo Pascal 7.0, а Turbo Pascal 6.0 — так это потому, что Turbo Vision именно в версии 6.0 появился, а не в 7.0

Его, если вам интересно, можно тут взять — и убедиться, что Turbo Vision и всё с ним связанное в нём уже есть…


Смеялся!
Извините, не распарсил все эти пространные рассуждения с увлечением какой есть паскаль и где его (вдруг!) можно взять.
Мдас. Как-то смешно у вас вышло.

Так я не понял, вы младше? Или старше?
Если младше, то видимо просто работа была не так давно? В каком году? BP/TP в 95ом заменила Delphi, я кстати на ней и писал.

У меня под боком в 2000 г. в банковской системе сидел товарищ на Clipper'е, ну так вы понимаете как мы его дразнили. Плывет по реке Клипер, на Клипере Шкипер, у Шкипера… :-)

Если младше, то видимо просто работа была не так давно? В каком году?
Не работа, а учёба. Последний раз я общался с Turbo Pascal'ем чуть меньше 10 лет назад, думаю сейчас там уже всё таки C++ учат — но не удивлюсь, если тот самый Turbo C++ 3.1 под Dosbox'ом.

Думаете зря его учат ставить на 64 битный Windows 10? Сайт для любителей старины? Нет — это для студентов…

BP/TP в 95ом заменила Delphi, я кстати на ней и писал.
В том-то и дело, что не заменила. Ибо собирать программы под DOS не умела и курсы пришлось бы переписывать.

С Turbo Pascal я познакомиился когда он уже был весьма и весьма «устаревшим» и в «индустрии» уже не использовался, но обучали на нём ещё 10 лет назад точно, а Turbo C/C++ 3.1 — и до сих пор обучают. «С полной боевой выкладкой» — Crt, Graph, далее везде. Под Dosbox'ом…
Я занимаюсь наукой и участвую в научных дискуссиях. Там — за малейшей уход в сторону от вопроса сразу же бьется по рукам, потому что нельзя отнимать живое время людей, которые участвуют в дискуссии в живую, онлайн, или читая вычитку. Просто мгновенно отбирается микрофон при уводе в сторону.

Жаль, что на хабре так нельзя. В таком случае у вас бы отобрали микрофон давным-давно. Впрочем, еще не все потеряно...

Жаль, что на хабре так нельзя. В таком случае у вас бы отобрали микрофон давным-давно. Впрочем, еще не все потеряно...


В связи с чем такая недоброжелательность? Не припомню чтобы я вас чем-то обидел.
С точки зрения программиста, в начале цикла произойдёт запись в элемент ptr[0], и возникнет структурное исключение/сигнал. Оно будет обработано, и всё будет хорошо.

Я похоже совсем си забыл или чего-то не понимаю. Вроде же массивы вполне себе индексируются с нуля?


UPD: дошло, тупил таки. Если кто ещё не въехал — речь о случае, когда указатель nullptr.

Sign up to leave a comment.